Skip to content

Commit

Permalink
feat(query): Support create password policy
Browse files Browse the repository at this point in the history
  • Loading branch information
b41sh committed Dec 19, 2023
1 parent 4705cae commit d7a664e
Show file tree
Hide file tree
Showing 54 changed files with 2,824 additions and 31 deletions.
41 changes: 41 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/common/exception/src/exception_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ build_exceptions! {
NetworkPolicyAlreadyExists(2208),
IllegalNetworkPolicy(2209),
NetworkPolicyIsUsedByUser(2210),
UnknownPasswordPolicy(2211),
PasswordPolicyAlreadyExists(2212),
IllegalPasswordPolicy(2213),
PasswordPolicyIsUsedByUser(2214),
InvalidPassword(2215),

// Meta api error codes.
DatabaseAlreadyExists(2301),
Expand Down
2 changes: 2 additions & 0 deletions src/meta/app/src/principal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod connection;
mod file_format;
mod network_policy;
mod ownership_info;
mod password_policy;
mod principal_identity;
mod role_info;
mod user_auth;
Expand All @@ -35,6 +36,7 @@ pub use connection::*;
pub use file_format::*;
pub use network_policy::NetworkPolicy;
pub use ownership_info::OwnershipInfo;
pub use password_policy::PasswordPolicy;
pub use principal_identity::PrincipalIdentity;
pub use role_info::RoleInfo;
pub use role_info::RoleInfoSerdeError;
Expand Down
35 changes: 35 additions & 0 deletions src/meta/app/src/principal/password_policy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2021 Datafuse Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use chrono::DateTime;
use chrono::Utc;

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq, Default)]
pub struct PasswordPolicy {
pub name: String,
pub min_length: u64,
pub max_length: u64,
pub min_upper_case_chars: u64,
pub min_lower_case_chars: u64,
pub min_numeric_chars: u64,
pub min_special_chars: u64,
pub min_age_days: u64,
pub max_age_days: u64,
pub max_retries: u64,
pub lockout_time_mins: u64,
pub history: u64,
pub comment: String,
pub create_on: DateTime<Utc>,
pub update_on: Option<DateTime<Utc>>,
}
16 changes: 16 additions & 0 deletions src/meta/app/src/principal/user_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ pub struct UserOption {
default_role: Option<String>,

network_policy: Option<String>,

password_policy: Option<String>,
}

impl UserOption {
Expand All @@ -115,6 +117,7 @@ impl UserOption {
flags,
default_role: None,
network_policy: None,
password_policy: None,
}
}

Expand All @@ -137,6 +140,11 @@ impl UserOption {
self
}

pub fn with_password_policy(mut self, password_policy: Option<String>) -> Self {
self.password_policy = password_policy;
self
}

pub fn with_set_flag(mut self, flag: UserOptionFlag) -> Self {
self.flags.insert(flag);
self
Expand All @@ -154,6 +162,10 @@ impl UserOption {
self.network_policy.as_ref()
}

pub fn password_policy(&self) -> Option<&String> {
self.password_policy.as_ref()
}

pub fn set_default_role(&mut self, default_role: Option<String>) {
self.default_role = default_role;
}
Expand All @@ -162,6 +174,10 @@ impl UserOption {
self.network_policy = network_policy;
}

pub fn set_password_policy(&mut self, password_policy: Option<String>) {
self.password_policy = password_policy;
}

pub fn set_all_flag(&mut self) {
self.flags = BitFlags::all();
}
Expand Down
60 changes: 59 additions & 1 deletion src/meta/proto-conv/src/user_from_to_protobuf_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ impl FromToProto for mt::principal::UserOption {
Ok(mt::principal::UserOption::default()
.with_flags(flags)
.with_default_role(p.default_role)
.with_network_policy(p.network_policy))
.with_network_policy(p.network_policy)
.with_password_policy(p.password_policy))
}

