Skip to content

Commit 61ebeb4

Browse files
committed
feat: object warehouse support rbac
1 parent 00f4bd2 commit 61ebeb4

21 files changed

+460
-85
lines changed

src/meta/app/src/principal/ownership_object.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ pub enum OwnershipObject {
5252
UDF {
5353
name: String,
5454
},
55+
56+
Warehouse {
57+
uid: String,
58+
},
5559
}
5660

5761
impl OwnershipObject {
@@ -80,6 +84,7 @@ impl fmt::Display for OwnershipObject {
8084
}
8185
OwnershipObject::UDF { name } => write!(f, "UDF {name}"),
8286
OwnershipObject::Stage { name } => write!(f, "STAGE {name}"),
87+
OwnershipObject::Warehouse { uid } => write!(f, "Warehouse {uid}"),
8388
}
8489
}
8590
}
@@ -119,6 +124,7 @@ impl KeyCodec for OwnershipObject {
119124
}
120125
OwnershipObject::Stage { name } => b.push_raw("stage-by-name").push_str(name),
121126
OwnershipObject::UDF { name } => b.push_raw("udf-by-name").push_str(name),
127+
OwnershipObject::Warehouse { uid } => b.push_raw("warehouse-by-uid").push_str(uid),
122128
}
123129
}
124130

@@ -165,9 +171,13 @@ impl KeyCodec for OwnershipObject {
165171
let name = p.next_str()?;
166172
Ok(OwnershipObject::UDF { name })
167173
}
174+
"warehouse-by-uid" => {
175+
let uid = p.next_str()?;
176+
Ok(OwnershipObject::Warehouse { uid })
177+
}
168178
_ => Err(kvapi::KeyError::InvalidSegment {
169179
i: p.index(),
170-
expect: "database-by-id|database-by-catalog-id|table-by-id|table-by-catalog-id|stage-by-name|udf-by-name"
180+
expect: "database-by-id|database-by-catalog-id|table-by-id|table-by-catalog-id|stage-by-name|udf-by-name|warehouse-by-uid"
171181
.to_string(),
172182
got: q.to_string(),
173183
}),

src/meta/app/src/principal/tenant_ownership_object_ident.rs

+16
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,22 @@ mod tests {
254254
let parsed = TenantOwnershipObjectIdent::from_str_key(&key).unwrap();
255255
assert_eq!(role_grantee, parsed);
256256
}
257+
258+
// warehouse
259+
{
260+
let role_grantee = TenantOwnershipObjectIdent::new_unchecked(
261+
Tenant::new_literal("test"),
262+
OwnershipObject::Warehouse {
263+
uid: "n87s".to_string(),
264+
},
265+
);
266+
267+
let key = role_grantee.to_string_key();
268+
assert_eq!("__fd_object_owners/test/warehouse-by-uid/n87s", key);
269+
270+
let parsed = TenantOwnershipObjectIdent::from_str_key(&key).unwrap();
271+
assert_eq!(role_grantee, parsed);
272+
}
257273
}
258274

259275
#[test]

src/meta/app/src/principal/user_grant.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ impl GrantObject {
111111
GrantObject::Stage(_) => {
112112
UserPrivilegeSet::available_privileges_on_stage(available_ownership)
113113
}
114-
GrantObject::Warehouse(_) => UserPrivilegeSet::available_privileges_on_warehouse(),
114+
GrantObject::Warehouse(_) => {
115+
UserPrivilegeSet::available_privileges_on_warehouse(available_ownership)
116+
}
115117
}
116118
}
117119

src/meta/app/src/principal/user_privilege.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ pub enum UserPrivilegeType {
7676
Write = 1 << 19,
7777
// Privilege to Create database
7878
CreateDatabase = 1 << 20,
79+
// Privilege to Create warehouse
80+
CreateWarehouse = 1 << 21,
7981
// Discard Privilege Type
8082
Set = 1 << 4,
8183
}
@@ -102,6 +104,7 @@ const ALL_PRIVILEGES: BitFlags<UserPrivilegeType> = make_bitflags!(
102104
| Read
103105
| Write
104106
| CreateDatabase
107+
| CreateWarehouse
105108
}
106109
);
107110

