Skip to content
Snippets Groups Projects
Select Git revision
  • 9d31d87553b4d903a51085ebef28561b1d7dbcac
  • master default protected
  • fix/PRXS-3401-ValidateValidationOpts
  • feature/PRXS-3383-CollectionsRankSortAPI
  • feature/3149-LocaleCodeAsID-Feature
  • feature/PRXS-3383-CollectionsSort
  • feature/3109-SerializeFeature
  • release/0.33
  • feature/3109-RecoverySchema
  • feature/3109-feature
  • fix/PRXS-3369-ValidateFields
  • refactor/PRXS-3306-MovePkgGroup1
  • refactor/6-pkg-refactor-expr
  • fix/PRXS-3360-TemplateBuilderPatch
  • feature/3293-MongoV2
  • feature/3272-GoVersionUp
  • feature/PRXS-3218-HideTemplateActions
  • feature/PRXS-3234-PruneIdents
  • feature/3146-UpdateItemStorageInterface
  • feature/3274-ObjectIndexesFixes
  • feature/PRXS-3143-3235-ReferenceOptions
  • v0.33.1
  • v0.32.0
  • v0.31.1
  • v0.31.0
  • v0.30.0
  • v0.29.0
  • v0.28.0
  • v0.27.0-alpha.1+16
  • v0.27.0-alpha.1+15
  • v0.27.0-alpha.1+14
  • v0.27.0-alpha.1+13
  • v0.27.0-alpha.1+12
  • v0.27.0-alpha.1+11
  • v0.27.0-alpha.1+10
  • v0.27.0-alpha.1+9
  • v0.27.0-alpha.1+8
  • v0.27.0-alpha.1+7
  • v0.27.0-alpha.1+6
  • v0.27.0-alpha.1+5
  • v0.27.0-alpha.1+4
41 results

expr.go

