Add support for the Personal Ownership policy
Upstream refs: * https://github.com/bitwarden/server/pull/1013 * https://bitwarden.com/help/article/policies/#personal-ownership
This commit is contained in:
parent
85adcf1ae5
commit
9f86196a9d
|
@ -225,6 +225,11 @@ fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn:
|
||||||
fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
|
||||||
let mut data: ShareCipherData = data.into_inner().data;
|
let mut data: ShareCipherData = data.into_inner().data;
|
||||||
|
|
||||||
|
// This check is usually only needed in update_cipher_from_data(), but we
|
||||||
|
// need it here as well to avoid creating an empty cipher in the call to
|
||||||
|
// cipher.save() below.
|
||||||
|
enforce_personal_ownership_policy(&data.Cipher, &headers, &conn)?;
|
||||||
|
|
||||||
let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
|
let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
|
||||||
cipher.user_uuid = Some(headers.user.uuid.clone());
|
cipher.user_uuid = Some(headers.user.uuid.clone());
|
||||||
cipher.save(&conn)?;
|
cipher.save(&conn)?;
|
||||||
|
@ -251,6 +256,38 @@ fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt
|
||||||
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enforces the personal ownership policy on user-owned ciphers, if applicable.
|
||||||
|
/// A non-owner/admin user belonging to an org with the personal ownership policy
|
||||||
|
/// enabled isn't allowed to create new user-owned ciphers or modify existing ones
|
||||||
|
/// (that were created before the policy was applicable to the user). The user is
|
||||||
|
/// allowed to delete or share such ciphers to an org, however.
|
||||||
|
///
|
||||||
|
/// Ref: https://bitwarden.com/help/article/policies/#personal-ownership
|
||||||
|
fn enforce_personal_ownership_policy(
|
||||||
|
data: &CipherData,
|
||||||
|
headers: &Headers,
|
||||||
|
conn: &DbConn
|
||||||
|
) -> EmptyResult {
|
||||||
|
if data.OrganizationId.is_none() {
|
||||||
|
let user_uuid = &headers.user.uuid;
|
||||||
|
for policy in OrgPolicy::find_by_user(user_uuid, conn) {
|
||||||
|
if policy.enabled && policy.has_type(OrgPolicyType::PersonalOwnership) {
|
||||||
|
let org_uuid = &policy.org_uuid;
|
||||||
|
match UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn) {
|
||||||
|
Some(user) =>
|
||||||
|
if user.atype < UserOrgType::Admin &&
|
||||||
|
user.has_status(UserOrgStatus::Confirmed) {
|
||||||
|
err!("Due to an Enterprise Policy, you are restricted \
|
||||||
|
from saving items to your personal vault.")
|
||||||
|
},
|
||||||
|
None => err!("Error looking up user type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_cipher_from_data(
|
pub fn update_cipher_from_data(
|
||||||
cipher: &mut Cipher,
|
cipher: &mut Cipher,
|
||||||
data: CipherData,
|
data: CipherData,
|
||||||
|
@ -260,6 +297,8 @@ pub fn update_cipher_from_data(
|
||||||
nt: &Notify,
|
nt: &Notify,
|
||||||
ut: UpdateType,
|
ut: UpdateType,
|
||||||
) -> EmptyResult {
|
) -> EmptyResult {
|
||||||
|
enforce_personal_ownership_policy(&data, headers, conn)?;
|
||||||
|
|
||||||
// Check that the client isn't updating an existing cipher with stale data.
|
// Check that the client isn't updating an existing cipher with stale data.
|
||||||
if let Some(dt) = data.LastKnownRevisionDate {
|
if let Some(dt) = data.LastKnownRevisionDate {
|
||||||
match NaiveDateTime::parse_from_str(&dt, "%+") { // ISO 8601 format
|
match NaiveDateTime::parse_from_str(&dt, "%+") { // ISO 8601 format
|
||||||
|
|
|
@ -966,7 +966,7 @@ fn list_policies_token(org_id: String, token: String, conn: DbConn) -> JsonResul
|
||||||
fn get_policy(org_id: String, pol_type: i32, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
|
fn get_policy(org_id: String, pol_type: i32, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
|
||||||
let pol_type_enum = match OrgPolicyType::from_i32(pol_type) {
|
let pol_type_enum = match OrgPolicyType::from_i32(pol_type) {
|
||||||
Some(pt) => pt,
|
Some(pt) => pt,
|
||||||
None => err!("Invalid policy type"),
|
None => err!("Invalid or unsupported policy type"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type, &conn) {
|
let policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type, &conn) {
|
||||||
|
@ -1056,4 +1056,4 @@ fn get_plans(_headers: Headers, _conn: DbConn) -> JsonResult {
|
||||||
],
|
],
|
||||||
"ContinuationToken": null
|
"ContinuationToken": null
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@ pub enum OrgPolicyType {
|
||||||
TwoFactorAuthentication = 0,
|
TwoFactorAuthentication = 0,
|
||||||
MasterPassword = 1,
|
MasterPassword = 1,
|
||||||
PasswordGenerator = 2,
|
PasswordGenerator = 2,
|
||||||
|
// SingleOrg = 3, // Not currently supported.
|
||||||
|
// RequireSso = 4, // Not currently supported.
|
||||||
|
PersonalOwnership = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Local methods
|
/// Local methods
|
||||||
|
@ -40,6 +43,10 @@ impl OrgPolicy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_type(&self, policy_type: OrgPolicyType) -> bool {
|
||||||
|
self.atype == policy_type as i32
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_json(&self) -> Value {
|
pub fn to_json(&self) -> Value {
|
||||||
let data_json: Value = serde_json::from_str(&self.data).unwrap_or(Value::Null);
|
let data_json: Value = serde_json::from_str(&self.data).unwrap_or(Value::Null);
|
||||||
json!({
|
json!({
|
||||||
|
|
|
@ -412,11 +412,15 @@ impl UserOrganization {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_status(self, status: UserOrgStatus) -> bool {
|
pub fn has_status(&self, status: UserOrgStatus) -> bool {
|
||||||
self.status == status as i32
|
self.status == status as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_full_access(self) -> bool {
|
pub fn has_type(&self, user_type: UserOrgType) -> bool {
|
||||||
|
self.atype == user_type as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_full_access(&self) -> bool {
|
||||||
(self.access_all || self.atype >= UserOrgType::Admin) &&
|
(self.access_all || self.atype >= UserOrgType::Admin) &&
|
||||||
self.has_status(UserOrgStatus::Confirmed)
|
self.has_status(UserOrgStatus::Confirmed)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue