Registry testing - part 4
This commit is contained in:
@@ -437,19 +437,6 @@ pub(crate) async fn account_set(
|
||||
}
|
||||
}
|
||||
|
||||
if credential.secret != old_credential.secret {
|
||||
credential.secret = hash_secret(
|
||||
set.server
|
||||
.core
|
||||
.network
|
||||
.security
|
||||
.password_hash_algorithm,
|
||||
std::mem::take(&mut credential.secret),
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
}
|
||||
|
||||
if credential.otp_auth != old_credential.otp_auth
|
||||
&& !verify_otp_auth(
|
||||
credential.otp_auth.as_deref(),
|
||||
@@ -463,6 +450,30 @@ pub(crate) async fn account_set(
|
||||
);
|
||||
continue 'outer;
|
||||
}
|
||||
|
||||
if credential.secret != old_credential.secret {
|
||||
if let Err(err) =
|
||||
set.server.is_secure_password(&credential.secret, &[])
|
||||
{
|
||||
set.response.not_updated.append(
|
||||
id,
|
||||
SetError::invalid_properties()
|
||||
.with_property(Property::Secret)
|
||||
.with_description(err),
|
||||
);
|
||||
continue 'outer;
|
||||
}
|
||||
credential.secret = hash_secret(
|
||||
set.server
|
||||
.core
|
||||
.network
|
||||
.security
|
||||
.password_hash_algorithm,
|
||||
std::mem::take(&mut credential.secret),
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
}
|
||||
} else {
|
||||
set.response.not_updated.append(
|
||||
id,
|
||||
|
||||
@@ -104,22 +104,20 @@ pub(crate) async fn validate_account(
|
||||
}
|
||||
|
||||
if credential.secret != old_credential.secret {
|
||||
if !credential.secret.is_empty() {
|
||||
credential.secret = hash_secret(
|
||||
set.server
|
||||
.core
|
||||
.network
|
||||
.security
|
||||
.password_hash_algorithm,
|
||||
std::mem::take(&mut credential.secret),
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
} else {
|
||||
if let Err(err) =
|
||||
set.server.is_secure_password(&credential.secret, &[])
|
||||
{
|
||||
return Ok(Err(SetError::invalid_properties()
|
||||
.with_property(Property::Secret)
|
||||
.with_description("Password cannot be empty.")));
|
||||
.with_description(err)));
|
||||
}
|
||||
|
||||
credential.secret = hash_secret(
|
||||
set.server.core.network.security.password_hash_algorithm,
|
||||
std::mem::take(&mut credential.secret),
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
}
|
||||
}
|
||||
(
|
||||
@@ -240,7 +238,11 @@ async fn validate_credential_creation(
|
||||
.with_description("Only one password credential is allowed.")));
|
||||
}
|
||||
|
||||
if credential.secret.is_empty() {
|
||||
if let Err(err) = server.is_secure_password(&credential.secret, &[]) {
|
||||
Ok(Err(SetError::invalid_properties()
|
||||
.with_property(Property::Secret)
|
||||
.with_description(err)))
|
||||
} else {
|
||||
credential.secret = hash_secret(
|
||||
server.core.network.security.password_hash_algorithm,
|
||||
std::mem::take(&mut credential.secret),
|
||||
@@ -248,10 +250,6 @@ async fn validate_credential_creation(
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
Ok(Ok(()))
|
||||
} else {
|
||||
Ok(Err(SetError::invalid_properties()
|
||||
.with_property(Property::Secret)
|
||||
.with_description("Password cannot be empty.")))
|
||||
}
|
||||
}
|
||||
Credential::AppPassword(_) | Credential::ApiKey(_) => {
|
||||
|
||||
@@ -90,8 +90,9 @@ impl RegistrySet for Server {
|
||||
let has_account_id = (object_flags & OBJ_FILTER_ACCOUNT) != 0;
|
||||
let is_tenant_filtered =
|
||||
(object_flags & OBJ_FILTER_TENANT) != 0 && access_token.tenant_id().is_some();
|
||||
let is_account_filtered =
|
||||
has_account_id && !access_token.has_permission(Permission::Impersonate);
|
||||
let can_set_tenant = access_token.tenant_id().is_none();
|
||||
let can_set_account = access_token.has_permission(Permission::Impersonate);
|
||||
let is_account_filtered = has_account_id && !can_set_account;
|
||||
|
||||
// Build response
|
||||
let mut response = SetResponse::from_request(&request, self.core.jmap.set_max_objects)?;
|
||||
@@ -316,77 +317,28 @@ impl RegistrySet for Server {
|
||||
let is_create = matches!(modification, Modification::Create { .. });
|
||||
let mut unpatched_properties = VecMap::new();
|
||||
|
||||
for (key, value) in value.into_expanded_object() {
|
||||
let ptr = match (key, &modification) {
|
||||
(Key::Property(prop), _) => {
|
||||
JsonPointer::new(vec![JsonPointerItem::Key(Key::Property(prop))])
|
||||
}
|
||||
(Key::Borrowed(other), Modification::Update { .. }) => {
|
||||
JsonPointer::parse(other)
|
||||
}
|
||||
(Key::Owned(other), Modification::Update { .. }) => {
|
||||
JsonPointer::parse(&other)
|
||||
}
|
||||
(key, Modification::Create { .. }) => {
|
||||
set.failed(
|
||||
modification,
|
||||
SetError::invalid_properties().with_property(key.into_owned()),
|
||||
);
|
||||
continue 'outer;
|
||||
}
|
||||
};
|
||||
|
||||
if is_tenant_filtered || is_account_filtered {
|
||||
match ptr.last().and_then(|p| p.as_property_key()) {
|
||||
Some(Property::MemberTenantId) => {
|
||||
// SPDX-SnippetBegin
|
||||
// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
||||
// SPDX-License-Identifier: LicenseRef-SEL
|
||||
#[cfg(feature = "enterprise")]
|
||||
if access_token.tenant_id().is_some() {
|
||||
continue;
|
||||
}
|
||||
// SPDX-SnippetEnd
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
continue;
|
||||
}
|
||||
Some(Property::AccountId) => {
|
||||
set.failed(
|
||||
modification,
|
||||
SetError::forbidden()
|
||||
.with_property(Property::AccountId)
|
||||
.with_description("Cannot change server-set property"),
|
||||
);
|
||||
continue 'outer;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if is_create {
|
||||
// Patch object
|
||||
match new_object
|
||||
.patch(JsonPointerPatch::new(&ptr).with_create(is_create), value)
|
||||
{
|
||||
match new_object.patch(
|
||||
JsonPointerPatch::new(&JsonPointer::new(vec![]))
|
||||
.with_create(true)
|
||||
.with_can_set_tenant(can_set_tenant)
|
||||
.with_can_set_account(can_set_account),
|
||||
value,
|
||||
) {
|
||||
Ok(MaybeUnpatched::Patched) => {}
|
||||
Ok(MaybeUnpatched::Unpatched { property, value }) => {
|
||||
unpatched_properties.append(property, value);
|
||||
}
|
||||
Ok(MaybeUnpatched::UnpatchedMany { properties }) => {
|
||||
if unpatched_properties.is_empty() {
|
||||
unpatched_properties = properties;
|
||||
} else {
|
||||
unpatched_properties.extend(properties);
|
||||
}
|
||||
unpatched_properties = properties;
|
||||
}
|
||||
Err(err) => {
|
||||
set.failed(modification, err.into());
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if is_create {
|
||||
// Add tenantId for tenant filtered objects
|
||||
if is_tenant_filtered && let Some(tenant_id) = set.access_token.tenant_id()
|
||||
{
|
||||
@@ -397,6 +349,43 @@ impl RegistrySet for Server {
|
||||
if has_account_id {
|
||||
new_object.inner.set_account_id(set.account_id.into());
|
||||
}
|
||||
} else {
|
||||
for (key, value) in value.into_expanded_object() {
|
||||
let ptr = match key {
|
||||
Key::Property(prop) => {
|
||||
JsonPointer::new(vec![JsonPointerItem::Key(Key::Property(
|
||||
prop,
|
||||
))])
|
||||
}
|
||||
Key::Borrowed(other) => JsonPointer::parse(other),
|
||||
Key::Owned(other) => JsonPointer::parse(&other),
|
||||
};
|
||||
|
||||
// Patch object
|
||||
match new_object.patch(
|
||||
JsonPointerPatch::new(&ptr)
|
||||
.with_create(false)
|
||||
.with_can_set_tenant(can_set_tenant)
|
||||
.with_can_set_account(can_set_account),
|
||||
value,
|
||||
) {
|
||||
Ok(MaybeUnpatched::Patched) => {}
|
||||
Ok(MaybeUnpatched::Unpatched { property, value }) => {
|
||||
unpatched_properties.append(property, value);
|
||||
}
|
||||
Ok(MaybeUnpatched::UnpatchedMany { properties }) => {
|
||||
if unpatched_properties.is_empty() {
|
||||
unpatched_properties = properties;
|
||||
} else {
|
||||
unpatched_properties.extend(properties);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
set.failed(modification, err.into());
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate objects
|
||||
|
||||
Reference in New Issue
Block a user