diff --git a/server/channels/app/custom_profile_attributes.go b/server/channels/app/custom_profile_attributes.go index ca95fc75cb3..82d69f5bb73 100644 --- a/server/channels/app/custom_profile_attributes.go +++ b/server/channels/app/custom_profile_attributes.go @@ -21,14 +21,18 @@ const ( var cpaGroupID string +func (a *App) CpaGroupID() (string, error) { + return getCpaGroupID(a.Srv().propertyAccessService) +} + // ToDo: we should explore moving this to the database cache layer // instead of maintaining the ID cached at the application level -func (a *App) CpaGroupID() (string, error) { +func getCpaGroupID(service *PropertyAccessService) (string, error) { if cpaGroupID != "" { return cpaGroupID, nil } - cpaGroup, err := a.Srv().propertyAccessService.RegisterPropertyGroup(model.CustomProfileAttributesPropertyGroupName) + cpaGroup, err := service.RegisterPropertyGroup(model.CustomProfileAttributesPropertyGroupName) if err != nil { return "", errors.Wrap(err, "cannot register Custom Profile Attributes property group") } diff --git a/server/channels/app/property_access.go b/server/channels/app/property_access.go index 248c92a567b..557dd35e03d 100644 --- a/server/channels/app/property_access.go +++ b/server/channels/app/property_access.go @@ -68,7 +68,9 @@ func (pas *PropertyAccessService) GetPropertyGroup(name string) (*model.Property // CreatePropertyField creates a new property field. // This method rejects any attempt to set source_plugin_id - only plugins can set this via CreatePropertyFieldForPlugin. func (pas *PropertyAccessService) CreatePropertyField(callerID string, field *model.PropertyField) (*model.PropertyField, error) { - if !groupHasAccessRestrictions(field.GroupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(field.GroupID); err != nil { + return nil, fmt.Errorf("CreatePropertyField: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.CreatePropertyField(field) } @@ -98,7 +100,9 @@ func (pas *PropertyAccessService) CreatePropertyField(callerID string, field *mo // This method automatically sets the source_plugin_id to the provided pluginID. // Only use this method when creating fields through the Plugin API. func (pas *PropertyAccessService) CreatePropertyFieldForPlugin(pluginID string, field *model.PropertyField) (*model.PropertyField, error) { - if !groupHasAccessRestrictions(field.GroupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(field.GroupID); err != nil { + return nil, fmt.Errorf("CreatePropertyFieldForPlugin: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.CreatePropertyField(field) } @@ -129,7 +133,9 @@ func (pas *PropertyAccessService) CreatePropertyFieldForPlugin(pluginID string, // GetPropertyField retrieves a property field by group and field ID. // Field details are filtered based on the caller's access permissions. func (pas *PropertyAccessService) GetPropertyField(callerID string, groupID, id string) (*model.PropertyField, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("GetPropertyField: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.GetPropertyField(groupID, id) } @@ -144,7 +150,9 @@ func (pas *PropertyAccessService) GetPropertyField(callerID string, groupID, id // GetPropertyFields retrieves multiple property fields by their IDs. // Field details are filtered based on the caller's access permissions. func (pas *PropertyAccessService) GetPropertyFields(callerID string, groupID string, ids []string) ([]*model.PropertyField, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("GetPropertyFields: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.GetPropertyFields(groupID, ids) } @@ -159,7 +167,9 @@ func (pas *PropertyAccessService) GetPropertyFields(callerID string, groupID str // GetPropertyFieldByName retrieves a property field by name. // Field details are filtered based on the caller's access permissions. func (pas *PropertyAccessService) GetPropertyFieldByName(callerID string, groupID, targetID, name string) (*model.PropertyField, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("GetPropertyFieldByName: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.GetPropertyFieldByName(groupID, targetID, name) } @@ -194,7 +204,9 @@ func (pas *PropertyAccessService) CountAllPropertyFieldsForTarget(groupID, targe // SearchPropertyFields searches for property fields based on the given options. // Field details are filtered based on the caller's access permissions. func (pas *PropertyAccessService) SearchPropertyFields(callerID string, groupID string, opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("SearchPropertyFields: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.SearchPropertyFields(groupID, opts) } @@ -209,7 +221,9 @@ func (pas *PropertyAccessService) SearchPropertyFields(callerID string, groupID // UpdatePropertyField updates a property field. // Checks write access and ensures source_plugin_id is not changed. func (pas *PropertyAccessService) UpdatePropertyField(callerID string, groupID string, field *model.PropertyField) (*model.PropertyField, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("UpdatePropertyField: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.UpdatePropertyField(groupID, field) } @@ -244,7 +258,9 @@ func (pas *PropertyAccessService) UpdatePropertyField(callerID string, groupID s // UpdatePropertyFields updates multiple property fields. // Checks write access for all fields atomically before updating any. func (pas *PropertyAccessService) UpdatePropertyFields(callerID string, groupID string, fields []*model.PropertyField) ([]*model.PropertyField, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("UpdatePropertyFields: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.UpdatePropertyFields(groupID, fields) } @@ -304,7 +320,9 @@ func (pas *PropertyAccessService) UpdatePropertyFields(callerID string, groupID // DeletePropertyField deletes a property field and all its values. // Checks write access before allowing deletion. func (pas *PropertyAccessService) DeletePropertyField(callerID string, groupID, id string) error { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return fmt.Errorf("DeletePropertyField: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.DeletePropertyField(groupID, id) } @@ -330,7 +348,9 @@ func (pas *PropertyAccessService) DeletePropertyField(callerID string, groupID, // CreatePropertyValue creates a new property value. // Checks write access before allowing the creation. func (pas *PropertyAccessService) CreatePropertyValue(callerID string, value *model.PropertyValue) (*model.PropertyValue, error) { - if !groupHasAccessRestrictions(value.GroupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(value.GroupID); err != nil { + return nil, fmt.Errorf("CreatePropertyValue: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.CreatePropertyValue(value) } @@ -357,7 +377,9 @@ func (pas *PropertyAccessService) CreatePropertyValue(callerID string, value *mo func (pas *PropertyAccessService) CreatePropertyValues(callerID string, values []*model.PropertyValue) ([]*model.PropertyValue, error) { shouldApplyAccessControl := false for _, value := range values { - if groupHasAccessRestrictions(value.GroupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(value.GroupID); err != nil { + return nil, fmt.Errorf("CreatePropertyValues: cannot determine access restrictions: %w", err) + } else if hasRestrictions { shouldApplyAccessControl = true break } @@ -394,7 +416,9 @@ func (pas *PropertyAccessService) CreatePropertyValues(callerID string, values [ // GetPropertyValue retrieves a property value by ID. // Returns (nil, nil) if the value exists but the caller doesn't have access. func (pas *PropertyAccessService) GetPropertyValue(callerID string, groupID, id string) (*model.PropertyValue, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("GetPropertyValue: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.GetPropertyValue(groupID, id) } @@ -420,7 +444,9 @@ func (pas *PropertyAccessService) GetPropertyValue(callerID string, groupID, id // GetPropertyValues retrieves multiple property values by their IDs. // Values the caller doesn't have access to are silently filtered out. func (pas *PropertyAccessService) GetPropertyValues(callerID string, groupID string, ids []string) ([]*model.PropertyValue, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("GetPropertyValues: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.GetPropertyValues(groupID, ids) } @@ -440,7 +466,9 @@ func (pas *PropertyAccessService) GetPropertyValues(callerID string, groupID str // SearchPropertyValues searches for property values based on the given options. // Values the caller doesn't have access to are silently filtered out. func (pas *PropertyAccessService) SearchPropertyValues(callerID string, groupID string, opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("SearchPropertyValues: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.SearchPropertyValues(groupID, opts) } @@ -460,7 +488,9 @@ func (pas *PropertyAccessService) SearchPropertyValues(callerID string, groupID // UpdatePropertyValue updates a property value. // Checks write access before allowing the update. func (pas *PropertyAccessService) UpdatePropertyValue(callerID string, groupID string, value *model.PropertyValue) (*model.PropertyValue, error) { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return nil, fmt.Errorf("UpdatePropertyValue: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.UpdatePropertyValue(groupID, value) } @@ -487,7 +517,9 @@ func (pas *PropertyAccessService) UpdatePropertyValue(callerID string, groupID s func (pas *PropertyAccessService) UpdatePropertyValues(callerID string, groupID string, values []*model.PropertyValue) ([]*model.PropertyValue, error) { shouldApplyAccessControl := false for _, value := range values { - if groupHasAccessRestrictions(value.GroupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(value.GroupID); err != nil { + return nil, fmt.Errorf("UpdatePropertyValues: cannot determine access restrictions: %w", err) + } else if hasRestrictions { shouldApplyAccessControl = true break } @@ -528,7 +560,9 @@ func (pas *PropertyAccessService) UpdatePropertyValues(callerID string, groupID // UpsertPropertyValue creates or updates a property value. // Checks write access before allowing the upsert. func (pas *PropertyAccessService) UpsertPropertyValue(callerID string, value *model.PropertyValue) (*model.PropertyValue, error) { - if !groupHasAccessRestrictions(value.GroupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(value.GroupID); err != nil { + return nil, fmt.Errorf("UpsertPropertyValue cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.UpsertPropertyValue(value) } @@ -555,7 +589,9 @@ func (pas *PropertyAccessService) UpsertPropertyValue(callerID string, value *mo func (pas *PropertyAccessService) UpsertPropertyValues(callerID string, values []*model.PropertyValue) ([]*model.PropertyValue, error) { shouldApplyAccessControl := false for _, value := range values { - if groupHasAccessRestrictions(value.GroupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(value.GroupID); err != nil { + return nil, fmt.Errorf("UpsertPropertyValues: cannot determine access restrictions: %w", err) + } else if hasRestrictions { shouldApplyAccessControl = true break } @@ -596,7 +632,9 @@ func (pas *PropertyAccessService) UpsertPropertyValues(callerID string, values [ // DeletePropertyValue deletes a property value. // Checks write access before allowing deletion. func (pas *PropertyAccessService) DeletePropertyValue(callerID string, groupID, id string) error { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return fmt.Errorf("DeletePropertyValue: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.DeletePropertyValue(groupID, id) } @@ -627,7 +665,9 @@ func (pas *PropertyAccessService) DeletePropertyValue(callerID string, groupID, // DeletePropertyValuesForTarget deletes all property values for a specific target. // Checks write access for all affected fields atomically before deleting. func (pas *PropertyAccessService) DeletePropertyValuesForTarget(callerID string, groupID string, targetType string, targetID string) error { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return fmt.Errorf("DeletePropertyValuesForTarget: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.DeletePropertyValuesForTarget(groupID, targetType, targetID) } @@ -709,7 +749,9 @@ func (pas *PropertyAccessService) DeletePropertyValuesForTarget(callerID string, // DeletePropertyValuesForField deletes all property values for a specific field. // Checks write access before allowing deletion. func (pas *PropertyAccessService) DeletePropertyValuesForField(callerID string, groupID, fieldID string) error { - if !groupHasAccessRestrictions(groupID) { + if hasRestrictions, err := pas.groupHasAccessRestrictions(groupID); err != nil { + return fmt.Errorf("DeletePropertyValuesForField: cannot determine access restrictions: %w", err) + } else if !hasRestrictions { return pas.propertyService.DeletePropertyValuesForField(groupID, fieldID) } @@ -1185,6 +1227,10 @@ func (pas *PropertyAccessService) applyValueReadAccessControl(values []*model.Pr return filtered, nil } -func groupHasAccessRestrictions(groupId string) bool { - return cpaGroupID != "" && groupId == cpaGroupID +func (pas *PropertyAccessService) groupHasAccessRestrictions(groupId string) (bool, error) { + cpaID, err := getCpaGroupID(pas) + if err != nil { + return false, err + } + return groupId == cpaID, nil }