* refactor: Move property value sanitization to model layer * feat: Add value sanitization for custom profile attributes * refactor: Update custom profile attributes to use json.RawMessage * refactor: Update patchCustomProfileAttribute to handle json.RawMessage directly * refactor: Refactor custom profile attributes handler with improved validation * refactor: Rename `patchCustomProfileAttribute` to `patchCPAValues` * refactor: Replace ReturnJSON with json.NewEncoder and add error logging * feat: Add encoding/json import to property_value.go * refactor: Update property value tests to use json.RawMessage * fix: Convert string value to json.RawMessage in property value test * fix: Convert string literals to json.RawMessage in property value tests * fix: Add missing encoding/json import in custom_profile_attributes.go * fix: Preserve JSON RawMessage type in listCPAValues function * fix: Update custom profile attributes test to use json.RawMessage * feat: Add json import to custom_profile_attributes_test.go * refactor: Update ListCPAValues and PatchCPAValues to use json.RawMessage * refactor: Rename `actualValue` to `updatedValue` in custom profile attributes test * refactor: Improve user permission and audit logging for custom profile attributes patch * refactor: Optimize CPA field lookup by using ListCPAFields() and map * fix: Correct user ID reference in custom profile attributes patch endpoint * refactor: Change patchCPAValues to use map[string]json.RawMessage for results * refactor: format and fix tests * test: Add comprehensive unit tests for sanitizePropertyValue function * test: Add test case for invalid property value type * feat: Use `model.NewId()` to generate valid IDs in custom profile attributes tests * refactor: Replace hardcoded IDs with dynamic variables in custom profile attributes test * refactor: restore variable name * refactor: drop undesired changes * chore: refresh app layers * feat: Update API definition to support string or string array values for custom profile attributes * test: Add test cases for multiselect custom profile attribute values * test: Add tests for multiselect custom profile attribute values * test: Isolate array value test in separate t.Run * test: Add test case for multiselect array values in custom profile attributes * refactor: Move array value test from TestCreateCPAField to TestPatchCPAValue * test: Update custom profile attributes test assertions * test: add test case for handling array values in GetCPAValue * test: Add array value tests for property value store * refactor(store): no need to convert to json the rawmessage * chore: lint * i18n * use model to interface with sqlx * fix: Allow empty strings for text, date, and select profile attributes * refactor: Filter out empty strings in multiselect and multiuser fields * refactor: Update multiuser field sanitization to validate and error on invalid IDs * refactor: Simplify sanitizePropertyValue function with reduced code duplication * fix: Allow empty user ID in custom profile attribute sanitization * refactor: Convert comment-based subtests to nested t.Run in TestSanitizePropertyValue * refactor: Convert comment-based subtests to nested t.Run tests in TestSanitizePropertyValue --------- Co-authored-by: Mattermost Build <build@mattermost.com>
152 строки
4.4 KiB
Go
152 строки
4.4 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package model
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type PropertyFieldType string
|
|
|
|
const (
|
|
PropertyFieldTypeText PropertyFieldType = "text"
|
|
PropertyFieldTypeSelect PropertyFieldType = "select"
|
|
PropertyFieldTypeMultiselect PropertyFieldType = "multiselect"
|
|
PropertyFieldTypeDate PropertyFieldType = "date"
|
|
PropertyFieldTypeUser PropertyFieldType = "user"
|
|
PropertyFieldTypeMultiuser PropertyFieldType = "multiuser"
|
|
)
|
|
|
|
type PropertyField struct {
|
|
ID string `json:"id"`
|
|
GroupID string `json:"group_id"`
|
|
Name string `json:"name"`
|
|
Type PropertyFieldType `json:"type"`
|
|
Attrs StringInterface `json:"attrs"`
|
|
TargetID string `json:"target_id"`
|
|
TargetType string `json:"target_type"`
|
|
CreateAt int64 `json:"create_at"`
|
|
UpdateAt int64 `json:"update_at"`
|
|
DeleteAt int64 `json:"delete_at"`
|
|
}
|
|
|
|
func (pf *PropertyField) Auditable() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"id": pf.ID,
|
|
"group_id": pf.GroupID,
|
|
"name": pf.Name,
|
|
"type": pf.Type,
|
|
"attrs": pf.Attrs,
|
|
"target_id": pf.TargetID,
|
|
"target_type": pf.TargetType,
|
|
"create_at": pf.CreateAt,
|
|
"update_at": pf.UpdateAt,
|
|
"delete_at": pf.DeleteAt,
|
|
}
|
|
}
|
|
|
|
func (pf *PropertyField) PreSave() {
|
|
if pf.ID == "" {
|
|
pf.ID = NewId()
|
|
}
|
|
|
|
if pf.CreateAt == 0 {
|
|
pf.CreateAt = GetMillis()
|
|
}
|
|
pf.UpdateAt = pf.CreateAt
|
|
}
|
|
|
|
func (pf *PropertyField) IsValid() error {
|
|
if !IsValidId(pf.ID) {
|
|
return NewAppError("PropertyField.IsValid", "model.property_field.is_valid.app_error", map[string]any{"FieldName": "id", "Reason": "invalid id"}, "", http.StatusBadRequest)
|
|
}
|
|
|
|
if !IsValidId(pf.GroupID) {
|
|
return NewAppError("PropertyField.IsValid", "model.property_field.is_valid.app_error", map[string]any{"FieldName": "group_id", "Reason": "invalid id"}, "id="+pf.ID, http.StatusBadRequest)
|
|
}
|
|
|
|
if pf.Name == "" {
|
|
return NewAppError("PropertyField.IsValid", "model.property_field.is_valid.app_error", map[string]any{"FieldName": "name", "Reason": "value cannot be empty"}, "id="+pf.ID, http.StatusBadRequest)
|
|
}
|
|
|
|
if pf.Type != PropertyFieldTypeText &&
|
|
pf.Type != PropertyFieldTypeSelect &&
|
|
pf.Type != PropertyFieldTypeMultiselect &&
|
|
pf.Type != PropertyFieldTypeDate &&
|
|
pf.Type != PropertyFieldTypeUser &&
|
|
pf.Type != PropertyFieldTypeMultiuser {
|
|
return NewAppError("PropertyField.IsValid", "model.property_field.is_valid.app_error", map[string]any{"FieldName": "type", "Reason": "unknown value"}, "id="+pf.ID, http.StatusBadRequest)
|
|
}
|
|
|
|
if pf.CreateAt == 0 {
|
|
return NewAppError("PropertyField.IsValid", "model.property_field.is_valid.app_error", map[string]any{"FieldName": "create_at", "Reason": "value cannot be zero"}, "id="+pf.ID, http.StatusBadRequest)
|
|
}
|
|
|
|
if pf.UpdateAt == 0 {
|
|
return NewAppError("PropertyField.IsValid", "model.property_field.is_valid.app_error", map[string]any{"FieldName": "update_at", "Reason": "value cannot be zero"}, "id="+pf.ID, http.StatusBadRequest)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pf *PropertyField) SanitizeInput() {
|
|
pf.Name = strings.TrimSpace(pf.Name)
|
|
}
|
|
|
|
type PropertyFieldPatch struct {
|
|
Name *string `json:"name"`
|
|
Type *PropertyFieldType `json:"type"`
|
|
Attrs *map[string]any `json:"attrs"`
|
|
TargetID *string `json:"target_id"`
|
|
TargetType *string `json:"target_type"`
|
|
}
|
|
|
|
func (pfp *PropertyFieldPatch) Auditable() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"name": pfp.Name,
|
|
"type": pfp.Type,
|
|
"attrs": pfp.Attrs,
|
|
"target_id": pfp.TargetID,
|
|
"target_type": pfp.TargetType,
|
|
}
|
|
}
|
|
|
|
func (pfp *PropertyFieldPatch) SanitizeInput() {
|
|
if pfp.Name != nil {
|
|
pfp.Name = NewPointer(strings.TrimSpace(*pfp.Name))
|
|
}
|
|
}
|
|
|
|
func (pf *PropertyField) Patch(patch *PropertyFieldPatch) {
|
|
if patch.Name != nil {
|
|
pf.Name = *patch.Name
|
|
}
|
|
|
|
if patch.Type != nil {
|
|
pf.Type = *patch.Type
|
|
}
|
|
|
|
if patch.Attrs != nil {
|
|
pf.Attrs = *patch.Attrs
|
|
}
|
|
|
|
if patch.TargetID != nil {
|
|
pf.TargetID = *patch.TargetID
|
|
}
|
|
|
|
if patch.TargetType != nil {
|
|
pf.TargetType = *patch.TargetType
|
|
}
|
|
}
|
|
|
|
type PropertyFieldSearchOpts struct {
|
|
GroupID string
|
|
TargetType string
|
|
TargetID string
|
|
IncludeDeleted bool
|
|
Page int
|
|
PerPage int
|
|
}
|