diff options
author | BlackDex <[email protected]> | 2020-12-02 22:50:51 +0100 |
---|---|---|
committer | BlackDex <[email protected]> | 2020-12-02 22:50:51 +0100 |
commit | 7cf8809d777cd88ad5aa932324e51561724e3c32 (patch) | |
tree | b91ee144b03d922ec338894ffc4a3181dfa0e97f /src/auth.rs | |
parent | 9824d94a1cc039d1e0ac62301abf0b6fbb148709 (diff) | |
download | vaultwarden-7cf8809d777cd88ad5aa932324e51561724e3c32.tar.gz vaultwarden-7cf8809d777cd88ad5aa932324e51561724e3c32.zip |
Adding Manager Role support
This has been requested a few times (#1136 & #246 & forum), and there already were two
(1:1 duplicate) PR's (#1222 & #1223) which needed some changes and no
followups or further comments unfortunally.
This PR adds two auth headers.
- ManagerHeaders
Checks if the user-type is Manager or higher and if the manager is
part of that collection or not.
- ManagerHeadersLoose
Check if the user-type is Manager or higher, but does not check if the
user is part of the collection, needed for a few features like
retreiving all the users of an org.
I think this is the safest way to implement this instead of having to
check this within every function which needs this manually.
Also some extra checks if a manager has access to all collections or
just a selection.
fixes #1136
Diffstat (limited to 'src/auth.rs')
-rw-r--r-- | src/auth.rs | 131 |
1 files changed, 130 insertions, 1 deletions
diff --git a/src/auth.rs b/src/auth.rs index da6f8fa4..53a25357 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -220,7 +220,7 @@ use rocket::{ }; use crate::db::{ - models::{Device, User, UserOrgStatus, UserOrgType, UserOrganization}, + models::{Device, User, UserOrgStatus, UserOrgType, UserOrganization, CollectionUser}, DbConn, }; @@ -310,6 +310,8 @@ pub struct OrgHeaders { pub device: Device, pub user: User, pub org_user_type: UserOrgType, + pub org_user: UserOrganization, + pub org_id: String, } // org_id is usually the second param ("/organizations/<org_id>") @@ -370,6 +372,8 @@ impl<'a, 'r> FromRequest<'a, 'r> for OrgHeaders { err_handler!("Unknown user type in the database") } }, + org_user, + org_id, }) } _ => err_handler!("Error getting the organization id"), @@ -419,6 +423,131 @@ impl Into<Headers> for AdminHeaders { } } + + + + +// col_id is usually the forth param ("/organizations/<org_id>/collections/<col_id>") +// But there cloud be cases where it is located in a query value. +// First check the param, if this is not a valid uuid, we will try the query value. +fn get_col_id(request: &Request) -> Option<String> { + if let Some(Ok(col_id)) = request.get_param::<String>(3) { + if uuid::Uuid::parse_str(&col_id).is_ok() { + return Some(col_id); + } + } + + if let Some(Ok(col_id)) = request.get_query_value::<String>("collectionId") { + if uuid::Uuid::parse_str(&col_id).is_ok() { + return Some(col_id); + } + } + + None +} + +/// The ManagerHeaders are used to check if you are at least a Manager +/// and have access to the specific collection provided via the <col_id>/collections/collectionId. +/// This does strict checking on the collection_id, ManagerHeadersLoose does not. +pub struct ManagerHeaders { + pub host: String, + pub device: Device, + pub user: User, + pub org_user_type: UserOrgType, +} + +impl<'a, 'r> FromRequest<'a, 'r> for ManagerHeaders { + type Error = &'static str; + + fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> { + match request.guard::<OrgHeaders>() { + Outcome::Forward(_) => Outcome::Forward(()), + Outcome::Failure(f) => Outcome::Failure(f), + Outcome::Success(headers) => { + if headers.org_user_type >= UserOrgType::Manager { + match get_col_id(request) { + Some(col_id) => { + let conn = match request.guard::<DbConn>() { + Outcome::Success(conn) => conn, + _ => err_handler!("Error getting DB"), + }; + + if !headers.org_user.access_all { + match CollectionUser::find_by_collection_and_user(&col_id, &headers.org_user.user_uuid, &conn) { + Some(_) => (), + None => err_handler!("The current user isn't a manager for this collection"), + } + } + }, + _ => err_handler!("Error getting the collection id"), + } + + Outcome::Success(Self { + host: headers.host, + device: headers.device, + user: headers.user, + org_user_type: headers.org_user_type, + }) + } else { + err_handler!("You need to be a Manager, Admin or Owner to call this endpoint") + } + } + } + } +} + +impl Into<Headers> for ManagerHeaders { + fn into(self) -> Headers { + Headers { + host: self.host, + device: self.device, + user: self.user, + } + } +} + +/// The ManagerHeadersLoose is used when you at least need to be a Manager, +/// but there is no collection_id sent with the request (either in the path or as form data). +pub struct ManagerHeadersLoose { + pub host: String, + pub device: Device, + pub user: User, + pub org_user_type: UserOrgType, +} + +impl<'a, 'r> FromRequest<'a, 'r> for ManagerHeadersLoose { + type Error = &'static str; + + fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> { + match request.guard::<OrgHeaders>() { + Outcome::Forward(_) => Outcome::Forward(()), + Outcome::Failure(f) => Outcome::Failure(f), + Outcome::Success(headers) => { + if headers.org_user_type >= UserOrgType::Manager { + Outcome::Success(Self { + host: headers.host, + device: headers.device, + user: headers.user, + org_user_type: headers.org_user_type, + }) + } else { + err_handler!("You need to be a Manager, Admin or Owner to call this endpoint") + } + } + } + } +} + +impl Into<Headers> for ManagerHeadersLoose { + fn into(self) -> Headers { + Headers { + host: self.host, + device: self.device, + user: self.user, + } + } +} + pub struct OwnerHeaders { pub host: String, pub device: Device, |