fn to_pb(&self) -> Result<pb::UserOption, Incompatible> {
Expand All @@ -109,6 +110,7 @@ impl FromToProto for mt::principal::UserOption {
flags: self.flags().bits(),
default_role: self.default_role().cloned(),
network_policy: self.network_policy().cloned(),
password_policy: self.password_policy().cloned(),
})
}
}
Expand Down Expand Up @@ -389,3 +391,59 @@ impl FromToProto for mt::principal::NetworkPolicy {
})
}
}

impl FromToProto for mt::principal::PasswordPolicy {
type PB = pb::PasswordPolicy;
fn get_pb_ver(p: &Self::PB) -> u64 {
p.ver
}
fn from_pb(p: pb::PasswordPolicy) -> Result<Self, Incompatible>
where Self: Sized {
reader_check_msg(p.ver, p.min_reader_ver)?;
Ok(mt::principal::PasswordPolicy {
name: p.name.clone(),
min_length: p.min_length,
max_length: p.max_length,
min_upper_case_chars: p.min_upper_case_chars,
min_lower_case_chars: p.min_lower_case_chars,
min_numeric_chars: p.min_numeric_chars,
min_special_chars: p.min_special_chars,
min_age_days: p.min_age_days,
max_age_days: p.max_age_days,
max_retries: p.max_retries,
lockout_time_mins: p.lockout_time_mins,
history: p.history,
comment: p.comment,
create_on: DateTime::<Utc>::from_pb(p.create_on)?,
update_on: match p.update_on {
Some(t) => Some(DateTime::<Utc>::from_pb(t)?),
None => None,
},
})
}

fn to_pb(&self) -> Result<pb::PasswordPolicy, Incompatible> {
Ok(pb::PasswordPolicy {
ver: VER,
min_reader_ver: MIN_READER_VER,
name: self.name.clone(),
min_length: self.min_length,
max_length: self.max_length,
min_upper_case_chars: self.min_upper_case_chars,
min_lower_case_chars: self.min_lower_case_chars,
min_numeric_chars: self.min_numeric_chars,
min_special_chars: self.min_special_chars,
min_age_days: self.min_age_days,
max_age_days: self.max_age_days,
max_retries: self.max_retries,
lockout_time_mins: self.lockout_time_mins,
history: self.history,
comment: self.comment.clone(),
create_on: self.create_on.to_pb()?,
update_on: match &self.update_on {
Some(t) => Some(t.to_pb()?),
None => None,
},
})
}
}
1 change: 1 addition & 0 deletions src/meta/proto-conv/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const META_CHANGE_LOG: &[(u64, &str)] = &[
(64, "2023-11-16: Add: user.proto/NDJsonFileFormatParams add field `missing_field_as` and `null_field_as`", ),
(65, "2023-11-16: Retype: use Datetime<Utc> instead of u64 to in lvt.time", ),
(66, "2023-12-15: Add: stage.proto/StageInfo::created_on", ),
(67, "2023-12-19: Add: user.proto/PasswordPolicy and UserOption::password_policy", ),
// Dear developer:
// If you're gonna add a new metadata version, you'll have to add a test for it.
// You could just copy an existing test file(e.g., `../tests/it/v024_table_meta.rs`)
Expand Down
1 change: 1 addition & 0 deletions src/meta/proto-conv/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,4 @@ mod v063_connection;
mod v064_ndjson_format_params;
mod v065_least_visible_time;
mod v066_stage_create_on;
mod v067_password_policy;
110 changes: 110 additions & 0 deletions src/meta/proto-conv/tests/it/v067_password_policy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2021 Datafuse Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;

use chrono::TimeZone;
use chrono::Utc;
use databend_common_meta_app::principal::UserPrivilegeType;
use enumflags2::make_bitflags;
use minitrace::func_name;

use crate::common;

// These bytes are built when a new version in introduced,
// and are kept for backward compatibility test.
//
// *************************************************************
// * These messages should never be updated, *
// * only be added when a new version is added, *
// * or be removed when an old version is no longer supported. *
// *************************************************************
//
// The message bytes are built from the output of `test_build_pb_buf()`
#[test]
fn test_decode_v67_password_policy() -> anyhow::Result<()> {
// password policy
let bytes: Vec<u8> = vec![
10, 19, 116, 101, 115, 116, 112, 97, 115, 115, 119, 111, 114, 100, 112, 111, 108, 105, 99,
121, 49, 16, 8, 24, 30, 32, 1, 40, 2, 48, 3, 56, 4, 64, 10, 72, 90, 80, 5, 88, 10, 96, 1,
106, 12, 115, 111, 109, 101, 32, 99, 111, 109, 109, 101, 110, 116, 114, 23, 50, 48, 49, 52,
45, 49, 49, 45, 50, 56, 32, 49, 50, 58, 48, 48, 58, 48, 57, 32, 85, 84, 67, 122, 23, 50,
48, 49, 52, 45, 49, 49, 45, 50, 56, 32, 49, 50, 58, 48, 48, 58, 48, 57, 32, 85, 84, 67,
160, 6, 67, 168, 6, 24,
];

let want = || databend_common_meta_app::principal::PasswordPolicy {
name: "testpasswordpolicy1".to_string(),
min_length: 8,
max_length: 30,
min_upper_case_chars: 1,
min_lower_case_chars: 2,
min_numeric_chars: 3,
min_special_chars: 4,
min_age_days: 10,
max_age_days: 90,
max_retries: 5,
lockout_time_mins: 10,
history: 1,
comment: "some comment".to_string(),
create_on: Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 9).unwrap(),
update_on: Some(Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 9).unwrap()),
};

