[pbs-devel] [RFC proxmox-backup 05/15] add ApiToken to user.cfg and CachedUserInfo
Fabian Grünbichler
f.gruenbichler at proxmox.com
Mon Oct 19 09:39:09 CEST 2020
Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---
src/bin/proxmox-backup-client.rs | 4 +-
src/bin/proxmox_backup_manager/acl.rs | 2 +-
src/config/cached_user_info.rs | 48 +++++++++++++++----
src/config/user.rs | 67 ++++++++++++++++++++++++---
4 files changed, 102 insertions(+), 19 deletions(-)
diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs
index 1fbbca09..bf336243 100644
--- a/src/bin/proxmox-backup-client.rs
+++ b/src/bin/proxmox-backup-client.rs
@@ -36,7 +36,7 @@ use proxmox_backup::api2::types::*;
use proxmox_backup::api2::version;
use proxmox_backup::client::*;
use proxmox_backup::pxar::catalog::*;
-use proxmox_backup::config::user::complete_user_name;
+use proxmox_backup::config::user::complete_userid;
use proxmox_backup::backup::{
archive_type,
decrypt_key,
@@ -2010,7 +2010,7 @@ fn main() {
let change_owner_cmd_def = CliCommand::new(&API_METHOD_CHANGE_BACKUP_OWNER)
.arg_param(&["group", "new-owner"])
.completion_cb("group", complete_backup_group)
- .completion_cb("new-owner", complete_user_name)
+ .completion_cb("new-owner", complete_userid)
.completion_cb("repository", complete_repository);
let cmd_def = CliCommandMap::new()
diff --git a/src/bin/proxmox_backup_manager/acl.rs b/src/bin/proxmox_backup_manager/acl.rs
index bc2e8f7a..3fbb3bcb 100644
--- a/src/bin/proxmox_backup_manager/acl.rs
+++ b/src/bin/proxmox_backup_manager/acl.rs
@@ -60,7 +60,7 @@ pub fn acl_commands() -> CommandLineInterface {
"update",
CliCommand::new(&api2::access::acl::API_METHOD_UPDATE_ACL)
.arg_param(&["path", "role"])
- .completion_cb("userid", config::user::complete_user_name)
+ .completion_cb("userid", config::user::complete_userid)
.completion_cb("path", config::datastore::complete_acl_path)
);
diff --git a/src/config/cached_user_info.rs b/src/config/cached_user_info.rs
index cf9c534d..93f360c8 100644
--- a/src/config/cached_user_info.rs
+++ b/src/config/cached_user_info.rs
@@ -9,10 +9,10 @@ use lazy_static::lazy_static;
use proxmox::api::UserInformation;
use super::acl::{AclTree, ROLE_NAMES, ROLE_ADMIN};
-use super::user::User;
+use super::user::{ApiToken, User};
use crate::api2::types::Userid;
-/// Cache User/Group/Acl configuration data for fast permission tests
+/// Cache User/Group/Token/Acl configuration data for fast permission tests
pub struct CachedUserInfo {
user_cfg: Arc<SectionConfigData>,
acl_tree: Arc<AclTree>,
@@ -57,20 +57,44 @@ impl CachedUserInfo {
Ok(config)
}
- /// Test if a user account is enabled and not expired
+ /// Test if a userid is enabled and not expired
pub fn is_active_user(&self, userid: &Userid) -> bool {
- if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
- if !info.enable.unwrap_or(true) {
+ if userid.is_tokenid() {
+ if let Ok(owner) = userid.owner() {
+ if !self.is_active_user(&owner) {
+ return false;
+ }
+ } else {
return false;
}
- if let Some(expire) = info.expire {
- if expire > 0 && expire <= now() {
+
+ if let Ok(info) = self.user_cfg.lookup::<ApiToken>("token", userid.as_str()) {
+ if !info.enable.unwrap_or(true) {
return false;
}
+ if let Some(expire) = info.expire {
+ if expire > 0 && expire <= now() {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
}
- return true;
} else {
- return false;
+ if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
+ if !info.enable.unwrap_or(true) {
+ return false;
+ }
+ if let Some(expire) = info.expire {
+ if expire > 0 && expire <= now() {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
}
}
@@ -116,6 +140,12 @@ impl CachedUserInfo {
privs |= role_privs;
}
}
+
+ if userid.is_tokenid() {
+ // limit privs to that of owning user
+ privs &= self.lookup_privs(&userid.owner().unwrap(), path);
+ }
+
privs
}
}
diff --git a/src/config/user.rs b/src/config/user.rs
index efb346d8..5ebb9f99 100644
--- a/src/config/user.rs
+++ b/src/config/user.rs
@@ -52,6 +52,36 @@ pub const EMAIL_SCHEMA: Schema = StringSchema::new("E-Mail Address.")
.max_length(64)
.schema();
+#[api(
+ properties: {
+ tokenid: {
+ schema: PROXMOX_TOKEN_ID_SCHEMA,
+ },
+ comment: {
+ optional: true,
+ schema: SINGLE_LINE_COMMENT_SCHEMA,
+ },
+ enable: {
+ optional: true,
+ schema: ENABLE_USER_SCHEMA,
+ },
+ expire: {
+ optional: true,
+ schema: EXPIRE_USER_SCHEMA,
+ },
+ }
+)]
+#[derive(Serialize,Deserialize)]
+/// ApiToken properties.
+pub struct ApiToken {
+ pub tokenid: 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>,
+}
#[api(
properties: {
@@ -103,15 +133,21 @@ pub struct User {
}
fn init() -> SectionConfig {
- let obj_schema = match User::API_SCHEMA {
- Schema::Object(ref obj_schema) => obj_schema,
+ let mut config = SectionConfig::new(&PROXMOX_USER_OR_TOKEN_ID_SCHEMA);
+
+ let user_schema = match User::API_SCHEMA {
+ Schema::Object(ref user_schema) => user_schema,
_ => unreachable!(),
};
+ let user_plugin = SectionConfigPlugin::new("user".to_string(), Some("userid".to_string()), user_schema);
+ config.register_plugin(user_plugin);
- let plugin = SectionConfigPlugin::new("user".to_string(), Some("userid".to_string()), obj_schema);
- let mut config = SectionConfig::new(&PROXMOX_USER_ID_SCHEMA);
-
- config.register_plugin(plugin);
+ let token_schema = match ApiToken::API_SCHEMA {
+ Schema::Object(ref token_schema) => token_schema,
+ _ => unreachable!(),
+ };
+ let token_plugin = SectionConfigPlugin::new("token".to_string(), Some("tokenid".to_string()), token_schema);
+ config.register_plugin(token_plugin);
config
}
@@ -208,7 +244,24 @@ pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
// shell completion helper
pub fn complete_user_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
match config() {
- Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
+ Ok((data, _digest)) => {
+ data.sections.iter()
+ .filter_map(|(id, (section_type, _))| {
+ if section_type == "user" {
+ Some(id.to_string())
+ } else {
+ None
+ }
+ }).collect()
+ },
Err(_) => return vec![],
}
}
+
+// shell completion helper
+pub fn complete_userid(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
+ match config() {
+ Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
+ Err(_) => vec![],
+ }
+}
--
2.20.1
More information about the pbs-devel
mailing list