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
117 lines
3.2 KiB
Go
117 lines
3.2 KiB
Go
package rbac
|
|
|
|
// function checks all given rules
|
|
//
|
|
// - indexRules are rules optimized for quick lookup.
|
|
// The trie data structure approach is used to optimize lookups and reduce
|
|
// memory consumption.
|
|
//
|
|
// - rolesByKind are roles optimized for quick lookup
|
|
// roles are grouped by kind and each kind contains fast-lookup (map[role-id]bool)
|
|
//
|
|
// - op and res represent operation and resource that are checked
|
|
//
|
|
// - trace is optional; when not nil, function will update trace struct
|
|
// with information as it traverses and checks the rules
|
|
func check(indexedRules *ruleIndex, rolesByKind partRoles, op, res string, trace *Trace) Access {
|
|
baseTraceInfo(trace, res, op, rolesByKind)
|
|
|
|
if member(rolesByKind, AnonymousRole) && len(rolesByKind) > 1 {
|
|
// Integrity check; when user is member of anonymous role
|
|
// should not be member of any other type of role
|
|
return resolve(trace, Deny, failedIntegrityCheck)
|
|
}
|
|
|
|
if member(rolesByKind, BypassRole) {
|
|
// if user has at least one bypass role, we allow access
|
|
return resolve(trace, Allow, bypassRoleMembership)
|
|
}
|
|
|
|
if indexedRules.empty() {
|
|
// no rules to check
|
|
return resolve(trace, Inherit, noRules)
|
|
}
|
|
|
|
var (
|
|
match *Rule
|
|
allowed bool
|
|
)
|
|
|
|
//
|
|
if trace != nil {
|
|
// from this point on, there is a chance trace (if set)
|
|
// will contain some rules.
|
|
//
|
|
// Stable order needs to be ensured: there is no production
|
|
// code that relies on that but tests might fail and API
|
|
// response would be flaky.
|
|
defer sortTraceRules(trace)
|
|
}
|
|
|
|
// Priority is important here. We want to have
|
|
// stable RBAC check behaviour and ability
|
|
// to override allow/deny depending on how niche the role (type) is:
|
|
// - context (eg owners) are more niche than common
|
|
// - rules for common roles are more important than authenticated and anonymous role types
|
|
//
|
|
// Note that bypass roles are intentionally ignored here; if user is member of
|
|
// bypass role there is no need to check any other rule
|
|
for _, kind := range []roleKind{ContextRole, CommonRole, AuthenticatedRole, AnonymousRole} {
|
|
// not a member of any role of this kind
|
|
if len(rolesByKind[kind]) == 0 {
|
|
continue
|
|
}
|
|
|
|
// reset allowed to false
|
|
// for each role kind
|
|
allowed = false
|
|
|
|
for r := range rolesByKind[kind] {
|
|
match = indexedRules.matchingRule(r, op, res)
|
|
|
|
// check all rules for each role the security-context
|
|
if match == nil {
|
|
// no rules match
|
|
continue
|
|
}
|
|
|
|
if trace != nil {
|
|
// if trace is enabled, append
|
|
// each matching rule
|
|
trace.Rules = append(trace.Rules, match)
|
|
}
|
|
|
|
if match.Access == Deny {
|
|
// if we stumble upon Deny we short-circuit the check
|
|
return resolve(trace, Deny, "")
|
|
}
|
|
|
|
if match.Access == Allow {
|
|
// allow rule found, we need to check rules on other roles
|
|
// before we allow it
|
|
allowed = true
|
|
}
|
|
}
|
|
|
|
if allowed {
|
|
// at least one of the roles (per role type) in the security context
|
|
// allows operation on a resource
|
|
return resolve(trace, Allow, "")
|
|
}
|
|
}
|
|
|
|
// No rule matched
|
|
return resolve(trace, Inherit, noMatch)
|
|
}
|
|
|
|
// at least one of the roles must be set to true
|
|
func member(r partRoles, k roleKind) bool {
|
|
for _, is := range r[k] {
|
|
if is {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|