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) }