common::test_pb_from_to(func_name!(), want())?;
common::test_load_old(func_name!(), bytes.as_slice(), 67, want())?;

// user info with password policy
let bytes: Vec<u8> = vec![
10, 9, 116, 101, 115, 116, 95, 117, 115, 101, 114, 18, 1, 37, 26, 25, 18, 17, 10, 13, 116,
101, 115, 116, 95, 112, 97, 115, 115, 119, 111, 114, 100, 16, 1, 160, 6, 67, 168, 6, 24,
34, 26, 10, 18, 10, 8, 10, 0, 160, 6, 67, 168, 6, 24, 16, 2, 160, 6, 67, 168, 6, 24, 160,
6, 67, 168, 6, 24, 42, 15, 8, 10, 16, 128, 80, 24, 128, 160, 1, 160, 6, 67, 168, 6, 24, 50,
46, 8, 1, 18, 5, 114, 111, 108, 101, 49, 26, 8, 109, 121, 112, 111, 108, 105, 99, 121, 34,
19, 116, 101, 115, 116, 112, 97, 115, 115, 119, 111, 114, 100, 112, 111, 108, 105, 99, 121,
49, 160, 6, 67, 168, 6, 24, 160, 6, 67, 168, 6, 24,
];

let want = || databend_common_meta_app::principal::UserInfo {
name: "test_user".to_string(),
hostname: "%".to_string(),
auth_info: databend_common_meta_app::principal::AuthInfo::Password {
hash_value: [
116, 101, 115, 116, 95, 112, 97, 115, 115, 119, 111, 114, 100,
]
.to_vec(),
hash_method: databend_common_meta_app::principal::PasswordHashMethod::DoubleSha1,
},
grants: databend_common_meta_app::principal::UserGrantSet::new(
vec![databend_common_meta_app::principal::GrantEntry::new(
databend_common_meta_app::principal::GrantObject::Global,
make_bitflags!(UserPrivilegeType::{Create}),
)],
HashSet::new(),
),
quota: databend_common_meta_app::principal::UserQuota {
max_cpu: 10,
max_memory_in_bytes: 10240,
max_storage_in_bytes: 20480,
},
option: databend_common_meta_app::principal::UserOption::default()
.with_set_flag(databend_common_meta_app::principal::UserOptionFlag::TenantSetting)
.with_default_role(Some("role1".into()))
.with_network_policy(Some("mypolicy".to_string()))
.with_password_policy(Some("testpasswordpolicy1".to_string())),
};

common::test_pb_from_to(func_name!(), want())?;
common::test_load_old(func_name!(), bytes.as_slice(), 67, want())
}
Loading

0 comments on commit d7a664e

Please sign in to comment.