mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-31 10:18:19 +02:00 
			
		
		
		
	Initial version of policies
This commit is contained in:
		| @@ -79,6 +79,9 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     let collections = Collection::find_by_user_uuid(&headers.user.uuid, &conn); | ||||
|     let collections_json: Vec<Value> = collections.iter().map(Collection::to_json).collect(); | ||||
|  | ||||
|     let policies = OrgPolicy::find_by_user(&headers.user.uuid, &conn); | ||||
|     let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect(); | ||||
|  | ||||
|     let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn); | ||||
|     let ciphers_json: Vec<Value> = ciphers | ||||
|         .iter() | ||||
| @@ -95,6 +98,7 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|         "Profile": user_json, | ||||
|         "Folders": folders_json, | ||||
|         "Collections": collections_json, | ||||
|         "Policies": policies_json, | ||||
|         "Ciphers": ciphers_json, | ||||
|         "Domains": domains_json, | ||||
|         "Object": "sync" | ||||
| @@ -648,7 +652,7 @@ fn post_attachment( | ||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) { | ||||
|         err_discard!("Cipher is not write accessible", data) | ||||
|     } | ||||
|      | ||||
|  | ||||
|     let mut params = content_type.params(); | ||||
|     let boundary_pair = params.next().expect("No boundary provided"); | ||||
|     let boundary = boundary_pair.1; | ||||
|   | ||||
| @@ -2,6 +2,7 @@ use rocket::request::Form; | ||||
| use rocket::Route; | ||||
| use rocket_contrib::json::Json; | ||||
| use serde_json::Value; | ||||
| use num_traits::FromPrimitive; | ||||
|  | ||||
| use crate::api::{ | ||||
|     EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, Notify, NumberOrString, PasswordData, UpdateType, | ||||
| @@ -45,6 +46,9 @@ pub fn routes() -> Vec<Route> { | ||||
|         delete_user, | ||||
|         post_delete_user, | ||||
|         post_org_import, | ||||
|         list_policies, | ||||
|         get_policy, | ||||
|         put_policy, | ||||
|     ] | ||||
| } | ||||
|  | ||||
| @@ -830,22 +834,13 @@ struct RelationsData { | ||||
| fn post_org_import( | ||||
|     query: Form<OrgIdData>, | ||||
|     data: JsonUpcase<ImportData>, | ||||
|     headers: Headers, | ||||
|     headers: AdminHeaders, | ||||
|     conn: DbConn, | ||||
|     nt: Notify, | ||||
| ) -> EmptyResult { | ||||
|     let data: ImportData = data.into_inner().data; | ||||
|     let org_id = query.into_inner().organization_id; | ||||
|  | ||||
|     let org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) { | ||||
|         Some(user) => user, | ||||
|         None => err!("User is not part of the organization"), | ||||
|     }; | ||||
|  | ||||
|     if org_user.atype < UserOrgType::Admin { | ||||
|         err!("Only admins or owners can import into an organization") | ||||
|     } | ||||
|  | ||||
|     // Read and create the collections | ||||
|     let collections: Vec<_> = data | ||||
|         .Collections | ||||
| @@ -866,6 +861,8 @@ fn post_org_import( | ||||
|         relations.push((relation.Key, relation.Value)); | ||||
|     } | ||||
|  | ||||
|     let headers: Headers = headers.into(); | ||||
|  | ||||
|     // Read and create the ciphers | ||||
|     let ciphers: Vec<_> = data | ||||
|         .Ciphers | ||||
| @@ -901,3 +898,59 @@ fn post_org_import( | ||||
|     let mut user = headers.user; | ||||
|     user.update_revision(&conn) | ||||
| } | ||||
|  | ||||
| #[get("/organizations/<org_id>/policies")] | ||||
| fn list_policies(org_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult { | ||||
|     let policies = OrgPolicy::find_by_org(&org_id, &conn); | ||||
|     let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect(); | ||||
|  | ||||
|     Ok(Json(json!({ | ||||
|         "Data": policies_json, | ||||
|         "Object": "list", | ||||
|         "ContinuationToken": null | ||||
|     }))) | ||||
| } | ||||
|  | ||||
| #[get("/organizations/<org_id>/policies/<pol_type>")] | ||||
| fn get_policy(org_id: String, pol_type: i32, _headers: AdminHeaders, conn: DbConn) -> JsonResult { | ||||
|     let pol_type_enum = match OrgPolicyType::from_i32(pol_type) { | ||||
|         Some(pt) => pt, | ||||
|         None => err!("Invalid policy type"), | ||||
|     }; | ||||
|  | ||||
|     let policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type, &conn) { | ||||
|         Some(p) => p, | ||||
|         None => OrgPolicy::new(org_id, pol_type_enum, "{}".to_string()), | ||||
|     }; | ||||
|  | ||||
|     Ok(Json(policy.to_json())) | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize)] | ||||
| struct PolicyData { | ||||
|     enabled: bool, | ||||
|     #[serde(rename = "type")] | ||||
|     _type: i32, | ||||
|     data: Value, | ||||
| } | ||||
|  | ||||
| #[put("/organizations/<org_id>/policies/<pol_type>", data = "<data>")] | ||||
| fn put_policy(org_id: String, pol_type: i32, data: Json<PolicyData>, _headers: AdminHeaders, conn: DbConn) -> JsonResult { | ||||
|     let data: PolicyData = data.into_inner(); | ||||
|  | ||||
|     let pol_type_enum = match OrgPolicyType::from_i32(pol_type) { | ||||
|         Some(pt) => pt, | ||||
|         None => err!("Invalid policy type"), | ||||
|     }; | ||||
|  | ||||
|     let mut policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type, &conn) { | ||||
|         Some(p) => p, | ||||
|         None => OrgPolicy::new(org_id, pol_type_enum, "{}".to_string()), | ||||
|     }; | ||||
|  | ||||
|     policy.enabled = data.enabled; | ||||
|     policy.data = serde_json::to_string(&data.data)?; | ||||
|     policy.save(&conn)?; | ||||
|  | ||||
|     Ok(Json(policy.to_json())) | ||||
| } | ||||
							
								
								
									
										11
									
								
								src/auth.rs
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/auth.rs
									
									
									
									
									
								
							| @@ -4,6 +4,7 @@ | ||||
