mattermost-community-enterp.../public/model/access_policy.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

244 lines
7.4 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"fmt"
"slices"
"github.com/pkg/errors"
"golang.org/x/mod/semver"
)
const (
AccessControlPolicyTypeParent = "parent"
AccessControlPolicyTypeChannel = "channel"
MaxPolicyNameLength = 128
AccessControlPolicyVersionV0_1 = "v0.1"
AccessControlPolicyVersionV0_2 = "v0.2"
)
// AccessControlAttribute represents a user attribute with its name and possible values
type AccessControlAttribute struct {
Attribute PropertyField `json:"attribute"`
Values []string `json:"values"`
}
type AccessControlPolicyTestResponse struct {
Users []*User `json:"users"`
Total int64 `json:"total"`
}
type GetAccessControlPolicyOptions struct {
Type string `json:"type"`
ParentID string `json:"parent_id"`
Cursor AccessControlPolicyCursor `json:"cursor"`
Limit int `json:"limit"`
}
type AccessControlPolicySearch struct {
Term string `json:"term"`
Type string `json:"type"`
ParentID string `json:"parent_id"`
IDs []string `json:"ids"`
Cursor AccessControlPolicyCursor `json:"cursor"`
Limit int `json:"limit"`
IncludeChildren bool `json:"include_children"`
Active bool `json:"active"`
}
type AccessControlPolicyCursor struct {
ID string `json:"id"`
}
type AccessControlPoliciesWithCount struct {
Policies []*AccessControlPolicy `json:"policies"`
Total int64 `json:"total"`
}
type AccessControlPolicy struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Active bool `json:"active"`
CreateAt int64 `json:"create_at"`
Revision int `json:"revision"`
Version string `json:"version"`
Imports []string `json:"imports"`
Rules []AccessControlPolicyRule `json:"rules"`
Props map[string]any `json:"props"` // add auto-sync property here, also maybe the attributes being used in the expression
}
type AccessControlPolicyRule struct {
Actions []string `json:"actions"`
Expression string `json:"expression"`
}
type CELExpressionError struct {
Line int `json:"line"`
Column int `json:"column"`
Message string `json:"message"`
}
type AccessControlQueryResult struct {
MatchedSubjectIDs []string `json:"matched_subject_ids"`
}
func (p *AccessControlPolicy) IsValid() *AppError {
switch p.Version {
case AccessControlPolicyVersionV0_1:
return p.accessPolicyVersionV0_1()
case AccessControlPolicyVersionV0_2:
return p.accessPolicyVersionV0_2()
default:
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.version.app_error", nil, "", 400)
}
}
func (p *AccessControlPolicy) accessPolicyVersionV0_1() *AppError {
if !slices.Contains([]string{AccessControlPolicyTypeParent, AccessControlPolicyTypeChannel}, p.Type) {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.type.app_error", nil, "", 400)
}
if !IsValidId(p.ID) {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.id.app_error", nil, "", 400)
}
if p.Type == AccessControlPolicyTypeParent && (p.Name == "" || len(p.Name) > MaxPolicyNameLength) {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.name.app_error", nil, "", 400)
}
if p.Revision < 0 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.revision.app_error", nil, "", 400)
}
if !semver.IsValid(p.Version) {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.version.app_error", nil, "", 400)
}
switch p.Type {
case AccessControlPolicyTypeParent:
if len(p.Rules) == 0 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.rules.app_error", nil, "", 400)
}
if len(p.Imports) > 0 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.imports.app_error", nil, "", 400)
}
case AccessControlPolicyTypeChannel:
if len(p.Rules) == 0 && len(p.Imports) == 0 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.rules_imports.app_error", nil, "", 400)
}
if len(p.Rules) == 0 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.rules.app_error", nil, "", 400)
}
if len(p.Imports) > 1 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.imports.app_error", nil, "", 400)
}
}
return nil
}
func (p *AccessControlPolicy) accessPolicyVersionV0_2() *AppError {
if !slices.Contains([]string{AccessControlPolicyTypeParent, AccessControlPolicyTypeChannel}, p.Type) {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.type.app_error", nil, "", 400)
}
if !IsValidId(p.ID) {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.id.app_error", nil, "", 400)
}
if p.Type == AccessControlPolicyTypeParent && (p.Name == "" || len(p.Name) > MaxPolicyNameLength) {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.name.app_error", nil, "", 400)
}
if p.Revision < 0 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.revision.app_error", nil, "", 400)
}
if !semver.IsValid(p.Version) {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.version.app_error", nil, "", 400)
}
switch p.Type {
case AccessControlPolicyTypeParent:
if len(p.Rules) == 0 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.rules.app_error", nil, "", 400)
}
if len(p.Imports) > 0 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.imports.app_error", nil, "", 400)
}
case AccessControlPolicyTypeChannel:
if len(p.Rules) == 0 && len(p.Imports) == 0 {
return NewAppError("AccessControlPolicy.IsValid", "model.access_policy.is_valid.rules_imports.app_error", nil, "", 400)
}
}
return nil
}
func (p *AccessControlPolicy) Inherit(parent *AccessControlPolicy) *AppError {
rules := make([]AccessControlPolicyRule, len(p.Rules))
switch p.Version {
case AccessControlPolicyVersionV0_1:
p.Imports = []string{parent.ID}
for i, rule := range p.Rules {
actions := make([]string, len(rule.Actions))
copy(actions, rule.Actions)
rules[i] = AccessControlPolicyRule{
Actions: actions,
Expression: fmt.Sprintf("policies.id_%s", p.ID),
}
}
case AccessControlPolicyVersionV0_2:
if slices.Contains(p.Imports, parent.ID) {
return NewAppError("AccessControlPolicy.Inherit", "model.access_policy.inherit.already_imported.app_error", nil, "", 400)
}
p.Imports = append(p.Imports, parent.ID)
default:
return NewAppError("AccessControlPolicy.Inherit", "model.access_policy.inherit.version.app_error", nil, "", 400)
}
if appErr := p.IsValid(); appErr != nil {
return appErr
}
return nil
}
func (c *AccessControlPolicyCursor) IsEmpty() bool {
return c.ID == ""
}
func (c *AccessControlPolicyCursor) IsValid() error {
if c.IsEmpty() {
return nil
}
if !IsValidId(c.ID) {
return errors.New("cursor id is invalid")
}
return nil
}
func (p *AccessControlPolicy) Auditable() map[string]any {
return map[string]any{
"id": p.ID,
"type": p.Type,
"revision": p.Revision,
}
}