forked from proxmox-mirrors/proxmox
access-control: define User
, UserWithTokens
and ApiTokens
types
these types are used by the user config in `proxmox-backup` server. this commit factors them out so we can re-use them in other products as well as this crate. Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
This commit is contained in:
parent
86ffeef24a
commit
47eeecf711
@ -16,7 +16,10 @@ description = "A collection of utilities to implement access control management.
|
|||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
nix.workspace = true
|
nix.workspace = true
|
||||||
openssl.workspace = true
|
openssl.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
|
||||||
# proxmox-notify.workspace = true
|
# proxmox-notify.workspace = true
|
||||||
proxmox-auth-api = { workspace = true, features = [ "api-types" ] }
|
proxmox-auth-api = { workspace = true, features = [ "api-types" ] }
|
||||||
|
proxmox-schema.workspace = true
|
||||||
proxmox-product-config.workspace = true
|
proxmox-product-config.workspace = true
|
||||||
|
proxmox-time.workspace = true
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
pub mod acl;
|
pub mod acl;
|
||||||
pub mod init;
|
pub mod init;
|
||||||
|
pub mod types;
|
||||||
|
228
proxmox-access-control/src/types.rs
Normal file
228
proxmox-access-control/src/types.rs
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
use proxmox_auth_api::types::{Authid, Userid, PROXMOX_TOKEN_ID_SCHEMA};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use proxmox_schema::{
|
||||||
|
api,
|
||||||
|
api_types::{COMMENT_SCHEMA, SINGLE_LINE_COMMENT_FORMAT},
|
||||||
|
BooleanSchema, IntegerSchema, Schema, StringSchema, Updater,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ENABLE_USER_SCHEMA: Schema = BooleanSchema::new(
|
||||||
|
"Enable the account (default). You can set this to '0' to disable the account.",
|
||||||
|
)
|
||||||
|
.default(true)
|
||||||
|
.schema();
|
||||||
|
|
||||||
|
pub const EXPIRE_USER_SCHEMA: Schema = IntegerSchema::new(
|
||||||
|
"Account expiration date (seconds since epoch). '0' means no expiration date.",
|
||||||
|
)
|
||||||
|
.default(0)
|
||||||
|
.minimum(0)
|
||||||
|
.schema();
|
||||||
|
|
||||||
|
pub const FIRST_NAME_SCHEMA: Schema = StringSchema::new("First name.")
|
||||||
|
.format(&SINGLE_LINE_COMMENT_FORMAT)
|
||||||
|
.min_length(2)
|
||||||
|
.max_length(64)
|
||||||
|
.schema();
|
||||||
|
|
||||||
|
pub const LAST_NAME_SCHEMA: Schema = StringSchema::new("Last name.")
|
||||||
|
.format(&SINGLE_LINE_COMMENT_FORMAT)
|
||||||
|
.min_length(2)
|
||||||
|
.max_length(64)
|
||||||
|
.schema();
|
||||||
|
|
||||||
|
pub const EMAIL_SCHEMA: Schema = StringSchema::new("E-Mail Address.")
|
||||||
|
.format(&SINGLE_LINE_COMMENT_FORMAT)
|
||||||
|
.min_length(2)
|
||||||
|
.max_length(64)
|
||||||
|
.schema();
|
||||||
|
|
||||||
|
#[api(
|
||||||
|
properties: {
|
||||||
|
userid: {
|
||||||
|
type: Userid,
|
||||||
|
},
|
||||||
|
comment: {
|
||||||
|
optional: true,
|
||||||
|
schema: COMMENT_SCHEMA,
|
||||||
|
},
|
||||||
|
enable: {
|
||||||
|
optional: true,
|
||||||
|
schema: ENABLE_USER_SCHEMA,
|
||||||
|
},
|
||||||
|
expire: {
|
||||||
|
optional: true,
|
||||||
|
schema: EXPIRE_USER_SCHEMA,
|
||||||
|
},
|
||||||
|
firstname: {
|
||||||
|
optional: true,
|
||||||
|
schema: FIRST_NAME_SCHEMA,
|
||||||
|
},
|
||||||
|
lastname: {
|
||||||
|
schema: LAST_NAME_SCHEMA,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
schema: EMAIL_SCHEMA,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
tokens: {
|
||||||
|
type: Array,
|
||||||
|
optional: true,
|
||||||
|
description: "List of user's API tokens.",
|
||||||
|
items: {
|
||||||
|
type: ApiToken
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"totp-locked": {
|
||||||
|
type: bool,
|
||||||
|
optional: true,
|
||||||
|
default: false,
|
||||||
|
description: "True if the user is currently locked out of TOTP factors",
|
||||||
|
},
|
||||||
|
"tfa-locked-until": {
|
||||||
|
optional: true,
|
||||||
|
description: "Contains a timestamp until when a user is locked out of 2nd factors",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)]
|
||||||
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
/// User properties with added list of ApiTokens
|
||||||
|
pub struct UserWithTokens {
|
||||||
|
pub userid: Userid,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub comment: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub enable: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub expire: Option<i64>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub firstname: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub lastname: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub email: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||||
|
pub tokens: Vec<ApiToken>,
|
||||||
|
#[serde(skip_serializing_if = "bool_is_false", default)]
|
||||||
|
pub totp_locked: bool,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tfa_locked_until: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool_is_false(b: &bool) -> bool {
|
||||||
|
!b
|
||||||
|
}
|
||||||
|
|
||||||
|
#[api(
|
||||||
|
properties: {
|
||||||
|
tokenid: {
|
||||||
|
schema: PROXMOX_TOKEN_ID_SCHEMA,
|
||||||
|
},
|
||||||
|
comment: {
|
||||||
|
optional: true,
|
||||||
|
schema: COMMENT_SCHEMA,
|
||||||
|
},
|
||||||
|
enable: {
|
||||||
|
optional: true,
|
||||||
|
schema: ENABLE_USER_SCHEMA,
|
||||||
|
},
|
||||||
|
expire: {
|
||||||
|
optional: true,
|
||||||
|
schema: EXPIRE_USER_SCHEMA,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)]
|
||||||
|
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
/// ApiToken properties.
|
||||||
|
pub struct ApiToken {
|
||||||
|
pub tokenid: Authid,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub comment: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub enable: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub expire: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiToken {
|
||||||
|
pub fn is_active(&self) -> bool {
|
||||||
|
if !self.enable.unwrap_or(true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let Some(expire) = self.expire {
|
||||||
|
let now = proxmox_time::epoch_i64();
|
||||||
|
if expire > 0 && expire <= now {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[api(
|
||||||
|
properties: {
|
||||||
|
userid: {
|
||||||
|
type: Userid,
|
||||||
|
},
|
||||||
|
comment: {
|
||||||
|
optional: true,
|
||||||
|
schema: COMMENT_SCHEMA,
|
||||||
|
},
|
||||||
|
enable: {
|
||||||
|
optional: true,
|
||||||
|
schema: ENABLE_USER_SCHEMA,
|
||||||
|
},
|
||||||
|
expire: {
|
||||||
|
optional: true,
|
||||||
|
schema: EXPIRE_USER_SCHEMA,
|
||||||
|
},
|
||||||
|
firstname: {
|
||||||
|
optional: true,
|
||||||
|
schema: FIRST_NAME_SCHEMA,
|
||||||
|
},
|
||||||
|
lastname: {
|
||||||
|
schema: LAST_NAME_SCHEMA,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
schema: EMAIL_SCHEMA,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)]
|
||||||
|
#[derive(Serialize, Deserialize, Updater, PartialEq, Eq)]
|
||||||
|
/// User properties.
|
||||||
|
pub struct User {
|
||||||
|
#[updater(skip)]
|
||||||
|
pub userid: Userid,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub comment: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub enable: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub expire: Option<i64>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub firstname: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub lastname: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub email: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl User {
|
||||||
|
pub fn is_active(&self) -> bool {
|
||||||
|
if !self.enable.unwrap_or(true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let Some(expire) = self.expire {
|
||||||
|
let now = proxmox_time::epoch_i64();
|
||||||
|
if expire > 0 && expire <= now {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user