@@ -129,6 +132,7 @@ impl Display for UserPrivilegeType {
129132
UserPrivilegeType::Read => "Read",
130133
UserPrivilegeType::Write => "Write",
131134
UserPrivilegeType::CreateDatabase => "CREATE DATABASE",
135+
UserPrivilegeType::CreateWarehouse => "CREATE WAREHOUSE",
132136
})
133137
}
134138
}
@@ -166,6 +170,9 @@ impl From<databend_common_ast::ast::UserPrivilegeType> for UserPrivilegeType {
166170
databend_common_ast::ast::UserPrivilegeType::CreateDatabase => {
167171
UserPrivilegeType::CreateDatabase
168172
}
173+
databend_common_ast::ast::UserPrivilegeType::CreateWarehouse => {
174+
UserPrivilegeType::CreateWarehouse
175+
}
169176
databend_common_ast::ast::UserPrivilegeType::Set => UserPrivilegeType::Set,
170177
}
171178
}
@@ -199,8 +206,8 @@ impl UserPrivilegeSet {
199206
let database_privs = Self::available_privileges_on_database(false);
200207
let stage_privs_without_ownership = Self::available_privileges_on_stage(false);
201208
let udf_privs_without_ownership = Self::available_privileges_on_udf(false);
202-
let wh_privs_without_ownership = Self::available_privileges_on_warehouse();
203-
let privs = make_bitflags!(UserPrivilegeType::{ Usage | Super | CreateUser | DropUser | CreateRole | DropRole | CreateDatabase | Grant | CreateDataMask });
209+
let wh_privs_without_ownership = Self::available_privileges_on_warehouse(false);
210+
let privs = make_bitflags!(UserPrivilegeType::{ Usage | Super | CreateUser | DropUser | CreateRole | DropRole | CreateDatabase | Grant | CreateDataMask | CreateWarehouse });
204211
(database_privs.privileges
205212
| privs
206213
| stage_privs_without_ownership.privileges
@@ -234,8 +241,12 @@ impl UserPrivilegeSet {
234241
}
235242
}
236243

237-
pub fn available_privileges_on_warehouse() -> Self {
238-
make_bitflags!(UserPrivilegeType::{ Usage }).into()
244+
pub fn available_privileges_on_warehouse(available_ownership: bool) -> Self {
245+
if available_ownership {
246+
make_bitflags!(UserPrivilegeType::{ Usage | Ownership }).into()
247+
} else {
248+
make_bitflags!(UserPrivilegeType::{ Usage }).into()
249+
}
239250
}
240251

241252
pub fn available_privileges_on_udf(available_ownership: bool) -> Self {

src/meta/proto-conv/src/ownership_from_to_protobuf_impl.rs

+8
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ impl FromToProto for mt::principal::OwnershipObject {
8888
pb::ownership_object::Object::Stage(pb::ownership_object::OwnershipStageObject {
8989
stage,
9090
}) => Ok(mt::principal::OwnershipObject::Stage { name: stage }),
91+
pb::ownership_object::Object::Warehouse(
92+
pb::ownership_object::OwnershipWarehouseObject { uid },
93+
) => Ok(mt::principal::OwnershipObject::Warehouse { uid }),
9194
}
9295
}
9396

@@ -123,6 +126,11 @@ impl FromToProto for mt::principal::OwnershipObject {
123126
stage: name.clone(),
124127
}),
125128
),
129+
mt::principal::OwnershipObject::Warehouse { uid } => {
130+
Some(pb::ownership_object::Object::Warehouse(
131+
pb::ownership_object::OwnershipWarehouseObject { uid: uid.clone() },
132+
))
133+
}
126134
};
127135
Ok(pb::OwnershipObject {
128136
ver: VER,

src/meta/proto-conv/src/util.rs

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ const META_CHANGE_LOG: &[(u64, &str)] = &[
146146
(114, "2024-12-12: Add: New DataType Interval."),
147147
(115, "2024-12-16: Add: udf.proto: add UDAFScript and UDAFServer"),
148148
(116, "2025-01-09: Add: MarkedDeletedIndexMeta"),
149+
(117, "2025-01-13: Add: Add new UserPrivilege CreateWarehouse and new OwnershipObject::Warehouse"),
149150
// Dear developer:
150151
// If you're gonna add a new metadata version, you'll have to add a test for it.
151152
// You could just copy an existing test file(e.g., `../tests/it/v024_table_meta.rs`)

src/meta/proto-conv/tests/it/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,4 @@ mod v113_warehouse_grantobject;
114114
mod v114_interval_datatype;
115115
mod v115_add_udaf_script;
116116
mod v116_marked_deleted_index_meta;
117+
mod v117_warehouse_ownershipobject;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright 2023 Datafuse Labs.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use std::collections::HashSet;
16+
17+
use chrono::DateTime;
18+
use chrono::Utc;
19+
use databend_common_meta_app as mt;
20+
use databend_common_meta_app::principal::OwnershipObject;
21+
use databend_common_meta_app::principal::UserGrantSet;
22+
use databend_common_meta_app::principal::UserPrivilegeType;
23+
use enumflags2::make_bitflags;
24+
use fastrace::func_name;
25+
26+
use crate::common;
27+
28+
// These bytes are built when a new version in introduced,
29+
// and are kept for backward compatibility test.
30+
//
31+
// *************************************************************
32+
// * These messages should never be updated, *
33+
// * only be added when a new version is added, *
34+
// * or be removed when an old version is no longer supported. *
35+
// *************************************************************
36+
//
37+
38+
#[test]
39+
fn test_decode_v117_grant_object() -> anyhow::Result<()> {
40+
let role_info_v117 = vec![
41+
10, 2, 114, 49, 18, 172, 1, 10, 21, 10, 8, 10, 0, 160, 6, 117, 168, 6, 24, 16, 128, 128,
42+
128, 1, 160, 6, 117, 168, 6, 24, 10, 31, 10, 21, 18, 13, 10, 7, 100, 101, 102, 97, 117,
43+
108, 116, 18, 2, 100, 98, 160, 6, 117, 168, 6, 24, 16, 2, 160, 6, 117, 168, 6, 24, 10, 35,
44+
10, 25, 26, 17, 10, 7, 100, 101, 102, 97, 117, 108, 116, 18, 2, 100, 98, 26, 2, 116, 98,
45+
160, 6, 117, 168, 6, 24, 16, 2, 160, 6, 117, 168, 6, 24, 10, 22, 10, 12, 34, 4, 10, 2, 102,
46+
49, 160, 6, 117, 168, 6, 24, 16, 1, 160, 6, 117, 168, 6, 24, 10, 24, 10, 12, 42, 4, 10, 2,
47+
115, 49, 160, 6, 117, 168, 6, 24, 16, 128, 128, 32, 160, 6, 117, 168, 6, 24, 10, 21, 10, 8,
48+
10, 0, 160, 6, 117, 168, 6, 24, 16, 254, 255, 191, 1, 160, 6, 117, 168, 6, 24, 160, 6, 117,
49+
168, 6, 24, 26, 23, 49, 57, 55, 48, 45, 48, 49, 45, 48, 49, 32, 48, 48, 58, 48, 48, 58, 48,
50+
48, 32, 85, 84, 67, 34, 23, 49, 57, 55, 48, 45, 48, 49, 45, 48, 49, 32, 48, 48, 58, 48, 48,
51+
58, 48, 48, 32, 85, 84, 67, 160, 6, 117, 168, 6, 24,
52+
];
53+
let want = || mt::principal::RoleInfo {
54+
name: "r1".to_string(),
55+
grants: UserGrantSet::new(
56+
vec![
57+
mt::principal::GrantEntry::new(
58+
mt::principal::GrantObject::Global,
59+
make_bitflags!(UserPrivilegeType::{CreateWarehouse}),
60+
),
61+
mt::principal::GrantEntry::new(
62+
mt::principal::GrantObject::Database("default".to_string(), "db".to_string()),
63+
make_bitflags!(UserPrivilegeType::{Create}),
64+
),
65+
mt::principal::GrantEntry::new(
66+
mt::principal::GrantObject::Table(
67+
"default".to_string(),
68+
"db".to_string(),
69+
"tb".to_string(),
70+
),
71+
make_bitflags!(UserPrivilegeType::{Create}),
72+
),
73+
mt::principal::GrantEntry::new(
74+
mt::principal::GrantObject::UDF("f1".to_string()),
75+
make_bitflags!(UserPrivilegeType::{Usage}),
76+
),
77+
mt::principal::GrantEntry::new(
78+
mt::principal::GrantObject::Stage("s1".to_string()),
79+
make_bitflags!(UserPrivilegeType::{Write}),
80+
),
81+
// test new global privilege CreateWarehouse
82+
mt::principal::GrantEntry::new(
83+
mt::principal::GrantObject::Global,
84+
make_bitflags!(UserPrivilegeType::{Create | Select | Insert | Update | Delete | Drop | Alter | Super | CreateUser | DropUser | CreateRole | DropRole | Grant | CreateStage | Set | CreateDataMask | Ownership | Read | Write | CreateWarehouse }),
85+
),
86+
],
87+
HashSet::new(),
88+
),
89+
created_on: DateTime::<Utc>::default(),
90+
update_on: DateTime::<Utc>::default(),
91+
};
92+
93+
common::test_pb_from_to(func_name!(), want())?;
94+
common::test_load_old(func_name!(), role_info_v117.as_slice(), 117, want())?;
95+
96+
Ok(())
97+
}
98+
99+
#[test]
100+
fn test_decode_v117_ownership() -> anyhow::Result<()> {
101+
let ownership_info_v117 = vec![
102+
10, 2, 114, 49, 18, 19, 42, 11, 10, 9, 97, 117, 110, 105, 113, 117, 101, 105, 100, 160, 6,
103+
117, 168, 6, 24, 160, 6, 117, 168, 6, 24,
104+
];
105+
106+
let want = || mt::principal::OwnershipInfo {
107+
role: "r1".to_string(),
108+
object: OwnershipObject::Warehouse {
109+
uid: "auniqueid".to_string(),
110+
},
111+
};
112+
common::test_pb_from_to(func_name!(), want())?;
113+
common::test_load_old(func_name!(), ownership_info_v117.as_slice(), 117, want())?;
114+
115+
Ok(())
116+
}

src/meta/protos/proto/ownership.proto

+5
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,15 @@ message OwnershipObject {
4646
string stage = 1;
4747
}
4848

49+
message OwnershipWarehouseObject {
50+
string uid = 1;
51+
}
52+
4953
oneof object {
5054
OwnershipDatabaseObject database = 1;
5155
OwnershipTableObject table = 2;
5256
OwnershipUdfObject udf = 3;
5357
OwnershipStageObject stage = 4;
58+
OwnershipWarehouseObject warehouse = 5;
5459
}
5560
}

src/query/ast/src/ast/statements/principal.rs

+3
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ pub enum UserPrivilegeType {
150150
Write,
151151
// Privilege to Create database
152152
CreateDatabase,
153+
// Privilege to Create warehouse
154+
CreateWarehouse,
153155
// Discard Privilege Type
154156
Set,
155157
}
@@ -178,6 +180,7 @@ impl Display for UserPrivilegeType {
178180
UserPrivilegeType::Read => "Read",
179181
UserPrivilegeType::Write => "Write",
180182
UserPrivilegeType::CreateDatabase => "CREATE DATABASE",
183+
UserPrivilegeType::CreateWarehouse => "CREATE WAREHOUSE",
181184
})
182185
}
183186
}

