From 5538dd6751d88647c2b2190f0040b8c707e229d2 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Mon, 21 Mar 2022 00:01:50 +0100 Subject: [PATCH] User admin methods --- bffhd/capnp/machine.rs | 14 ++--- bffhd/capnp/user.rs | 119 ++++++++++++++++++++++++++----------- bffhd/capnp/user_system.rs | 17 ++++-- bffhd/resources/mod.rs | 6 +- bffhd/session/mod.rs | 12 ++-- bffhd/users/mod.rs | 5 ++ 6 files changed, 120 insertions(+), 53 deletions(-) diff --git a/bffhd/capnp/machine.rs b/bffhd/capnp/machine.rs index 35423ee..70ab2d4 100644 --- a/bffhd/capnp/machine.rs +++ b/bffhd/capnp/machine.rs @@ -40,7 +40,7 @@ impl Machine { builder.set_urn(&format!("urn:fabaccess:resource:{}", self.resource.get_id())); { - let user = self.session.get_user(); + let user = self.session.get_user_ref(); let state = self.resource.get_state_ref(); let state = state.as_ref(); @@ -113,7 +113,7 @@ impl UseServer for Machine { let resource = self.resource.clone(); let session = self.session.clone(); Promise::from_future(async move { - let user = session.get_user(); + let user = session.get_user_ref(); resource.try_update(session, Status::InUse(user)).await; Ok(()) }) @@ -127,7 +127,7 @@ impl UseServer for Machine { let resource = self.resource.clone(); let session = self.session.clone(); Promise::from_future(async move { - let user = session.get_user(); + let user = session.get_user_ref(); resource .try_update(session, Status::Reserved(user)) .await; @@ -200,7 +200,7 @@ impl ManageServer for Machine { mut result: manage::GetMachineInfoExtendedResults, ) -> Promise<(), ::capnp::Error> { let mut builder = result.get(); - let user = User::new(self.session.clone()); + let user = User::new_self(self.session.clone()); user.build_else(self.resource.get_current_user(), builder.reborrow().init_current_user()); user.build_else(self.resource.get_previous_user(), builder.init_last_user()); Promise::ok(()) @@ -233,7 +233,7 @@ impl ManageServer for Machine { let session = self.session.clone(); Promise::from_future(async move { resource - .force_set(Status::InUse(session.get_user())) + .force_set(Status::InUse(session.get_user_ref())) .await; Ok(()) }) @@ -270,7 +270,7 @@ impl ManageServer for Machine { let session = self.session.clone(); Promise::from_future(async move { resource - .force_set(Status::Blocked(session.get_user())) + .force_set(Status::Blocked(session.get_user_ref())) .await; Ok(()) }) @@ -295,7 +295,7 @@ impl AdminServer for Machine { _: admin::ForceSetStateResults, ) -> Promise<(), ::capnp::Error> { use api::schema::machine_capnp::machine::MachineState as APIMState; - let user = self.session.get_user(); + let user = self.session.get_user_ref(); let state = match pry!(pry!(params.get()).get_state()) { APIMState::Free => Status::Free, APIMState::Blocked => Status::Blocked(user), diff --git a/bffhd/capnp/user.rs b/bffhd/capnp/user.rs index 4fbcb71..c72c541 100644 --- a/bffhd/capnp/user.rs +++ b/bffhd/capnp/user.rs @@ -1,30 +1,55 @@ +use capnp::capability::Promise; +use capnp_rpc::pry; use crate::session::SessionHandle; use api::user_capnp::user::{admin, info, manage, Builder}; -use crate::users::UserRef; +use crate::authorization::permissions::Permission; +use crate::users::{db, UserRef}; +#[derive(Clone)] pub struct User { session: SessionHandle, + user: UserRef, } impl User { - pub fn new(session: SessionHandle) -> Self { - Self { session } + pub fn new(session: SessionHandle, user: UserRef) -> Self { + Self { session, user } } - pub fn build_else(&self, user: Option, mut builder: Builder) { - if let Some(user) = user { - builder.set_username(user.get_username()); + pub fn new_self(session: SessionHandle) -> Self { + let user = session.get_user_ref(); + Self::new(session, user) + } + + pub fn build_else(&self, user: Option, builder: Builder) { + if let Some(user) = user.and_then(|u| self.session.users.get_user(u.get_username())) { + self.fill(user, builder); } } - pub fn build_into(self, mut builder: Builder) { - let user = self.session.get_user(); - builder.set_username(user.get_username()); + pub fn build(session: SessionHandle, mut builder: Builder) { + let this = Self::new_self(session); + let user = this.session.get_user(); + this.fill(user, builder); } - pub fn build(session: SessionHandle, mut builder: Builder) { - let this = Self::new(session); - this.build_into(builder) + pub fn fill(&self, user: db::User, mut builder: Builder) { + builder.set_username(user.id.as_str()); + + let client = Self::new(self.session.clone(), UserRef::new(user.id.clone())); + + // We have permissions on ourself + let is_me = &self.session.get_user_ref().id == &user.id; + + if is_me || self.session.has_perm(Permission::new("bffh.users.info")) { + builder.set_info(capnp_rpc::new_client(client.clone())); + } + if is_me { + builder.set_manage(capnp_rpc::new_client(client.clone())); + } + if self.session.has_perm(Permission::new("bffh.users.admin")) { + builder.set_admin(capnp_rpc::new_client(client.clone())); + } } } @@ -32,21 +57,25 @@ impl info::Server for User { fn list_roles( &mut self, _: info::ListRolesParams, - _: info::ListRolesResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + mut result: info::ListRolesResults, + ) -> Promise<(), ::capnp::Error> { + let user = self.session.get_user(); + let mut builder = result.get().init_roles(user.userdata.roles.len() as u32); + for (i, role) in user.userdata.roles.into_iter().enumerate() { + let mut b = builder.reborrow().get(i as u32); + b.set_name(role.as_str()); + } + Promise::ok(()) } } impl manage::Server for User { fn pwd( &mut self, - _: manage::PwdParams, - _: manage::PwdResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + params: manage::PwdParams, + mut results: manage::PwdResults, + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } @@ -57,35 +86,55 @@ impl admin::Server for User { &mut self, _: admin::GetUserInfoExtendedParams, _: admin::GetUserInfoExtendedResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } fn add_role( &mut self, - _: admin::AddRoleParams, + param: admin::AddRoleParams, _: admin::AddRoleResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + ) -> Promise<(), ::capnp::Error> { + let rolename = pry!(pry!(pry!(param.get()).get_role()).get_name()); + + if let Some(_role) = self.session.roles.get(rolename) { + let mut target = self.session.users.get_user(self.user.get_username()).unwrap(); + + // Only update if needed + if !target.userdata.roles.iter().any(|r| r.as_str() == rolename) { + target.userdata.roles.push(rolename.to_string()); + self.session.users.put_user(self.user.get_username(), &target); + } + } + + Promise::ok(()) } fn remove_role( &mut self, - _: admin::RemoveRoleParams, + param: admin::RemoveRoleParams, _: admin::RemoveRoleResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + ) -> Promise<(), ::capnp::Error> { + let rolename = pry!(pry!(pry!(param.get()).get_role()).get_name()); + + if let Some(_role) = self.session.roles.get(rolename) { + let mut target = self.session.users.get_user(self.user.get_username()).unwrap(); + + // Only update if needed + if target.userdata.roles.iter().any(|r| r.as_str() == rolename) { + target.userdata.roles.retain(|r| r.as_str() != rolename); + self.session.users.put_user(self.user.get_username(), &target); + } + } + + Promise::ok(()) } fn pwd( &mut self, _: admin::PwdParams, _: admin::PwdResults, - ) -> ::capnp::capability::Promise<(), ::capnp::Error> { - ::capnp::capability::Promise::err(::capnp::Error::unimplemented( + ) -> Promise<(), ::capnp::Error> { + Promise::err(::capnp::Error::unimplemented( "method not implemented".to_string(), )) } diff --git a/bffhd/capnp/user_system.rs b/bffhd/capnp/user_system.rs index 78f05b3..2cfb2d0 100644 --- a/bffhd/capnp/user_system.rs +++ b/bffhd/capnp/user_system.rs @@ -1,4 +1,6 @@ use capnp::capability::Promise; +use capnp_rpc::pry; +use libc::user; use api::usersystem_capnp::user_system::{ info, manage, Server as UserSystem, self as system, @@ -7,6 +9,7 @@ use crate::authorization::permissions::Permission; use crate::capnp::user::User; use crate::session::SessionHandle; +use crate::users::UserRef; #[derive(Clone)] pub struct Users { @@ -56,11 +59,17 @@ impl manage::Server for Users { fn get_user_list( &mut self, _: manage::GetUserListParams, - _: manage::GetUserListResults, + mut result: manage::GetUserListResults, ) -> Promise<(), ::capnp::Error> { - Promise::err(::capnp::Error::unimplemented( - "method not implemented".to_string(), - )) + let userdb = self.session.users.into_inner(); + let users = pry!(userdb.get_all() + .map_err(|e| capnp::Error::failed(format!("UserDB error: {:?}", e)))); + let mut builder = result.get().init_user_list(users.len() as u32); + let me = User::new_self(self.session.clone()); + for (i, (_, user)) in users.into_iter().enumerate() { + me.fill(user, builder.reborrow().get(i as u32)); + } + Promise::ok(()) } fn add_user( &mut self, diff --git a/bffhd/resources/mod.rs b/bffhd/resources/mod.rs index ac5573b..02a9d21 100644 --- a/bffhd/resources/mod.rs +++ b/bffhd/resources/mod.rs @@ -167,7 +167,7 @@ impl Resource { pub async fn try_update(&self, session: SessionHandle, new: Status) { let old = self.get_state(); let old: &Archived = old.as_ref(); - let user = session.get_user(); + let user = session.get_user_ref(); if session.has_manage(self) // Default allow for managers @@ -224,7 +224,7 @@ impl Resource { let s: &Archived = state.as_ref(); let i: &Archived = &s.inner; if let ArchivedStatus::InUse(user) = &i.state { - let current = session.get_user(); + let current = session.get_user_ref(); if user == ¤t { self.set_state(MachineState::free(Some(current))); } @@ -236,7 +236,7 @@ impl Resource { } pub fn visible(&self, session: &SessionHandle) -> bool { - session.has_disclose(self) || self.is_owned_by(session.get_user()) + session.has_disclose(self) || self.is_owned_by(session.get_user_ref()) } pub fn is_owned_by(&self, owner: UserRef) -> bool { diff --git a/bffhd/session/mod.rs b/bffhd/session/mod.rs index 1ff81b4..916debd 100644 --- a/bffhd/session/mod.rs +++ b/bffhd/session/mod.rs @@ -7,7 +7,7 @@ use crate::authorization::permissions::Permission; use crate::authorization::roles::{Roles}; use crate::resources::Resource; use crate::Users; -use crate::users::UserRef; +use crate::users::{db, UserRef}; #[derive(Clone)] @@ -40,17 +40,21 @@ impl SessionManager { #[derive(Clone)] pub struct SessionHandle { - users: Users, - roles: Roles, + pub users: Users, + pub roles: Roles, user: UserRef, } impl SessionHandle { - pub fn get_user(&self) -> UserRef { + pub fn get_user_ref(&self) -> UserRef { self.user.clone() } + pub fn get_user(&self) -> db::User { + self.users.get_user(self.user.get_username()).expect("Failed to get user self") + } + pub fn has_disclose(&self, resource: &Resource) -> bool { if let Some(user) = self.users.get_user(self.user.get_username()) { self.roles.is_permitted(&user.userdata, &resource.get_required_privs().disclose) diff --git a/bffhd/users/mod.rs b/bffhd/users/mod.rs index 18c36a2..d42a8da 100644 --- a/bffhd/users/mod.rs +++ b/bffhd/users/mod.rs @@ -93,6 +93,11 @@ impl Users { .map(|user| Deserialize::::deserialize(user.as_ref(), &mut Infallible).unwrap()) } + pub fn put_user(&self, uid: &str, user: &db::User) -> Result<(), lmdb::Error> { + tracing::trace!(uid, ?user, "Updating user"); + self.userdb.put(uid, user) + } + pub fn load_file>(&self, path: P) -> anyhow::Result<()> { let f = std::fs::read(path)?; let map: HashMap = toml::from_slice(&f)?;