Skip to content

Commit a36804f

Browse files
authored
feat(query):masking policy support rbac (#18982)
* feat(query):masking policy support rbac * add some comment * add some unit test
1 parent 9a27184 commit a36804f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1157
-78
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/meta/api/src/data_mask_api.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ pub trait DatamaskApi: Send + Sync {
4848
name_ident: &DataMaskNameIdent,
4949
) -> Result<Option<SeqV<DatamaskMeta>>, MetaError>;
5050

51+
async fn get_data_mask_id(
52+
&self,
53+
name_ident: &DataMaskNameIdent,
54+
) -> Result<Option<SeqV<DataMaskId>>, MetaError>;
55+
56+
async fn get_data_mask_name_by_id(
57+
&self,
58+
tenant: &Tenant,
59+
policy_id: u64,
60+
) -> Result<Option<String>, MetaError>;
61+
5162
async fn get_data_mask_by_id(
5263
&self,
5364
tenant: &Tenant,

src/meta/api/src/data_mask_api_impl.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ use databend_common_meta_app::data_mask::CreateDatamaskReply;
1717
use databend_common_meta_app::data_mask::CreateDatamaskReq;
1818
use databend_common_meta_app::data_mask::DataMaskId;
1919
use databend_common_meta_app::data_mask::DataMaskIdIdent;
20+
use databend_common_meta_app::data_mask::DataMaskIdToNameIdent;
2021
use databend_common_meta_app::data_mask::DataMaskNameIdent;
22+
use databend_common_meta_app::data_mask::DataMaskNameIdentRaw;
2123
use databend_common_meta_app::data_mask::DatamaskMeta;
2224
use databend_common_meta_app::data_mask::MaskPolicyIdTableId;
2325
use databend_common_meta_app::data_mask::MaskPolicyTableIdIdent;
@@ -46,6 +48,7 @@ use crate::txn_condition_util::txn_cond_eq_keys_with_prefix;
4648
use crate::txn_condition_util::txn_cond_eq_seq;
4749
use crate::txn_core_util::send_txn;
4850
use crate::txn_core_util::txn_delete_exact;
51+
use crate::txn_op_builder_util::txn_op_del;
4952
use crate::txn_op_builder_util::txn_op_put_pb;
5053

5154
/// DatamaskApi is implemented upon kvapi::KVApi.
@@ -77,6 +80,8 @@ impl<KV: kvapi::KVApi<Error = MetaError>> DatamaskApi for KV {
7780

7881
let id = DataMaskId::new(masking_policy_id);
7982
let id_ident = DataMaskIdIdent::new_generic(name_ident.tenant(), id);
83+
let id_to_name_ident = DataMaskIdToNameIdent::new_generic(name_ident.tenant(), id);
84+
let name_raw = DataMaskNameIdentRaw::from(name_ident.clone());
8085
let id_list_key = MaskPolicyTableIdListIdent::new_from(name_ident.clone());
8186

8287
debug!(
@@ -91,9 +96,11 @@ impl<KV: kvapi::KVApi<Error = MetaError>> DatamaskApi for KV {
9196
txn.condition.push(txn_cond_eq_seq(name_ident, 0));
9297
txn.condition
9398
.push(txn_cond_eq_seq(&row_access_name_ident, 0));
99+
94100
txn.if_then.extend(vec![
95-
txn_op_put_pb(name_ident, &id, None)?, // name -> db_id
101+
txn_op_put_pb(name_ident, &id, None)?, // name -> masking_policy_id
96102
txn_op_put_pb(&id_ident, &meta, None)?, // id -> meta
103+
txn_op_put_pb(&id_to_name_ident, &name_raw, None)?, // id -> name
97104
// TODO: Tentative retention for compatibility MaskPolicyTableIdListIdent related logic. It can be directly deleted later
98105
txn_op_put_pb(&id_list_key, &id_list, None)?, // data mask name -> id_list
99106
]);
@@ -159,7 +166,9 @@ impl<KV: kvapi::KVApi<Error = MetaError>> DatamaskApi for KV {
159166
}
160167

161168
// No references - drop the policy
162-
let id_ident = seq_id.data.into_t_ident(tenant);
169+
let id_ident = seq_id.data.into_t_ident(tenant.clone());
170+
let id_to_name_ident =
171+
DataMaskIdToNameIdent::new_generic(tenant, DataMaskId::new(policy_id));
163172
let mut txn = TxnRequest::default();
164173

165174
// Ensure no new references were created
@@ -168,6 +177,7 @@ impl<KV: kvapi::KVApi<Error = MetaError>> DatamaskApi for KV {
168177

169178
txn_delete_exact(&mut txn, name_ident, seq_id.seq);
170179
txn_delete_exact(&mut txn, &id_ident, seq_meta.seq);
180+
txn.if_then.push(txn_op_del(&id_to_name_ident));
171181

172182
// TODO: Tentative retention for compatibility. Can be deleted later.
173183
clear_table_column_mask_policy(self, name_ident, &mut txn).await?;
@@ -191,6 +201,26 @@ impl<KV: kvapi::KVApi<Error = MetaError>> DatamaskApi for KV {
191201
Ok(res.map(|(_, seq_meta)| seq_meta))
192202
}
193203

204+
async fn get_data_mask_id(
205+
&self,
206+
name_ident: &DataMaskNameIdent,
207+
) -> Result<Option<SeqV<DataMaskId>>, MetaError> {
208+
self.get_pb(name_ident).await
209+
}
210+
211+
async fn get_data_mask_name_by_id(
212+
&self,
213+
tenant: &Tenant,
214+
policy_id: u64,
215+
) -> Result<Option<String>, MetaError> {
216+
let ident = DataMaskIdToNameIdent::new_generic(tenant.clone(), DataMaskId::new(policy_id));
217+
let seq_meta = self.get_pb(&ident).await?;
218+
219+
debug!(ident :% =(&ident); "get_data_mask_name_by_id");
220+
221+
Ok(seq_meta.map(|s| s.data.data_mask_name().to_string()))
222+
}
223+
194224
async fn get_data_mask_by_id(
195225
&self,
196226
tenant: &Tenant,

src/meta/api/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
extern crate databend_common_meta_types;
2121
pub mod catalog_api;
22-
mod data_mask_api;
22+
pub mod data_mask_api;
2323
mod data_mask_api_impl;
2424
pub mod data_retention_util;
2525
mod database_api;

src/meta/api/src/row_access_policy_api.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ pub trait RowAccessPolicyApi: Send + Sync {
5151
name_ident: &RowAccessPolicyNameIdent,
5252
) -> Result<Option<(SeqV<RowAccessPolicyId>, SeqV<RowAccessPolicyMeta>)>, MetaError>;
5353

54+
async fn get_row_access_policy_name_by_id(
55+
&self,
56+
tenant: &Tenant,
57+
policy_id: u64,
58+
) -> Result<Option<String>, MetaError>;
59+
5460
async fn get_row_access_policy_by_id(
5561
&self,
5662
tenant: &Tenant,

src/meta/api/src/row_access_policy_api_impl.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ use databend_common_meta_app::row_access_policy::CreateRowAccessPolicyReply;
2020
use databend_common_meta_app::row_access_policy::CreateRowAccessPolicyReq;
2121
use databend_common_meta_app::row_access_policy::RowAccessPolicyId;
2222
use databend_common_meta_app::row_access_policy::RowAccessPolicyIdIdent;
23+
use databend_common_meta_app::row_access_policy::RowAccessPolicyIdToNameIdent;
2324
use databend_common_meta_app::row_access_policy::RowAccessPolicyMeta;
2425
use databend_common_meta_app::row_access_policy::RowAccessPolicyNameIdent;
26+
use databend_common_meta_app::row_access_policy::RowAccessPolicyNameIdentRaw;
2527
use databend_common_meta_app::row_access_policy::RowAccessPolicyTableIdIdent;
2628
use databend_common_meta_app::tenant::Tenant;
2729
use databend_common_meta_app::tenant_key::errors::ExistError;
@@ -44,6 +46,7 @@ use crate::txn_condition_util::txn_cond_eq_keys_with_prefix;
4446
use crate::txn_condition_util::txn_cond_eq_seq;
4547
use crate::txn_core_util::send_txn;
4648
use crate::txn_core_util::txn_delete_exact;
49+
use crate::txn_op_builder_util::txn_op_del;
4750
use crate::txn_op_builder_util::txn_op_put_pb;
4851

4952
/// RowAccessPolicyApi is implemented upon kvapi::KVApi.
@@ -73,8 +76,12 @@ impl<KV: kvapi::KVApi<Error = MetaError>> RowAccessPolicyApi for KV {
7376
// Create row policy by inserting these record:
7477
// name -> id
7578
// id -> policy
79+
// id -> name
7680

7781
let id_ident = RowAccessPolicyIdIdent::new_generic(name_ident.tenant(), policy_id);
82+
let id_to_name_ident =
83+
RowAccessPolicyIdToNameIdent::new_generic(name_ident.tenant(), policy_id);
84+
let name_raw = RowAccessPolicyNameIdentRaw::from(name_ident.clone());
7885

7986
debug!(
8087
id :? =(&id_ident),
@@ -89,6 +96,7 @@ impl<KV: kvapi::KVApi<Error = MetaError>> RowAccessPolicyApi for KV {
8996
txn.if_then.extend(vec![
9097
txn_op_put_pb(name_ident, &policy_id, None)?, // name -> policy_id
9198
txn_op_put_pb(&id_ident, &meta, None)?, // id -> meta
99+
txn_op_put_pb(&id_to_name_ident, &name_raw, None)?, // id -> name
92100
]);
93101
}
94102

@@ -151,6 +159,10 @@ impl<KV: kvapi::KVApi<Error = MetaError>> RowAccessPolicyApi for KV {
151159

152160
// No references - drop the policy
153161
let id_ident = seq_id.data.into_t_ident(tenant);
162+
let id_to_name_ident = RowAccessPolicyIdToNameIdent::new_generic(
163+
tenant.clone(),
164+
RowAccessPolicyId::new(policy_id),
165+
);
154166
let mut txn = TxnRequest::default();
155167

156168
// Ensure no new references were created
@@ -159,6 +171,7 @@ impl<KV: kvapi::KVApi<Error = MetaError>> RowAccessPolicyApi for KV {
159171

160172
txn_delete_exact(&mut txn, name_ident, seq_id.seq);
161173
txn_delete_exact(&mut txn, &id_ident, seq_meta.seq);
174+
txn.if_then.push(txn_op_del(&id_to_name_ident));
162175

163176
let (succ, _responses) = send_txn(self, txn).await?;
164177
if succ {
@@ -179,6 +192,24 @@ impl<KV: kvapi::KVApi<Error = MetaError>> RowAccessPolicyApi for KV {
179192
Ok(res)
180193
}
181194

195+
async fn get_row_access_policy_name_by_id(
196+
&self,
197+
tenant: &Tenant,
198+
policy_id: u64,
199+
) -> Result<Option<String>, MetaError> {
200+
let ident = RowAccessPolicyIdToNameIdent::new_generic(
201+
tenant.clone(),
202+
RowAccessPolicyId::new(policy_id),
203+
);
204+
let seq_meta = self.get_pb(&ident).await?;
205+
206+
debug!(ident :% =(&ident); "get_row_access_policy_name_by_id");
207+
208+
let name = seq_meta.map(|s| s.data.row_access_name().to_string());
209+
210+
Ok(name)
211+
}
212+
182213
async fn get_row_access_policy_by_id(
183214
&self,
184215
tenant: &Tenant,
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2021 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 crate::data_mask::DataMaskId;
16+
use crate::tenant_key::ident::TIdent;
17+
use crate::tenant_key::raw::TIdentRaw;
18+
19+
/// Tenantless key mapping a masking policy id back to its name.
20+
/// This enables reverse lookup for SHOW GRANTS and ownership listings.
21+
pub type DataMaskIdToNameIdent = TIdent<Resource, DataMaskId>;
22+
/// Raw form of [`DataMaskIdToNameIdent`] used for serde/protobuf.
23+
pub type DataMaskIdToNameIdentRaw = TIdentRaw<Resource, DataMaskId>;
24+
25+
pub use kvapi_impl::Resource;
26+
27+
impl DataMaskIdToNameIdent {
28+
pub fn data_mask_id(&self) -> DataMaskId {
29+
*self.name()
30+
}
31+
}
32+
33+
impl DataMaskIdToNameIdentRaw {
34+
pub fn data_mask_id(&self) -> DataMaskId {
35+
*self.name()
36+
}
37+
}
38+
39+
mod kvapi_impl {
40+
41+
use databend_common_meta_kvapi::kvapi;
42+
43+
use crate::data_mask::data_mask_id_to_name_ident::DataMaskIdToNameIdent;
44+
use crate::data_mask::data_mask_name_ident::DataMaskNameIdentRaw;
45+
use crate::tenant_key::resource::TenantResource;
46+
47+
pub struct Resource;
48+
impl TenantResource for Resource {
49+
const PREFIX: &'static str = "__fd_datamask_id_to_name";
50+
const TYPE: &'static str = "DataMaskIdToNameIdent";
51+
const HAS_TENANT: bool = true;
52+
type ValueType = DataMaskNameIdentRaw;
53+
}
54+
55+
impl kvapi::Value for DataMaskNameIdentRaw {
56+
type KeyType = DataMaskIdToNameIdent;
57+
fn dependency_keys(&self, _key: &Self::KeyType) -> impl IntoIterator<Item = String> {
58+
[]
59+
}
60+
}
61+
}
62+
63+
#[cfg(test)]
64+
mod tests {
65+
use databend_common_meta_kvapi::kvapi::Key;
66+
67+
use crate::data_mask::DataMaskId;
68+
use crate::data_mask::DataMaskIdToNameIdent;
69+
use crate::tenant::Tenant;
70+
71+
#[test]
72+
fn test_data_mask_id_ident() {
73+
let tenant = Tenant::new_literal("dummy");
74+
let ident = DataMaskIdToNameIdent::new_generic(tenant, DataMaskId::new(3));
75+
76+
let key = ident.to_string_key();
77+
assert_eq!(key, "__fd_datamask_id_to_name/dummy/3");
78+
79+
assert_eq!(ident, DataMaskIdToNameIdent::from_str_key(&key).unwrap());
80+
}
81+
}

src/meta/app/src/data_mask/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
pub mod data_mask_id_ident;
16+
pub mod data_mask_id_to_name_ident;
1617
pub mod data_mask_name_ident;
1718
pub mod mask_policy_policy_table_id_ident;
1819
pub mod mask_policy_table_id_list_ident;
@@ -23,7 +24,10 @@ use chrono::DateTime;
2324
use chrono::Utc;
2425
pub use data_mask_id_ident::DataMaskId;
2526
pub use data_mask_id_ident::DataMaskIdIdent;
27+
pub use data_mask_id_to_name_ident::DataMaskIdToNameIdent;
28+
pub use data_mask_id_to_name_ident::DataMaskIdToNameIdentRaw;
2629
pub use data_mask_name_ident::DataMaskNameIdent;
30+
pub use data_mask_name_ident::DataMaskNameIdentRaw;
2731
pub use mask_policy_policy_table_id_ident::MaskPolicyIdTableId;
2832
pub use mask_policy_policy_table_id_ident::MaskPolicyTableIdIdent;
2933
pub use mask_policy_table_id_list_ident::MaskPolicyTableIdListIdent;

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ use databend_common_meta_kvapi::kvapi::KeyCodec;
3030
/// - `table-by-catalog-id/<catalog>/<table_id>`
3131
/// - `stage-by-name/<stage_name>`
3232
/// - `udf-by-name/<udf_name>`
33+
/// - `procedure-by-id/<procedure_id>`
34+
/// - `masking-policy-by-id/<policy_id>`
3335
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
3436
pub enum OwnershipObject {
3537
/// used on the fuse databases
@@ -68,6 +70,10 @@ pub enum OwnershipObject {
6870
Procedure {
6971
procedure_id: u64,
7072
},
73+
74+
MaskingPolicy {
75+
policy_id: u64,
76+
},
7177
}
7278

7379
impl OwnershipObject {
@@ -100,6 +106,9 @@ impl fmt::Display for OwnershipObject {
100106
OwnershipObject::Connection { name } => write!(f, "CONNECTION {name}"),
101107
OwnershipObject::Sequence { name } => write!(f, "SEQUENCE {name}"),
102108
OwnershipObject::Procedure { procedure_id } => write!(f, "PROCEDURE {procedure_id}"),
109+
OwnershipObject::MaskingPolicy { policy_id } => {
110+
write!(f, "MASKING POLICY {policy_id}")
111+
}
103112
}
104113
}
105114
}
@@ -145,6 +154,9 @@ impl KeyCodec for OwnershipObject {
145154
OwnershipObject::Procedure { procedure_id } => {
146155
b.push_raw("procedure-by-id").push_u64(*procedure_id)
147156
}
157+
OwnershipObject::MaskingPolicy { policy_id } => {
158+
b.push_raw("masking-policy-by-id").push_u64(*policy_id)
159+
}
148160
}
149161
}
150162

@@ -207,9 +219,13 @@ impl KeyCodec for OwnershipObject {
207219
let procedure_id = p.next_u64()?;
208220
Ok(OwnershipObject::Procedure { procedure_id })
209221
}
222+
"masking-policy-by-id" => {
223+
let policy_id = p.next_u64()?;
224+
Ok(OwnershipObject::MaskingPolicy { policy_id })
225+
}
210226
_ => Err(kvapi::KeyError::InvalidSegment {
211227
i: p.index(),
212-
expect: "database-by-id|database-by-catalog-id|table-by-id|table-by-catalog-id|stage-by-name|udf-by-name|warehouse-by-id|connection-by-name"
228+
expect: "database-by-id|database-by-catalog-id|table-by-id|table-by-catalog-id|stage-by-name|udf-by-name|warehouse-by-id|connection-by-name|masking-policy-by-id"
213229
.to_string(),
214230
got: q.to_string(),
215231
}),

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,23 @@ mod tests {
271271
let parsed = TenantOwnershipObjectIdent::from_str_key(&key).unwrap();
272272
assert_eq!(role_grantee, parsed);
273273
}
274+
275+
// masking policy
276+
{
277+
let role_grantee = TenantOwnershipObjectIdent::new_unchecked(
278+
Tenant::new_literal("tenant_mask"),
279+
OwnershipObject::MaskingPolicy { policy_id: 99 },
280+
);
281+
282+
let key = role_grantee.to_string_key();
283+
assert_eq!(
284+
"__fd_object_owners/tenant_mask/masking-policy-by-id/99",
285+
key
286+
);
287+
288+
let parsed = TenantOwnershipObjectIdent::from_str_key(&key).unwrap();
289+
assert_eq!(role_grantee, parsed);
290+
}
274291
}
275292

276293
#[test]

0 commit comments

Comments
 (0)