Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
185 lines
3.6 KiB
Go
185 lines
3.6 KiB
Go
package rbac
|
|
|
|
import (
|
|
"github.com/cortezaproject/corteza/server/pkg/auth"
|
|
"github.com/cortezaproject/corteza/server/pkg/slice"
|
|
)
|
|
|
|
func merge(base RuleSet, new ...*Rule) (out RuleSet) {
|
|
var (
|
|
o int
|
|
blen = len(base)
|
|
)
|
|
|
|
if blen == 0 {
|
|
// Nothing exists yet, mark all as dirty
|
|
for r := range new {
|
|
new[r].dirty = true
|
|
}
|
|
|
|
return new
|
|
} else {
|
|
out = base
|
|
|
|
newRules:
|
|
for _, rule := range new {
|
|
// Never go beyond the last base rule (blen)
|
|
for o = 0; o < blen; o++ {
|
|
if eq(out[o], rule) {
|
|
out[o].dirty = out[o].Access != rule.Access
|
|
out[o].Access = rule.Access
|
|
|
|
// only one rule can match so proceed with next new rule
|
|
continue newRules
|
|
}
|
|
}
|
|
|
|
// none of the base new matched, append
|
|
var c = *rule
|
|
c.dirty = true
|
|
|
|
out = append(out, &c)
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func eq(a, b *Rule) bool {
|
|
if a == nil || b == nil {
|
|
return false
|
|
}
|
|
|
|
return a.RoleID == b.RoleID &&
|
|
a.Resource == b.Resource &&
|
|
a.Operation == b.Operation
|
|
}
|
|
|
|
func ruleByRole(base RuleSet, roleID uint64) (out RuleSet) {
|
|
for _, r := range base {
|
|
if r.RoleID == roleID {
|
|
out = append(out, r)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Dirty returns list of deleted (Access==Inherit) and changed (dirty) rules
|
|
func flushable(set RuleSet) (deletable, updatable, final RuleSet) {
|
|
deletable, updatable, final = RuleSet{}, RuleSet{}, RuleSet{}
|
|
|
|
for _, r := range set {
|
|
var c = *r
|
|
if r.Access == Inherit {
|
|
deletable = append(deletable, &c)
|
|
continue
|
|
}
|
|
|
|
if r.dirty {
|
|
updatable = append(updatable, &c)
|
|
}
|
|
|
|
final = append(final, &c)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Significant roles (sigRoles) returns two list of significant roles.
|
|
//
|
|
// First slice are roles that are allowed to perform an operation on a specific resource (directly or indirectly),
|
|
// 2nd slice are roles that are denied the op.
|
|
func (set RuleSet) sigRoles(res string, op string) (aRR, dRR []uint64) {
|
|
if hasWildcards(res) {
|
|
// nothing to do here, we need a direct resource id (level=0)
|
|
return
|
|
}
|
|
|
|
const (
|
|
dirRes = 0
|
|
indRes = 1
|
|
)
|
|
|
|
var (
|
|
rr = map[Access]map[int][]uint64{
|
|
Allow: {
|
|
dirRes: make([]uint64, 0),
|
|
indRes: make([]uint64, 0),
|
|
},
|
|
Deny: {
|
|
dirRes: make([]uint64, 0),
|
|
indRes: make([]uint64, 0),
|
|
},
|
|
}
|
|
)
|
|
|
|
// Extract all relevant rules (by op and resource) and group them by
|
|
// access and distance (rules for direct resources and rules for indirect resources)
|
|
for _, r := range set {
|
|
if r.Operation != op {
|
|
continue
|
|
}
|
|
|
|
if r.Resource == res {
|
|
// direct rules
|
|
rr[r.Access][dirRes] = append(rr[r.Access][dirRes], r.RoleID)
|
|
}
|
|
|
|
if matchResource(r.Resource, res) {
|
|
// rules on all resources of this type
|
|
rr[r.Access][indRes] = append(rr[r.Access][indRes], r.RoleID)
|
|
}
|
|
}
|
|
|
|
// Process all extracted roles and make sure that are filtered and ordered by relevance:
|
|
// 1. list of roles with denied operation directly on the resource
|
|
dRoles := slice.ToUint64BoolMap(rr[Deny][dirRes])
|
|
|
|
// 2. list of roles with allowed operation directly on the resource
|
|
aRoles := make(map[uint64]bool)
|
|
for _, r := range rr[Allow][dirRes] {
|
|
aRoles[r] = !dRoles[r]
|
|
}
|
|
|
|
// 3. list of roles with denied operation indirectly on the resource
|
|
for _, r := range rr[Deny][indRes] {
|
|
dRoles[r] = true
|
|
}
|
|
|
|
// 4. list of roles with allowed operation indirectly on the resource
|
|
for _, r := range rr[Allow][indRes] {
|
|
aRoles[r] = !dRoles[r]
|
|
}
|
|
|
|
for r, chk := range aRoles {
|
|
if chk {
|
|
aRR = append(aRR, r)
|
|
}
|
|
}
|
|
|
|
for r, chk := range dRoles {
|
|
if chk {
|
|
dRR = append(dRR, r)
|
|
}
|
|
}
|
|
|
|
// add bypass roles
|
|
for _, r := range auth.BypassRoles() {
|
|
if r == nil {
|
|
continue
|
|
}
|
|
|
|
aRR = append(aRR, r.ID)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func clear(set []*Rule) {
|
|
for _, r := range set {
|
|
r.dirty = false
|
|
}
|
|
}
|