| use crate::util::read_file; | ||||
| use chrono::{Duration, Utc}; | ||||
| use once_cell::sync::Lazy; | ||||
| use num_traits::FromPrimitive; | ||||
|  | ||||
| use jsonwebtoken::{self, Algorithm, Header}; | ||||
| use serde::de::DeserializeOwned; | ||||
| @@ -385,6 +386,16 @@ impl<'a, 'r> FromRequest<'a, 'r> for AdminHeaders { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Into<Headers> for AdminHeaders {     | ||||
|     fn into(self) -> Headers {  | ||||
|         Headers { | ||||
|             host: self.host, | ||||
|             device: self.device, | ||||
|             user: self.user | ||||
|         } | ||||
|      } | ||||
| } | ||||
|  | ||||
| pub struct OwnerHeaders { | ||||
|     pub host: String, | ||||
|     pub device: Device, | ||||
|   | ||||
| @@ -7,6 +7,7 @@ mod user; | ||||
| mod collection; | ||||
| mod organization; | ||||
| mod two_factor; | ||||
| mod org_policy; | ||||
|  | ||||
| pub use self::attachment::Attachment; | ||||
| pub use self::cipher::Cipher; | ||||
| @@ -17,3 +18,4 @@ pub use self::organization::Organization; | ||||
| pub use self::organization::{UserOrgStatus, UserOrgType, UserOrganization}; | ||||
| pub use self::two_factor::{TwoFactor, TwoFactorType}; | ||||
| pub use self::user::{Invitation, User}; | ||||
| pub use self::org_policy::{OrgPolicy, OrgPolicyType}; | ||||
							
								
								
									
										142
									
								
								src/db/models/org_policy.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/db/models/org_policy.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| use diesel; | ||||
| use diesel::prelude::*; | ||||
| use serde_json::Value; | ||||
|  | ||||
| use crate::api::EmptyResult; | ||||
| use crate::db::schema::org_policies; | ||||
| use crate::db::DbConn; | ||||
| use crate::error::MapResult; | ||||
|  | ||||
| use super::Organization; | ||||
|  | ||||
| #[derive(Debug, Identifiable, Queryable, Insertable, Associations, AsChangeset)] | ||||
| #[table_name = "org_policies"] | ||||
| #[belongs_to(Organization, foreign_key = "org_uuid")] | ||||
| #[primary_key(uuid)] | ||||
| pub struct OrgPolicy { | ||||
|     pub uuid: String, | ||||
|     pub org_uuid: String, | ||||
|     pub atype: i32, | ||||
|     pub enabled: bool, | ||||
|     pub data: String, | ||||
| } | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[derive(FromPrimitive)] | ||||
| pub enum OrgPolicyType { | ||||
|     TwoFactorAuthentication = 0, | ||||
|     MasterPassword = 1, | ||||
|     PasswordGenerator = 2, | ||||
| } | ||||
|  | ||||
| /// Local methods | ||||
| impl OrgPolicy { | ||||
|     pub fn new(org_uuid: String, atype: OrgPolicyType, data: String) -> Self { | ||||
|         Self { | ||||
|             uuid: crate::util::get_uuid(), | ||||
|             org_uuid, | ||||
|             atype: atype as i32, | ||||
|             enabled: false, | ||||
|             data, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn to_json(&self) -> Value { | ||||
|         let data_json: Value = serde_json::from_str(&self.data).unwrap_or(Value::Null); | ||||
|         json!({ | ||||
|             "Id": self.uuid, | ||||
|             "OrganizationId": self.org_uuid, | ||||
|             "Type": self.atype, | ||||
|             "Data": data_json, | ||||
|             "Enabled": self.enabled, | ||||
|             "Object": "policy", | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Database methods | ||||
| impl OrgPolicy { | ||||
|     #[cfg(feature = "postgresql")] | ||||
|     pub fn save(&mut self, conn: &DbConn) -> EmptyResult { | ||||
|         // We need to make sure we're not going to violate the unique constraint on org_uuid and atype. | ||||
|         // This happens automatically on other DBMS backends due to replace_into(). PostgreSQL does | ||||
|         // not support multiple constraints on ON CONFLICT clauses. | ||||
|         diesel::delete( | ||||
|             org_policies::table | ||||
|                 .filter(org_policies::org_uuid.eq(&self.org_uuid)) | ||||
|                 .filter(org_policies::atype.eq(&self.atype)), | ||||
|         ) | ||||
|         .execute(&**conn) | ||||
|         .map_res("Error deleting org_policy for insert")?; | ||||
|  | ||||
|         diesel::insert_into(org_policies::table) | ||||
|             .values(self) | ||||
|             .on_conflict(org_policies::uuid) | ||||
|             .do_update() | ||||
|             .set(self) | ||||
|             .execute(&**conn) | ||||
|             .map_res("Error saving org_policy") | ||||
|     } | ||||
|  | ||||
|     #[cfg(not(feature = "postgresql"))] | ||||
|     pub fn save(&mut self, conn: &DbConn) -> EmptyResult { | ||||
|         diesel::replace_into(org_policies::table) | ||||
|             .values(&*self) | ||||
|             .execute(&**conn) | ||||
|             .map_res("Error saving org_policy") | ||||
|     } | ||||
|  | ||||
|     pub fn delete(self, conn: &DbConn) -> EmptyResult { | ||||
|         diesel::delete(org_policies::table.filter(org_policies::uuid.eq(self.uuid))) | ||||
|             .execute(&**conn) | ||||
|             .map_res("Error deleting org_policy") | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> { | ||||
|         org_policies::table | ||||
|             .filter(org_policies::uuid.eq(uuid)) | ||||
|             .first::<Self>(&**conn) | ||||
|             .ok() | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> { | ||||
|         org_policies::table | ||||
|             .filter(org_policies::org_uuid.eq(org_uuid)) | ||||
|             .load::<Self>(&**conn) | ||||
|             .expect("Error loading org_policy") | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { | ||||
|         use crate::db::schema::users_organizations; | ||||
|  | ||||
|         org_policies::table | ||||
|             .left_join( | ||||
|                 users_organizations::table.on( | ||||
|                     users_organizations::org_uuid.eq(org_policies::org_uuid) | ||||
|                         .and(users_organizations::user_uuid.eq(user_uuid))) | ||||
|             ) | ||||
|             .select(org_policies::all_columns) | ||||
|             .load::<Self>(&**conn) | ||||
|             .expect("Error loading org_policy") | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_org_and_type(org_uuid: &str, atype: i32, conn: &DbConn) -> Option<Self> { | ||||
|         org_policies::table | ||||
|             .filter(org_policies::org_uuid.eq(org_uuid)) | ||||
|             .filter(org_policies::atype.eq(atype)) | ||||
|             .first::<Self>(&**conn) | ||||
|             .ok() | ||||
|     } | ||||
|  | ||||
|     pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { | ||||
|         diesel::delete(org_policies::table.filter(org_policies::org_uuid.eq(org_uuid))) | ||||
|             .execute(&**conn) | ||||
|             .map_res("Error deleting org_policy") | ||||
|     } | ||||
|  | ||||
|     /*pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { | ||||
|         diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(user_uuid))) | ||||
|             .execute(&**conn) | ||||
|             .map_res("Error deleting twofactors") | ||||
|     }*/ | ||||
| } | ||||
| @@ -1,7 +1,8 @@ | ||||
| use serde_json::Value; | ||||
| use std::cmp::Ordering; | ||||
| use num_traits::FromPrimitive; | ||||
|  | ||||
| use super::{CollectionUser, User}; | ||||
| use super::{CollectionUser, User, OrgPolicy}; | ||||
|  | ||||
| #[derive(Debug, Identifiable, Queryable, Insertable, AsChangeset)] | ||||
| #[table_name = "organizations"] | ||||
| @@ -33,6 +34,7 @@ pub enum UserOrgStatus { | ||||
| } | ||||
|  | ||||
| #[derive(Copy, Clone, PartialEq, Eq)] | ||||
| #[derive(FromPrimitive)] | ||||
| pub enum UserOrgType { | ||||
|     Owner = 0, | ||||
|     Admin = 1, | ||||
| @@ -135,16 +137,6 @@ impl UserOrgType { | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn from_i32(i: i32) -> Option<Self> { | ||||
|         match i { | ||||
|             0 => Some(UserOrgType::Owner), | ||||
|             1 => Some(UserOrgType::Admin), | ||||
|             2 => Some(UserOrgType::User), | ||||
|             3 => Some(UserOrgType::Manager), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Local methods | ||||
| @@ -170,6 +162,7 @@ impl Organization { | ||||
|             "UseEvents": false, | ||||
|             "UseGroups": false, | ||||
|             "UseTotp": true, | ||||
|             "UsePolicies": true, | ||||
|  | ||||
|             "BusinessName": null, | ||||
|             "BusinessAddress1":	null, | ||||
| @@ -250,6 +243,7 @@ impl Organization { | ||||
|         Cipher::delete_all_by_organization(&self.uuid, &conn)?; | ||||
|         Collection::delete_all_by_organization(&self.uuid, &conn)?; | ||||
|         UserOrganization::delete_all_by_organization(&self.uuid, &conn)?; | ||||
|         OrgPolicy::delete_all_by_organization(&self.uuid, &conn)?; | ||||
|  | ||||
|         diesel::delete(organizations::table.filter(organizations::uuid.eq(self.uuid))) | ||||
|             .execute(&**conn) | ||||
| @@ -267,7 +261,7 @@ impl Organization { | ||||
| impl UserOrganization { | ||||
|     pub fn to_json(&self, conn: &DbConn) -> Value { | ||||
|         let org = Organization::find_by_uuid(&self.org_uuid, conn).unwrap(); | ||||
|  | ||||
|          | ||||
|         json!({ | ||||
|             "Id": self.org_uuid, | ||||
|             "Name": org.name, | ||||
| @@ -280,6 +274,7 @@ impl UserOrganization { | ||||
|             "UseEvents": false, | ||||
|             "UseGroups": false, | ||||
|             "UseTotp": true, | ||||
|             "UsePolicies": true, | ||||
|  | ||||
|             "MaxStorageGb": 10, // The value doesn't matter, we don't check server-side | ||||
|  | ||||
|   | ||||
| @@ -77,6 +77,16 @@ table! { | ||||
|     } | ||||
| } | ||||
|  | ||||
| table! { | ||||
|     org_policies (uuid) { | ||||
|         uuid -> Varchar, | ||||
|         org_uuid -> Varchar, | ||||
|         atype -> Integer, | ||||
|         enabled -> Bool, | ||||
|         data -> Text, | ||||
|     } | ||||
| } | ||||
|  | ||||
| table! { | ||||
|     organizations (uuid) { | ||||
|         uuid -> Varchar, | ||||
| @@ -155,6 +165,7 @@ joinable!(devices -> users (user_uuid)); | ||||
| joinable!(folders -> users (user_uuid)); | ||||
| joinable!(folders_ciphers -> ciphers (cipher_uuid)); | ||||
| joinable!(folders_ciphers -> folders (folder_uuid)); | ||||
| joinable!(org_policies -> organizations (org_uuid)); | ||||
| joinable!(twofactor -> users (user_uuid)); | ||||
| joinable!(users_collections -> collections (collection_uuid)); | ||||
| joinable!(users_collections -> users (user_uuid)); | ||||
| @@ -170,6 +181,7 @@ allow_tables_to_appear_in_same_query!( | ||||
|     folders, | ||||
|     folders_ciphers, | ||||
|     invitations, | ||||
|     org_policies, | ||||
|     organizations, | ||||
|     twofactor, | ||||
|     users, | ||||
|   | ||||
| @@ -77,6 +77,16 @@ table! { | ||||
|     } | ||||
| } | ||||
|  | ||||
| table! { | ||||
|     org_policies (uuid) { | ||||
|         uuid -> Text, | ||||
|         org_uuid -> Text, | ||||
|         atype -> Integer, | ||||
|         enabled -> Bool, | ||||
|         data -> Text, | ||||
|     } | ||||
| } | ||||
|  | ||||
| table! { | ||||
|     organizations (uuid) { | ||||
|         uuid -> Text, | ||||
| @@ -155,6 +165,7 @@ joinable!(devices -> users (user_uuid)); | ||||
| joinable!(folders -> users (user_uuid)); | ||||
| joinable!(folders_ciphers -> ciphers (cipher_uuid)); | ||||
| joinable!(folders_ciphers -> folders (folder_uuid)); | ||||
| joinable!(org_policies -> organizations (org_uuid)); | ||||
| joinable!(twofactor -> users (user_uuid)); | ||||
| joinable!(users_collections -> collections (collection_uuid)); | ||||
| joinable!(users_collections -> users (user_uuid)); | ||||
| @@ -170,6 +181,7 @@ allow_tables_to_appear_in_same_query!( | ||||
|     folders, | ||||
|     folders_ciphers, | ||||
|     invitations, | ||||
|     org_policies, | ||||
|     organizations, | ||||
|     twofactor, | ||||
|     users, | ||||
|   | ||||
| @@ -77,6 +77,16 @@ table! { | ||||
|     } | ||||
| } | ||||
|  | ||||
| table! { | ||||
|     org_policies (uuid) { | ||||
|         uuid -> Text, | ||||
|         org_uuid -> Text, | ||||
|         atype -> Integer, | ||||
|         enabled -> Bool, | ||||
|         data -> Text, | ||||
|     } | ||||
| } | ||||
|  | ||||
| table! { | ||||
|     organizations (uuid) { | ||||
|         uuid -> Text, | ||||
| @@ -155,6 +165,7 @@ joinable!(devices -> users (user_uuid)); | ||||
| joinable!(folders -> users (user_uuid)); | ||||
| joinable!(folders_ciphers -> ciphers (cipher_uuid)); | ||||
| joinable!(folders_ciphers -> folders (folder_uuid)); | ||||
| joinable!(org_policies -> organizations (org_uuid)); | ||||
| joinable!(twofactor -> users (user_uuid)); | ||||
| joinable!(users_collections -> collections (collection_uuid)); | ||||
| joinable!(users_collections -> users (user_uuid)); | ||||
| @@ -170,6 +181,7 @@ allow_tables_to_appear_in_same_query!( | ||||
|     folders, | ||||
|     folders_ciphers, | ||||
|     invitations, | ||||
|     org_policies, | ||||
|     organizations, | ||||
|     twofactor, | ||||
|     users, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user