mirror of
https://github.com/mattermost/mattermost.git
synced 2026-10-16 07:09:04 +08:00
webapp: lock user attributes the caller cannot set via permission_values
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+16
@@ -64,6 +64,22 @@ describe('canEditUserAttribute', () => {
|
||||
expect(canEditUserAttribute(field, {canEditAdminManaged: false})).toBe(false);
|
||||
expect(canEditUserAttribute(field, {canEditAdminManaged: true})).toBe(true);
|
||||
});
|
||||
|
||||
test('locks permission_values none for everyone', () => {
|
||||
const field = buildField({permission_values: 'none'});
|
||||
expect(canEditUserAttribute(field, {canEditAdminManaged: true})).toBe(false);
|
||||
expect(canEditUserAttribute(field, {canEditAdminManaged: false})).toBe(false);
|
||||
});
|
||||
|
||||
test.each(['sysadmin', 'admin'] as const)('requires manage_system for permission_values %s', (level) => {
|
||||
const field = buildField({permission_values: level});
|
||||
expect(canEditUserAttribute(field, {canEditAdminManaged: true})).toBe(true);
|
||||
expect(canEditUserAttribute(field, {canEditAdminManaged: false})).toBe(false);
|
||||
});
|
||||
|
||||
test.each(['member', '', undefined] as const)('allows permission_values %p without manage_system', (level) => {
|
||||
expect(canEditUserAttribute(buildField({permission_values: level}), {canEditAdminManaged: false})).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAdminManagedLocked', () => {
|
||||
|
||||
+25
-5
@@ -7,7 +7,7 @@ import type {MessageDescriptor} from 'react-intl';
|
||||
import type {UserPropertyField} from '@mattermost/types/properties_user';
|
||||
|
||||
import {isEmail} from 'mattermost-redux/utils/helpers';
|
||||
import {isPropertyFieldRequired, isPropertyValueSet} from 'mattermost-redux/utils/property_utils';
|
||||
import {isPropertyFieldRequired, isPropertyFieldSourceManaged, isPropertyValueSet} from 'mattermost-redux/utils/property_utils';
|
||||
|
||||
import {validHttpUrl} from 'utils/url';
|
||||
|
||||
@@ -34,11 +34,31 @@ export function isAdminManagedLocked(field: UserPropertyField, {canEditAdminMana
|
||||
return field.attrs?.managed === 'admin' && !canEditAdminManaged;
|
||||
}
|
||||
|
||||
// Synced means any ldap/saml attr regardless of the target's auth_service.
|
||||
export function canEditUserAttribute(field: UserPropertyField, opts: UserAttributeLockOptions): boolean {
|
||||
const attrs = field.attrs;
|
||||
const optionsOmitted = Boolean(attrs?.options_omitted) && field.type !== 'graph';
|
||||
return !(attrs?.protected || attrs?.ldap || attrs?.saml || attrs?.owners?.length || optionsOmitted || isAdminManagedLocked(field, opts));
|
||||
if (isPropertyFieldSourceManaged(field) || isAdminManagedLocked(field, opts)) {
|
||||
return false;
|
||||
}
|
||||
if (field.attrs?.options_omitted && field.type !== 'graph') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The server rejects the whole save if any field fails this tier. User fields
|
||||
// target the system, so admin resolves to manage_system and empty defaults to member.
|
||||
const level = field.permission_values ?? '';
|
||||
switch (level) {
|
||||
case 'none':
|
||||
return false;
|
||||
case 'sysadmin':
|
||||
case 'admin':
|
||||
return opts.canEditAdminManaged;
|
||||
case 'member':
|
||||
case '':
|
||||
return true;
|
||||
default: {
|
||||
const unhandled: never = level;
|
||||
return unhandled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function hasUserAttributeValue(value: UserAttributeValue | undefined): boolean {
|
||||
|
||||
Reference in New Issue
Block a user