Blame
  • ruleset.go 7.55 KiB
    package permission
    
    import (
    	"git.perx.ru/perxis/perxis-go/pkg/data"
    	perxisexpr "git.perx.ru/perxis/perxis-go/pkg/expr"
    )
    
    type Action uint64
    
    const (
    	//ActionAny Action = iota
    	ActionCreate Action = iota + 1
    	ActionRead
    	ActionUpdate
    	ActionDelete
    )
    
    // Rule - правило доступа к контенту
    type Rule struct {
    	CollectionID string   `json:"collectionId" bson:"collectionId,omitempty"`
    	Actions      []Action `json:"actions" bson:"actions,omitempty"`
    	Access       Access   `json:"access" bson:"access,omitempty"`
    	// Поля не передаются API клиенту
    	HiddenFields []string `json:"hiddenFields,omitempty" bson:"hiddenFields,omitempty"`
    	// Запрещен чтение указанные полей
    	DenyReadFields []string `json:"denyReadFields,omitempty" bson:"denyReadFields,omitempty"`
    	// Запрещена запись в указанные поля
    	DenyWriteFields []string `json:"denyWriteFields,omitempty" bson:"denyWriteFields,omitempty"`
    	// Дополнительный фильтр
    	ReadFilter  string `json:"readFilter,omitempty" bson:"readFilter,omitempty"`
    	WriteFilter string `json:"writeFilter,omitempty" bson:"writeFilter,omitempty"`
    }
    
    func NewRule(collectionID string, actions ...Action) *Rule {
    	return &Rule{
    		CollectionID: collectionID,
    		Actions:      actions,
    	}
    }
    
    func (r Rule) Clone() *Rule {
    	return &Rule{
    		CollectionID:    r.CollectionID,
    		Actions:         append([]Action(nil), r.Actions...),
    		Access:          r.Access,
    		HiddenFields:    append([]string(nil), r.HiddenFields...),
    		DenyReadFields:  append([]string(nil), r.DenyReadFields...),
    		DenyWriteFields: append([]string(nil), r.DenyWriteFields...),
    		ReadFilter:      r.ReadFilter,
    		WriteFilter:     r.WriteFilter,
    	}
    }
    
    func (r Rule) WithReadFilter(f string) *Rule {
    	return &Rule{
    		CollectionID:    r.CollectionID,
    		Actions:         append([]Action(nil), r.Actions...),
    		Access:          r.Access,
    		HiddenFields:    append([]string(nil), r.HiddenFields...),
    		DenyReadFields:  append([]string(nil), r.DenyReadFields...),
    		DenyWriteFields: append([]string(nil), r.DenyWriteFields...),
    		ReadFilter:      f,
    		WriteFilter:     r.WriteFilter,
    	}
    }
    
    func (r Rule) WithWriteFilter(f string) *Rule {
    	return &Rule{
    		CollectionID:    r.CollectionID,
    		Actions:         append([]Action(nil), r.Actions...),
    		Access:          r.Access,
    		HiddenFields:    append([]string(nil), r.HiddenFields...),
    		DenyReadFields:  append([]string(nil), r.DenyReadFields...),
    		DenyWriteFields: append([]string(nil), r.DenyWriteFields...),
    		ReadFilter:      r.ReadFilter,
    		WriteFilter:     f,
    	}
    }
    
    func (r Rule) WithReadWriteFilter(f string) *Rule {
    	return &Rule{
    		CollectionID:    r.CollectionID,
    		Actions:         append([]Action(nil), r.Actions...),
    		Access:          r.Access,
    		HiddenFields:    append([]string(nil), r.HiddenFields...),
    		DenyReadFields:  append([]string(nil), r.DenyReadFields...),
    		DenyWriteFields: append([]string(nil), r.DenyWriteFields...),
    		ReadFilter:      f,
    		WriteFilter:     f,
    	}
    }
    
    func (r Rule) WithReadonlyFields(ff ...string) *Rule {
    	return &Rule{
    		CollectionID:    r.CollectionID,
    		Actions:         append([]Action(nil), r.Actions...),
    		Access:          r.Access,
    		HiddenFields:    append([]string(nil), r.HiddenFields...),
    		DenyReadFields:  append(ff, r.DenyReadFields...),
    		DenyWriteFields: append([]string(nil), r.DenyWriteFields...),
    		ReadFilter:      r.ReadFilter,
    		WriteFilter:     r.WriteFilter,
    	}
    }
    
    func (r Rule) WithHiddenFields(ff ...string) *Rule {
    	return &Rule{
    		CollectionID:    r.CollectionID,
    		Actions:         append([]Action(nil), r.Actions...),
    		Access:          r.Access,
    		HiddenFields:    append(ff, r.HiddenFields...),
    		DenyReadFields:  append([]string(nil), r.DenyReadFields...),
    		DenyWriteFields: append([]string(nil), r.DenyWriteFields...),
    		ReadFilter:      r.ReadFilter,
    		WriteFilter:     r.WriteFilter,
    	}
    }
    
    func (r Rule) GetPermission(action Action) *Permission {
    	for _, a := range r.Actions {
    		if a == action {
    			p := &Permission{
    				Permitted: true,
    			}
    
    			switch action {
    			case ActionRead:
    				p.Filter = r.ReadFilter
    				p.UnallowedFields = append(p.UnallowedFields, r.HiddenFields...)
    				p.UnallowedFields = append(p.UnallowedFields, r.DenyWriteFields...)
    			case ActionCreate, ActionUpdate, ActionDelete:
    				p.Filter = r.WriteFilter
    				p.UnallowedFields = append(p.UnallowedFields, r.DenyReadFields...)
    			}
    
    			p.UnallowedFields = data.SetFromSlice(p.UnallowedFields)
    
    			return p
    		}
    	}
    
    	return &Permission{}
    }
    
    type Ruleset interface {
    	GetRule(collectionID string) *Rule
    	Permission(collectionID string, action Action) *Permission
    }
    
    type Rules []*Rule
    
    func (r Rules) Permission(collectionID string, action Action) *Permission {
    	rule := r.GetRule(collectionID)
    	return rule.GetPermission(action)
    }
    
    func (r Rules) GetRule(collectionID string) *Rule {
    	for _, rule := range r {
    		if data.GlobMatch(collectionID, rule.CollectionID) {
    			return rule
    		}
    	}
    	return nil
    }
    
    func MergeRules(src, in Rules) Rules {
    	dst := make(Rules, 0, len(src)+len(in))
    	seen := make(map[string]struct{})
    
    	for _, rule := range src {
    		if _, ok := seen[rule.CollectionID]; !ok {
    			dst = append(dst, rule)
    			seen[rule.CollectionID] = struct{}{}
    		}
    	}
    
    	for _, rule := range in {
    		if _, ok := seen[rule.CollectionID]; !ok {
    			dst = append(dst, rule)
    			seen[rule.CollectionID] = struct{}{}
    		}
    	}
    
    	return dst
    }
    
    // MergeRule объединяет несколько Rule в один
    //   - пересечение действий
    //   - объединение hidden, readOnly, writeOnly fields
    //   - объединение фильтров
    func MergeRule(rules ...*Rule) *Rule {
    
    	if len(rules) == 0 {
    		return nil
    	}
    
    	var result *Rule
    	var writeFilter []string
    	var readFilter []string
    
    	for i, r := range rules {
    		if i == 0 { // first element
    
    			result = r.Clone()
    
    			result.CollectionID = ""
    			if result.WriteFilter != "" {
    				writeFilter = append(writeFilter, result.WriteFilter)
    			}
    			if result.ReadFilter != "" {
    				readFilter = append(readFilter, result.ReadFilter)
    			}
    
    			continue
    		}
    
    		result.Actions = data.GetIntersection(result.Actions, r.Actions)
    		result.HiddenFields = data.SetFromSlice(append(result.HiddenFields, r.HiddenFields...))
    		result.DenyReadFields = data.SetFromSlice(append(result.DenyReadFields, r.DenyReadFields...))
    		result.DenyWriteFields = data.SetFromSlice(append(result.DenyWriteFields, r.DenyWriteFields...))
    		if r.WriteFilter != "" {
    			writeFilter = append(writeFilter, r.WriteFilter)
    		}
    		if r.ReadFilter != "" {
    			readFilter = append(readFilter, r.ReadFilter)
    		}
    	}
    
    	result.WriteFilter = perxisexpr.And(data.SetFromSlice(writeFilter)...)
    	result.ReadFilter = perxisexpr.And(data.SetFromSlice(readFilter)...)
    
    	return result
    }
    
    type PrivilegedRuleset struct{}
    
    func (r PrivilegedRuleset) Permission(_ string, _ Action) *Permission {
    	return &Permission{
    		Permitted:       true,
    		UnallowedFields: []string{},
    	}
    }
    
    func (r PrivilegedRuleset) GetRule(collectionID string) *Rule {
    	return &Rule{
    		CollectionID:    collectionID,
    		Actions:         []Action{ActionRead, ActionCreate, ActionUpdate, ActionDelete},
    		HiddenFields:    []string{},
    		DenyReadFields:  []string{},
    		DenyWriteFields: []string{},
    	}
    }
    
    func Create(r Ruleset, collectionID string) *Permission {
    	return r.Permission(collectionID, ActionCreate)
    }
    
    func Read(r Ruleset, collectionID string) *Permission {
    	return r.Permission(collectionID, ActionRead)
    }
    
    func Update(r Ruleset, collectionID string) *Permission {
    	return r.Permission(collectionID, ActionUpdate)
    }
    
    func Delete(r Ruleset, collectionID string) *Permission {
    	return r.Permission(collectionID, ActionDelete)
    }