grafana/pkg/services/accesscontrol/database/policies.go

143 lines
3.5 KiB
Go

package database
import (
"context"
"fmt"
"strings"
"time"
"github.com/grafana/grafana/pkg/kinds/accesspolicy"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/sqlstore/session"
"github.com/grafana/grafana/pkg/util"
)
func GetAccessPolicies(ctx context.Context, orgID int64, sql *session.SessionDB, resolver accesscontrol.ScopeAttributeResolverFunc) ([]accesspolicy.Resource, error) {
type permissionInfo struct {
RoleUID string
RoleName string
Scope string
Action string
Created time.Time
Updated time.Time
}
info := &permissionInfo{}
policies := make([]accesspolicy.Resource, 0)
current := &accesspolicy.Resource{}
prevKey := ""
rows, err := sql.Query(ctx, `SELECT
role.uid as role_uid,
role.name as role_name,
scope,
action,
permission.created,
permission.updated
FROM permission
JOIN role ON permission.role_id = role.id
WHERE org_id=?
ORDER BY role.id ASC, scope ASC, action ASC`, orgID)
if err != nil {
return nil, err
}
created := time.Now()
updated := time.Now()
for rows.Next() {
err = rows.Scan(
&info.RoleUID,
&info.RoleName,
&info.Scope,
&info.Action,
&info.Created,
&info.Updated,
)
if err != nil {
return policies, err
}
key := info.RoleUID + "/" + info.Scope
if key != prevKey {
created = info.Created
updated = info.Updated
if len(current.Spec.Rules) > 0 {
current.Spec.Rules = accesspolicy.ReduceRules(current.Spec.Rules)
policies = append(policies, *current)
}
scope, err := resolver(ctx, orgID, info.Scope)
if err != nil {
return policies, err
}
if len(scope) != 3 {
return policies, fmt.Errorf("expected three part scope")
}
current = &accesspolicy.Resource{
Metadata: accesspolicy.Metadata{
CreationTimestamp: created,
UpdateTimestamp: updated,
},
Spec: accesspolicy.Spec{
Role: accesspolicy.RoleRef{
Kind: accesspolicy.RoleRefKindRole,
Name: info.RoleUID,
Xname: info.RoleName,
},
Scope: accesspolicy.ResourceRef{
Kind: scope[0],
Name: scope[2],
},
Rules: make([]accesspolicy.AccessRule, 0),
},
}
// When the value is not a UID, set the prefix to $ -- an invalid name
if scope[1] != "uid" {
current.Spec.Scope.Name = fmt.Sprintf("$%s:%s", scope[1], scope[2])
}
// Skip role+role binding for direct users
if strings.HasPrefix(info.RoleName, "managed:users:") {
current.Spec.Role.Kind = accesspolicy.RoleRefKindUser
current.Spec.Role.Name = "$TODO:" + info.RoleName
}
prevKey = key
}
if info.Created.Before(created) {
created = info.Created
current.Metadata.CreationTimestamp = created
}
if info.Updated.After(updated) {
updated = info.Updated
current.Metadata.UpdateTimestamp = updated
}
action := strings.Split(info.Action, ":")
if len(action) != 2 {
return policies, fmt.Errorf("expected two part action")
}
parts := strings.SplitN(action[0], ".", 2)
rule := accesspolicy.AccessRule{
Verb: action[1],
Kind: parts[0],
}
if len(parts) > 1 {
rule.Target = util.Pointer(parts[1])
}
// // When the scope is dashboards or teams
// // ... hymmm ... this would imply permissions
// if rule.Kind == current.Spec.Scope.Kind {
// rule.Kind = "*"
// }
current.Spec.Rules = append(current.Spec.Rules, rule)
}
if current.Spec.Scope.Name != "" {
current.Spec.Rules = accesspolicy.ReduceRules(current.Spec.Rules)
policies = append(policies, *current)
}
return policies, err
}