Select Git revision
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)
}