Add ability to precheck (in db query) access on resources

This commit is contained in:
Denis Arh
2019-08-14 17:19:10 +02:00
parent bf8deb9e0c
commit 69602148dc
3 changed files with 119 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
package permissions
import (
"fmt"
"strings"
)
type (
AccessCheck struct {
prefix string
pkColName string
resource Resource
operation Operation
roles []uint64
checkExplicitDeny bool
}
)
func InitAccessCheckFilter(operation Operation, roles []uint64, checkExplicitDeny bool) AccessCheck {
var ac = AccessCheck{
operation: operation,
roles: roles,
checkExplicitDeny: checkExplicitDeny,
pkColName: "id",
}
return ac
}
func (ac *AccessCheck) BindToEnv(resource Resource, prefix string) *AccessCheck {
ac.resource = resource
ac.prefix = prefix
return ac
}
func (ac *AccessCheck) SetPrimaryKeyName(col string) *AccessCheck {
ac.pkColName = col
return ac
}
// ToSql converts access check to SQL (with args) that will help with filtering
//
// Satisfies squirrel.Sqlizer interface
func (ac AccessCheck) ToSql() (sql string, args []interface{}, err error) {
if len(ac.roles) == 0 {
sql = "false"
return
}
sql = fmt.Sprintf(
`EXISTS (SELECT 1
FROM %s_permission_rules
WHERE resource = CONCAT(?, %s)
AND operation = ?
AND access = ?
AND rel_role IN (%s))`,
ac.prefix,
ac.pkColName,
// Generate placeholder for every role we have
strings.Repeat(",?", len(ac.roles))[1:],
)
args = []interface{}{
ac.resource,
ac.operation,
}
if ac.checkExplicitDeny {
// User has permissions to read on wildcard (*) resource
// so we need to check if there is any explicit denies
args = append(args, Deny)
sql = fmt.Sprintf("NOT %s", sql)
} else {
// User is explicitly denied to read on wildcard (*) resource
// check for all that have explicit allow
args = append(args, Allow)
}
for _, roleID := range ac.roles {
args = append(args, roleID)
}
return sql, args, nil
}
+11
View File
@@ -42,6 +42,17 @@ func (a Access) String() string {
}
}
// Bool convers boolean true to Allow and false to Deny
func BoolToCheckFunc(isTrue bool) CheckAccessFunc {
return func() Access {
if isTrue {
return Allow
}
return Deny
}
}
func (a *Access) UnmarshalJSON(data []byte) error {
switch string(data) {
case "allow":
@@ -116,6 +116,26 @@ func TestRuleSet_checkResource(t *testing.T) {
opAccess,
Deny,
},
{ // deny wc and and check if wc is denied
RuleSet{
DenyRule(role1, resThingWc, opAccess),
AllowRule(role1, resThing42, opAccess),
},
[]uint64{role1},
resThingWc,
opAccess,
Deny,
},
{ // allow wc and and check if wc is allowed
RuleSet{
AllowRule(role1, resThingWc, opAccess),
DenyRule(role1, resThing42, opAccess),
},
[]uint64{role1},
resThingWc,
opAccess,
Allow,
},
}
)