mirror of
https://github.com/mattermost/mattermost.git
synced 2026-04-13 04:57:45 -04:00
Custom profile attributes (properties) in Mattermost need to support security-critical use cases like Attribute-Based Access Control (ABAC), external identity system synchronization, and privacy-preserving collaboration. Without access controls on these properties, any user or component could modify property fields and values, making them unsuitable for security decisions. Additionally, different properties require different visibility patterns - some need to be publicly readable, some should only be visible to their managing system, and some require privacy-preserving visibility where users can only see shared values. This change introduces the PropertyAccessService, a wrapper around PropertyService that enforces access control for all property operations. This service is introduced in isolation and is not yet hooked up to the Plugin API, REST API, or app layer. It provides the foundation for a single enforcement point that will apply access restrictions consistently across all code paths once integrated.
77 lines
2.3 KiB
Go
77 lines
2.3 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package model
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
const (
|
|
// Property Field Access Control Attributes
|
|
PropertyAttrsProtected = "protected"
|
|
PropertyAttrsSourcePluginID = "source_plugin_id"
|
|
PropertyAttrsAccessMode = "access_mode"
|
|
|
|
// Access Modes
|
|
PropertyAccessModePublic = "" // Empty string means public (default)
|
|
PropertyAccessModeSourceOnly = "source_only"
|
|
PropertyAccessModeSharedOnly = "shared_only"
|
|
)
|
|
|
|
// IsKnownPropertyAccessMode checks if the given access mode is a recognized value
|
|
func IsKnownPropertyAccessMode(accessMode string) bool {
|
|
switch accessMode {
|
|
case PropertyAccessModePublic,
|
|
PropertyAccessModeSourceOnly,
|
|
PropertyAccessModeSharedOnly:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsPropertyFieldProtected returns whether a PropertyField is protected from modifications
|
|
// by callers other than the source plugin
|
|
func IsPropertyFieldProtected(field *PropertyField) bool {
|
|
if field.Attrs == nil {
|
|
return false
|
|
}
|
|
|
|
protected, ok := field.Attrs[PropertyAttrsProtected].(bool)
|
|
return ok && protected
|
|
}
|
|
|
|
// ValidatePropertyFieldAccessMode validates that the access_mode attribute is valid
|
|
// and compatible with the field type
|
|
func ValidatePropertyFieldAccessMode(field *PropertyField) error {
|
|
if field.Attrs == nil {
|
|
return nil
|
|
}
|
|
|
|
accessMode, ok := field.Attrs[PropertyAttrsAccessMode].(string)
|
|
if !ok {
|
|
// No access mode set, that's fine (defaults to public)
|
|
return nil
|
|
}
|
|
|
|
// Check if access mode is known
|
|
if !IsKnownPropertyAccessMode(accessMode) {
|
|
return fmt.Errorf("invalid access mode '%s'", accessMode)
|
|
}
|
|
|
|
// Validate shared_only is only used with select/multiselect fields
|
|
if accessMode == PropertyAccessModeSharedOnly {
|
|
if field.Type != PropertyFieldTypeSelect && field.Type != PropertyFieldTypeMultiselect {
|
|
return fmt.Errorf("access mode 'shared_only' can only be used with select or multiselect field types, got '%s'", field.Type)
|
|
}
|
|
}
|
|
|
|
// Validate that non-public access modes require protected flag
|
|
if accessMode == PropertyAccessModeSourceOnly || accessMode == PropertyAccessModeSharedOnly {
|
|
if !IsPropertyFieldProtected(field) {
|
|
return fmt.Errorf("access mode '%s' requires the field to be protected", accessMode)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|