src/query/ast/src/parser/statement.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -3278,6 +3278,10 @@ pub fn priv_type(i: Input) -> IResult<UserPrivilegeType> {
32783278
UserPrivilegeType::CreateDatabase,
32793279
rule! { CREATE ~ DATABASE },
32803280
),
3281+
value(
3282+
UserPrivilegeType::CreateWarehouse,
3283+
rule! { CREATE ~ WAREHOUSE },
3284+
),
32813285
value(UserPrivilegeType::DropUser, rule! { DROP ~ USER }),
32823286
value(UserPrivilegeType::CreateRole, rule! { CREATE ~ ROLE }),
32833287
value(UserPrivilegeType::DropRole, rule! { DROP ~ ROLE }),
@@ -3436,10 +3440,12 @@ pub fn grant_ownership_level(i: Input) -> IResult<AccountMgrLevel> {
34363440
enum Object {
34373441
Stage,
34383442
Udf,
3443+
Warehouse,
34393444
}
34403445
let object = alt((
34413446
value(Object::Udf, rule! { UDF }),
34423447
value(Object::Stage, rule! { STAGE }),
3448+
value(Object::Warehouse, rule! { STAGE }),
34433449
));
34443450

34453451
// Object object_name
@@ -3448,13 +3454,14 @@ pub fn grant_ownership_level(i: Input) -> IResult<AccountMgrLevel> {
34483454
|(object, object_name)| match object {
34493455
Object::Stage => AccountMgrLevel::Stage(object_name.to_string()),
34503456
Object::Udf => AccountMgrLevel::UDF(object_name.to_string()),
3457+
Object::Warehouse => AccountMgrLevel::Warehouse(object_name.to_string()),
34513458
},
34523459
);
34533460

34543461
rule!(
34553462
#db : "<database>.*"
34563463
| #table : "<database>.<table>"
3457-
| #object : "STAGE | UDF <object_name>"
3464+
| #object : "STAGE | UDF | WAREHOUSE <object_name>"
34583465
)(i)
34593466
}
34603467

src/query/management/src/role/role_mgr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ fn convert_to_grant_obj(owner_obj: &OwnershipObject) -> GrantObject {
504504
} => GrantObject::TableById(catalog_name.to_string(), *db_id, *table_id),
505505
OwnershipObject::Stage { name } => GrantObject::Stage(name.to_string()),
506506
OwnershipObject::UDF { name } => GrantObject::UDF(name.to_string()),
507+
OwnershipObject::Warehouse { uid } => GrantObject::Warehouse(uid.to_string()),
507508
}
508509
}
509510

0 commit comments

Comments
 (0)