Add filters to permissions for role route
It allows filtering for specific rules and also the rules which are applied to the resource, and not to a specific resource. Introduces generic methods for RuleSet and FindRules method to access_control generation template.
This commit is contained in:
@@ -276,6 +276,7 @@ endpoints:
|
||||
- Client ID
|
||||
- Session ID
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza-server/pkg/filter
|
||||
- github.com/cortezaproject/corteza-server/pkg/rbac
|
||||
apis:
|
||||
- name: list
|
||||
@@ -318,6 +319,15 @@ endpoints:
|
||||
type: uint64
|
||||
required: true
|
||||
title: Role ID
|
||||
get:
|
||||
- name: specific
|
||||
required: false
|
||||
title: Exclude (0, default), include (1) or return only (2) specific rules
|
||||
type: "filter.State"
|
||||
- name: resource
|
||||
type: "[]string"
|
||||
required: false
|
||||
title: Show only rules for a specific resource
|
||||
- name: delete
|
||||
path: "/{roleID}/rules"
|
||||
method: DELETE
|
||||
|
||||
@@ -2,11 +2,11 @@ package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/automation/rest/request"
|
||||
"github.com/cortezaproject/corteza-server/automation/service"
|
||||
"github.com/cortezaproject/corteza-server/automation/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/api"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ type (
|
||||
Trace(context.Context, uint64, []uint64, ...string) ([]*rbac.Trace, error)
|
||||
List() []map[string]string
|
||||
FindRulesByRoleID(context.Context, uint64) (rbac.RuleSet, error)
|
||||
FindRules(ctx context.Context, roleID uint64, specific filter.State, rr ...string) (rbac.RuleSet, error)
|
||||
Grant(ctx context.Context, rr ...*rbac.Rule) error
|
||||
}
|
||||
)
|
||||
@@ -43,7 +44,7 @@ func (ctrl Permissions) List(ctx context.Context, r *request.PermissionsList) (i
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Read(ctx context.Context, r *request.PermissionsRead) (interface{}, error) {
|
||||
return ctrl.ac.FindRulesByRoleID(ctx, r.RoleID)
|
||||
return ctrl.ac.FindRules(ctx, r.RoleID, r.Specific, r.Resource...)
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Delete(ctx context.Context, r *request.PermissionsDelete) (interface{}, error) {
|
||||
|
||||
@@ -11,6 +11,7 @@ package request
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -66,6 +67,16 @@ type (
|
||||
//
|
||||
// Role ID
|
||||
RoleID uint64 `json:",string"`
|
||||
|
||||
// Specific GET parameter
|
||||
//
|
||||
// Exclude (0, default), include (1) or return only (2) specific rules
|
||||
Specific filter.State
|
||||
|
||||
// Resource GET parameter
|
||||
//
|
||||
// Show only rules for a specific resource
|
||||
Resource []string
|
||||
}
|
||||
|
||||
PermissionsDelete struct {
|
||||
@@ -216,7 +227,9 @@ func NewPermissionsRead() *PermissionsRead {
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"roleID": r.RoleID,
|
||||
"roleID": r.RoleID,
|
||||
"specific": r.Specific,
|
||||
"resource": r.Resource,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,9 +238,42 @@ func (r PermissionsRead) GetRoleID() uint64 {
|
||||
return r.RoleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetSpecific() filter.State {
|
||||
return r.Specific
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetResource() []string {
|
||||
return r.Resource
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsRead) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["specific"]; ok && len(val) > 0 {
|
||||
r.Specific, err = payload.ParseFilterState(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["resource[]"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := tmp["resource"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
Generated
+58
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/automation/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
internalAuth "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/spf13/cast"
|
||||
@@ -259,6 +260,63 @@ func (svc accessControl) logGrants(ctx context.Context, rr []*rbac.Rule) {
|
||||
}
|
||||
}
|
||||
|
||||
// FindRules find all rules based on filters
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) FindRules(ctx context.Context, roleID uint64, specific filter.State, rr ...string) (out rbac.RuleSet, err error) {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return nil, AccessControlErrNotAllowedToSetPermissions()
|
||||
}
|
||||
|
||||
rules, err := svc.FindRulesByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
resources []rbac.Resource
|
||||
ruleMap = make(map[string]bool)
|
||||
uniqRuleID = func(r *rbac.Rule) string {
|
||||
return fmt.Sprintf("%s|%s|%d", r.Resource, r.Operation, r.RoleID)
|
||||
}
|
||||
)
|
||||
|
||||
// Filter based on resource
|
||||
if len(rr) > 0 {
|
||||
resources = make([]rbac.Resource, 0, len(rr))
|
||||
for _, r := range rr {
|
||||
if err = rbacResourceValidator(r); err != nil {
|
||||
return nil, fmt.Errorf("can not use resource %q: %w", r, err)
|
||||
}
|
||||
|
||||
resources = append(resources, rbac.NewResource(r))
|
||||
}
|
||||
} else {
|
||||
resources = svc.Resources()
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
for _, rule := range rules.FilterResource(res.RbacResource()) {
|
||||
if _, ok := ruleMap[uniqRuleID(rule)]; !ok {
|
||||
out = append(out, rule)
|
||||
ruleMap[uniqRuleID(rule)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter for Excluded, Include, or Exclusive specific rules
|
||||
switch specific {
|
||||
// Exclude all the specific rules
|
||||
case filter.StateExcluded:
|
||||
out = out.FilterRules(false)
|
||||
// Returns only all the specific rules
|
||||
case filter.StateExclusive:
|
||||
out = out.FilterRules(true)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindRulesByRoleID find all rules for a specific role
|
||||
//
|
||||
// This function is auto-generated
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
internalAuth "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
{{- range .imports }}
|
||||
@@ -204,6 +205,63 @@ func (svc accessControl) logGrants(ctx context.Context, rr []*rbac.Rule) {
|
||||
}
|
||||
}
|
||||
|
||||
// FindRules find all rules based on filters
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) FindRules(ctx context.Context, roleID uint64, specific filter.State, rr ...string) (out rbac.RuleSet, err error) {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return nil, AccessControlErrNotAllowedToSetPermissions()
|
||||
}
|
||||
|
||||
rules, err := svc.FindRulesByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
resources []rbac.Resource
|
||||
ruleMap = make(map[string]bool)
|
||||
uniqRuleID = func(r *rbac.Rule) string {
|
||||
return fmt.Sprintf("%s|%s|%d", r.Resource, r.Operation, r.RoleID)
|
||||
}
|
||||
)
|
||||
|
||||
// Filter based on resource
|
||||
if len(rr) > 0 {
|
||||
resources = make([]rbac.Resource, 0, len(rr))
|
||||
for _, r := range rr {
|
||||
if err = rbacResourceValidator(r); err != nil {
|
||||
return nil, fmt.Errorf("can not use resource %q: %w", r, err)
|
||||
}
|
||||
|
||||
resources = append(resources, rbac.NewResource(r))
|
||||
}
|
||||
} else {
|
||||
resources = svc.Resources()
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
for _, rule := range rules.FilterResource(res.RbacResource()) {
|
||||
if _, ok := ruleMap[uniqRuleID(rule)]; !ok {
|
||||
out = append(out, rule)
|
||||
ruleMap[uniqRuleID(rule)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter for Excluded, Include, or Exclusive specific rules
|
||||
switch specific {
|
||||
// Exclude all the specific rules
|
||||
case filter.StateExcluded:
|
||||
out = out.FilterRules(false)
|
||||
// Returns only all the specific rules
|
||||
case filter.StateExclusive:
|
||||
out = out.FilterRules(true)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindRulesByRoleID find all rules for a specific role
|
||||
//
|
||||
// This function is auto-generated
|
||||
|
||||
@@ -1291,6 +1291,7 @@ endpoints:
|
||||
- Client ID
|
||||
- Session ID
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza-server/pkg/filter
|
||||
- github.com/cortezaproject/corteza-server/pkg/rbac
|
||||
apis:
|
||||
- name: list
|
||||
@@ -1333,6 +1334,15 @@ endpoints:
|
||||
type: uint64
|
||||
required: true
|
||||
title: Role ID
|
||||
get:
|
||||
- name: specific
|
||||
required: false
|
||||
title: Exclude (0, default), include (1) or return only (2) specific rules
|
||||
type: "filter.State"
|
||||
- name: resource
|
||||
type: "[]string"
|
||||
required: false
|
||||
title: Show only rules for a specific resource
|
||||
- name: delete
|
||||
path: "/{roleID}/rules"
|
||||
method: DELETE
|
||||
|
||||
@@ -2,11 +2,11 @@ package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/rest/request"
|
||||
"github.com/cortezaproject/corteza-server/compose/service"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/api"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ type (
|
||||
Trace(context.Context, uint64, []uint64, ...string) ([]*rbac.Trace, error)
|
||||
List() []map[string]string
|
||||
FindRulesByRoleID(context.Context, uint64) (rbac.RuleSet, error)
|
||||
FindRules(ctx context.Context, roleID uint64, specific filter.State, rr ...string) (rbac.RuleSet, error)
|
||||
Grant(ctx context.Context, rr ...*rbac.Rule) error
|
||||
}
|
||||
)
|
||||
@@ -43,7 +44,7 @@ func (ctrl Permissions) List(ctx context.Context, r *request.PermissionsList) (i
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Read(ctx context.Context, r *request.PermissionsRead) (interface{}, error) {
|
||||
return ctrl.ac.FindRulesByRoleID(ctx, r.RoleID)
|
||||
return ctrl.ac.FindRules(ctx, r.RoleID, r.Specific, r.Resource...)
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Delete(ctx context.Context, r *request.PermissionsDelete) (interface{}, error) {
|
||||
|
||||
@@ -11,6 +11,7 @@ package request
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -66,6 +67,16 @@ type (
|
||||
//
|
||||
// Role ID
|
||||
RoleID uint64 `json:",string"`
|
||||
|
||||
// Specific GET parameter
|
||||
//
|
||||
// Exclude (0, default), include (1) or return only (2) specific rules
|
||||
Specific filter.State
|
||||
|
||||
// Resource GET parameter
|
||||
//
|
||||
// Show only rules for a specific resource
|
||||
Resource []string
|
||||
}
|
||||
|
||||
PermissionsDelete struct {
|
||||
@@ -216,7 +227,9 @@ func NewPermissionsRead() *PermissionsRead {
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"roleID": r.RoleID,
|
||||
"roleID": r.RoleID,
|
||||
"specific": r.Specific,
|
||||
"resource": r.Resource,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,9 +238,42 @@ func (r PermissionsRead) GetRoleID() uint64 {
|
||||
return r.RoleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetSpecific() filter.State {
|
||||
return r.Specific
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetResource() []string {
|
||||
return r.Resource
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsRead) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["specific"]; ok && len(val) > 0 {
|
||||
r.Specific, err = payload.ParseFilterState(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["resource[]"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := tmp["resource"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
Generated
+58
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
internalAuth "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/spf13/cast"
|
||||
@@ -369,6 +370,63 @@ func (svc accessControl) logGrants(ctx context.Context, rr []*rbac.Rule) {
|
||||
}
|
||||
}
|
||||
|
||||
// FindRules find all rules based on filters
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) FindRules(ctx context.Context, roleID uint64, specific filter.State, rr ...string) (out rbac.RuleSet, err error) {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return nil, AccessControlErrNotAllowedToSetPermissions()
|
||||
}
|
||||
|
||||
rules, err := svc.FindRulesByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
resources []rbac.Resource
|
||||
ruleMap = make(map[string]bool)
|
||||
uniqRuleID = func(r *rbac.Rule) string {
|
||||
return fmt.Sprintf("%s|%s|%d", r.Resource, r.Operation, r.RoleID)
|
||||
}
|
||||
)
|
||||
|
||||
// Filter based on resource
|
||||
if len(rr) > 0 {
|
||||
resources = make([]rbac.Resource, 0, len(rr))
|
||||
for _, r := range rr {
|
||||
if err = rbacResourceValidator(r); err != nil {
|
||||
return nil, fmt.Errorf("can not use resource %q: %w", r, err)
|
||||
}
|
||||
|
||||
resources = append(resources, rbac.NewResource(r))
|
||||
}
|
||||
} else {
|
||||
resources = svc.Resources()
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
for _, rule := range rules.FilterResource(res.RbacResource()) {
|
||||
if _, ok := ruleMap[uniqRuleID(rule)]; !ok {
|
||||
out = append(out, rule)
|
||||
ruleMap[uniqRuleID(rule)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter for Excluded, Include, or Exclusive specific rules
|
||||
switch specific {
|
||||
// Exclude all the specific rules
|
||||
case filter.StateExcluded:
|
||||
out = out.FilterRules(false)
|
||||
// Returns only all the specific rules
|
||||
case filter.StateExclusive:
|
||||
out = out.FilterRules(true)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindRulesByRoleID find all rules for a specific role
|
||||
//
|
||||
// This function is auto-generated
|
||||
|
||||
@@ -503,6 +503,7 @@ endpoints:
|
||||
- Client ID
|
||||
- Session ID
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza-server/pkg/filter
|
||||
- github.com/cortezaproject/corteza-server/pkg/rbac
|
||||
apis:
|
||||
- name: list
|
||||
@@ -545,6 +546,15 @@ endpoints:
|
||||
type: uint64
|
||||
required: true
|
||||
title: Role ID
|
||||
get:
|
||||
- name: specific
|
||||
required: false
|
||||
title: Exclude (0, default), include (1) or return only (2) specific rules
|
||||
type: "filter.State"
|
||||
- name: resource
|
||||
type: "[]string"
|
||||
required: false
|
||||
title: Show only rules for a specific resource
|
||||
- name: delete
|
||||
path: "/{roleID}/rules"
|
||||
method: DELETE
|
||||
|
||||
@@ -2,11 +2,11 @@ package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/federation/rest/request"
|
||||
"github.com/cortezaproject/corteza-server/federation/service"
|
||||
"github.com/cortezaproject/corteza-server/federation/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/api"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ type (
|
||||
Trace(context.Context, uint64, []uint64, ...string) ([]*rbac.Trace, error)
|
||||
List() []map[string]string
|
||||
FindRulesByRoleID(context.Context, uint64) (rbac.RuleSet, error)
|
||||
FindRules(ctx context.Context, roleID uint64, specific filter.State, rr ...string) (rbac.RuleSet, error)
|
||||
Grant(ctx context.Context, rr ...*rbac.Rule) error
|
||||
}
|
||||
)
|
||||
@@ -43,7 +44,7 @@ func (ctrl Permissions) List(ctx context.Context, r *request.PermissionsList) (i
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Read(ctx context.Context, r *request.PermissionsRead) (interface{}, error) {
|
||||
return ctrl.ac.FindRulesByRoleID(ctx, r.RoleID)
|
||||
return ctrl.ac.FindRules(ctx, r.RoleID, r.Specific, r.Resource...)
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Delete(ctx context.Context, r *request.PermissionsDelete) (interface{}, error) {
|
||||
|
||||
@@ -11,6 +11,7 @@ package request
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -66,6 +67,16 @@ type (
|
||||
//
|
||||
// Role ID
|
||||
RoleID uint64 `json:",string"`
|
||||
|
||||
// Specific GET parameter
|
||||
//
|
||||
// Exclude (0, default), include (1) or return only (2) specific rules
|
||||
Specific filter.State
|
||||
|
||||
// Resource GET parameter
|
||||
//
|
||||
// Show only rules for a specific resource
|
||||
Resource []string
|
||||
}
|
||||
|
||||
PermissionsDelete struct {
|
||||
@@ -216,7 +227,9 @@ func NewPermissionsRead() *PermissionsRead {
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"roleID": r.RoleID,
|
||||
"roleID": r.RoleID,
|
||||
"specific": r.Specific,
|
||||
"resource": r.Resource,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,9 +238,42 @@ func (r PermissionsRead) GetRoleID() uint64 {
|
||||
return r.RoleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetSpecific() filter.State {
|
||||
return r.Specific
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetResource() []string {
|
||||
return r.Resource
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsRead) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["specific"]; ok && len(val) > 0 {
|
||||
r.Specific, err = payload.ParseFilterState(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["resource[]"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := tmp["resource"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
Generated
+58
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/federation/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
internalAuth "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/spf13/cast"
|
||||
@@ -246,6 +247,63 @@ func (svc accessControl) logGrants(ctx context.Context, rr []*rbac.Rule) {
|
||||
}
|
||||
}
|
||||
|
||||
// FindRules find all rules based on filters
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) FindRules(ctx context.Context, roleID uint64, specific filter.State, rr ...string) (out rbac.RuleSet, err error) {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return nil, AccessControlErrNotAllowedToSetPermissions()
|
||||
}
|
||||
|
||||
rules, err := svc.FindRulesByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
resources []rbac.Resource
|
||||
ruleMap = make(map[string]bool)
|
||||
uniqRuleID = func(r *rbac.Rule) string {
|
||||
return fmt.Sprintf("%s|%s|%d", r.Resource, r.Operation, r.RoleID)
|
||||
}
|
||||
)
|
||||
|
||||
// Filter based on resource
|
||||
if len(rr) > 0 {
|
||||
resources = make([]rbac.Resource, 0, len(rr))
|
||||
for _, r := range rr {
|
||||
if err = rbacResourceValidator(r); err != nil {
|
||||
return nil, fmt.Errorf("can not use resource %q: %w", r, err)
|
||||
}
|
||||
|
||||
resources = append(resources, rbac.NewResource(r))
|
||||
}
|
||||
} else {
|
||||
resources = svc.Resources()
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
for _, rule := range rules.FilterResource(res.RbacResource()) {
|
||||
if _, ok := ruleMap[uniqRuleID(rule)]; !ok {
|
||||
out = append(out, rule)
|
||||
ruleMap[uniqRuleID(rule)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter for Excluded, Include, or Exclusive specific rules
|
||||
switch specific {
|
||||
// Exclude all the specific rules
|
||||
case filter.StateExcluded:
|
||||
out = out.FilterRules(false)
|
||||
// Returns only all the specific rules
|
||||
case filter.StateExclusive:
|
||||
out = out.FilterRules(true)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindRulesByRoleID find all rules for a specific role
|
||||
//
|
||||
// This function is auto-generated
|
||||
|
||||
@@ -220,6 +220,8 @@ func (d *restEndpointParamDef) Parser(arg string) string {
|
||||
return fmt.Sprintf("payload.Parse%s(%s), nil", export(d.Type), arg)
|
||||
case "string", "[]string":
|
||||
return fmt.Sprintf("%s, nil", arg)
|
||||
case "filter.State":
|
||||
return fmt.Sprintf("payload.ParseFilterState(%s), nil", arg)
|
||||
default:
|
||||
return fmt.Sprintf("%s(%s), nil", d.Type, arg)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package payload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/jmoiron/sqlx/types"
|
||||
"github.com/spf13/cast"
|
||||
"strconv"
|
||||
@@ -100,3 +101,7 @@ func ParseInt64(s string) int64 {
|
||||
func ParseBool(s string) bool {
|
||||
return cast.ToBool(s)
|
||||
}
|
||||
|
||||
func ParseFilterState(s string) filter.State {
|
||||
return filter.State(cast.ToUint(s))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package payload
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseFilterState(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected filter.State
|
||||
}{
|
||||
{
|
||||
"invalid string should be Excluded",
|
||||
"zero",
|
||||
filter.StateExcluded,
|
||||
},
|
||||
{
|
||||
"empty string should be Excluded",
|
||||
"0",
|
||||
filter.StateExcluded,
|
||||
},
|
||||
{
|
||||
"Excluded",
|
||||
"0",
|
||||
filter.StateExcluded,
|
||||
},
|
||||
{
|
||||
"Excluded",
|
||||
"1",
|
||||
filter.StateInclusive,
|
||||
},
|
||||
{
|
||||
"Excluded",
|
||||
"2",
|
||||
filter.StateExclusive,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req.Equal(tt.expected, ParseFilterState(tt.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -102,3 +102,29 @@ func level(r string) (score int) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// isSpecific will return true if rule relates to a specific resource
|
||||
//
|
||||
// ns::cmp:res/xx/* returns true
|
||||
// ns::cmp:res/xx/xx returns true
|
||||
// ns::cmp:res/ returns false
|
||||
// ns::cmp:res/*/* returns false
|
||||
func isSpecific(r string) (out bool) {
|
||||
out = false
|
||||
parts := strings.Split(r, pathSep)
|
||||
// remove resource name part
|
||||
parts = parts[1:]
|
||||
|
||||
// check parts for wildcard if it's not empty otherwise return false
|
||||
for _, p := range parts {
|
||||
if len(p) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if p != wildcard && !out {
|
||||
out = true
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -115,3 +115,27 @@ func TestLevel(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSpecific(t *testing.T) {
|
||||
var (
|
||||
tcc = []struct {
|
||||
r string
|
||||
e bool
|
||||
}{
|
||||
{"corteza::test/a/b/c", true},
|
||||
{"corteza::test/a/b/*", true},
|
||||
{"corteza::test/a/*/*", true},
|
||||
{"corteza::test/a/*/123", true},
|
||||
{"corteza::test/*/*/*", false},
|
||||
{"corteza::test/*/*", false},
|
||||
{"corteza::test/*", false},
|
||||
{"corteza::test/", false},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.r, func(t *testing.T) {
|
||||
require.Equal(t, tc.e, isSpecific(tc.r))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -62,7 +62,7 @@ func (set RuleSet) FilterAccess(a Access) (out RuleSet) {
|
||||
|
||||
func (set RuleSet) FilterResource(res string) (out RuleSet) {
|
||||
for _, r := range set {
|
||||
if !matchResource(r.Resource, res) {
|
||||
if !matchResource(res, r.Resource) {
|
||||
continue
|
||||
}
|
||||
out = append(out, r)
|
||||
@@ -71,6 +71,18 @@ func (set RuleSet) FilterResource(res string) (out RuleSet) {
|
||||
return
|
||||
}
|
||||
|
||||
// FilterRules will filter the rules based on given parameter(specific),
|
||||
// If params is true then it will return only the specific rules otherwise it will return non-specific rules
|
||||
func (set RuleSet) FilterRules(specific bool) (out RuleSet) {
|
||||
for _, r := range set {
|
||||
if specific == isSpecific(r.Resource) {
|
||||
out = append(out, r)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AllowRule helper func to create allow rule
|
||||
func AllowRule(id uint64, r, o string) *Rule {
|
||||
return &Rule{id, r, o, Allow, false}
|
||||
|
||||
@@ -1219,6 +1219,7 @@ endpoints:
|
||||
- Client ID
|
||||
- Session ID
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza-server/pkg/filter
|
||||
- github.com/cortezaproject/corteza-server/pkg/rbac
|
||||
apis:
|
||||
- name: list
|
||||
@@ -1262,6 +1263,15 @@ endpoints:
|
||||
type: uint64
|
||||
required: true
|
||||
title: Role ID
|
||||
get:
|
||||
- name: specific
|
||||
required: false
|
||||
title: Exclude (0, default), include (1) or return only (2) specific rules
|
||||
type: "filter.State"
|
||||
- name: resource
|
||||
type: "[]string"
|
||||
required: false
|
||||
title: Show only rules for a specific resource
|
||||
- name: delete
|
||||
path: "/{roleID}/rules"
|
||||
method: DELETE
|
||||
|
||||
@@ -2,8 +2,8 @@ package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/api"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza-server/system/rest/request"
|
||||
"github.com/cortezaproject/corteza-server/system/service"
|
||||
@@ -20,6 +20,7 @@ type (
|
||||
Trace(context.Context, uint64, []uint64, ...string) ([]*rbac.Trace, error)
|
||||
List() []map[string]string
|
||||
FindRulesByRoleID(context.Context, uint64) (rbac.RuleSet, error)
|
||||
FindRules(ctx context.Context, roleID uint64, specific filter.State, rr ...string) (rbac.RuleSet, error)
|
||||
Grant(ctx context.Context, rr ...*rbac.Rule) error
|
||||
}
|
||||
)
|
||||
@@ -43,7 +44,7 @@ func (ctrl Permissions) List(ctx context.Context, r *request.PermissionsList) (i
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Read(ctx context.Context, r *request.PermissionsRead) (interface{}, error) {
|
||||
return ctrl.ac.FindRulesByRoleID(ctx, r.RoleID)
|
||||
return ctrl.ac.FindRules(ctx, r.RoleID, r.Specific, r.Resource...)
|
||||
}
|
||||
|
||||
func (ctrl Permissions) Delete(ctx context.Context, r *request.PermissionsDelete) (interface{}, error) {
|
||||
|
||||
@@ -11,6 +11,7 @@ package request
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -66,6 +67,16 @@ type (
|
||||
//
|
||||
// Role ID
|
||||
RoleID uint64 `json:",string"`
|
||||
|
||||
// Specific GET parameter
|
||||
//
|
||||
// Exclude (0, default), include (1) or return only (2) specific rules
|
||||
Specific filter.State
|
||||
|
||||
// Resource GET parameter
|
||||
//
|
||||
// Show only rules for a specific resource
|
||||
Resource []string
|
||||
}
|
||||
|
||||
PermissionsDelete struct {
|
||||
@@ -216,7 +227,9 @@ func NewPermissionsRead() *PermissionsRead {
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"roleID": r.RoleID,
|
||||
"roleID": r.RoleID,
|
||||
"specific": r.Specific,
|
||||
"resource": r.Resource,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,9 +238,43 @@ func (r PermissionsRead) GetRoleID() uint64 {
|
||||
return r.RoleID
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetSpecific() filter.State {
|
||||
return r.Specific
|
||||
}
|
||||
|
||||
// Auditable returns all auditable/loggable parameters
|
||||
func (r PermissionsRead) GetResource() []string {
|
||||
return r.Resource
|
||||
}
|
||||
|
||||
// Fill processes request and fills internal variables
|
||||
func (r *PermissionsRead) Fill(req *http.Request) (err error) {
|
||||
|
||||
{
|
||||
// GET params
|
||||
tmp := req.URL.Query()
|
||||
|
||||
if val, ok := tmp["specific"]; ok && len(val) > 0 {
|
||||
|
||||
r.Specific, err = payload.ParseFilterState(val[0]), nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val, ok := tmp["resource[]"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if val, ok := tmp["resource"]; ok {
|
||||
r.Resource, err = val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var val string
|
||||
// path params
|
||||
|
||||
Generated
+58
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
internalAuth "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
@@ -576,6 +577,63 @@ func (svc accessControl) logGrants(ctx context.Context, rr []*rbac.Rule) {
|
||||
}
|
||||
}
|
||||
|
||||
// FindRules find all rules based on filters
|
||||
//
|
||||
// This function is auto-generated
|
||||
func (svc accessControl) FindRules(ctx context.Context, roleID uint64, specific filter.State, rr ...string) (out rbac.RuleSet, err error) {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return nil, AccessControlErrNotAllowedToSetPermissions()
|
||||
}
|
||||
|
||||
rules, err := svc.FindRulesByRoleID(ctx, roleID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
resources []rbac.Resource
|
||||
ruleMap = make(map[string]bool)
|
||||
uniqRuleID = func(r *rbac.Rule) string {
|
||||
return fmt.Sprintf("%s|%s|%d", r.Resource, r.Operation, r.RoleID)
|
||||
}
|
||||
)
|
||||
|
||||
// Filter based on resource
|
||||
if len(rr) > 0 {
|
||||
resources = make([]rbac.Resource, 0, len(rr))
|
||||
for _, r := range rr {
|
||||
if err = rbacResourceValidator(r); err != nil {
|
||||
return nil, fmt.Errorf("can not use resource %q: %w", r, err)
|
||||
}
|
||||
|
||||
resources = append(resources, rbac.NewResource(r))
|
||||
}
|
||||
} else {
|
||||
resources = svc.Resources()
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
for _, rule := range rules.FilterResource(res.RbacResource()) {
|
||||
if _, ok := ruleMap[uniqRuleID(rule)]; !ok {
|
||||
out = append(out, rule)
|
||||
ruleMap[uniqRuleID(rule)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter for Excluded, Include, or Exclusive specific rules
|
||||
switch specific {
|
||||
// Exclude all the specific rules
|
||||
case filter.StateExcluded:
|
||||
out = out.FilterRules(false)
|
||||
// Returns only all the specific rules
|
||||
case filter.StateExclusive:
|
||||
out = out.FilterRules(true)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindRulesByRoleID find all rules for a specific role
|
||||
//
|
||||
// This function is auto-generated
|
||||
|
||||
@@ -2,6 +2,7 @@ package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/id"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
@@ -30,7 +31,7 @@ func TestPermissionsList(t *testing.T) {
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
|
||||
h.apiInit().
|
||||
json := h.apiInit().
|
||||
Get("/permissions/").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
@@ -38,6 +39,8 @@ func TestPermissionsList(t *testing.T) {
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(fmt.Sprintf(`$.response[? @.type=="%s"]`, types.ComponentResourceType))).
|
||||
End()
|
||||
|
||||
fmt.Println("json: ", json.Response.Body)
|
||||
}
|
||||
|
||||
func TestPermissionsRead(t *testing.T) {
|
||||
@@ -54,6 +57,63 @@ func TestPermissionsRead(t *testing.T) {
|
||||
End()
|
||||
}
|
||||
|
||||
func TestPermissionsReadWithFilter(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
helpers.DenyMe(h, types.ComponentRbacResource(), "user.create")
|
||||
|
||||
// Specific resource related rules
|
||||
testID := id.Next()
|
||||
helpers.AllowMe(h, types.UserRbacResource(testID), "read")
|
||||
helpers.AllowMe(h, types.UserRbacResource(testID), "update")
|
||||
|
||||
// Only non-specific resource rules with `specific: 0` filter
|
||||
h.apiInit().
|
||||
Getf("/permissions/%d/rules", h.roleID).
|
||||
Query("specific", "0").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response`, 2)).
|
||||
End()
|
||||
|
||||
// Including all specific and non-specific resource rules with `specific: 1` filter
|
||||
h.apiInit().
|
||||
Getf("/permissions/%d/rules", h.roleID).
|
||||
Query("specific", "1").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response`, 4)).
|
||||
End()
|
||||
|
||||
// Only specific resource rules with `specific: 2` filter
|
||||
h.apiInit().
|
||||
Getf("/permissions/%d/rules", h.roleID).
|
||||
Query("specific", "2").
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response`, 2)).
|
||||
End()
|
||||
|
||||
// Only resource related rules with `resource: corteza::system:user/{ID}`
|
||||
h.apiInit().
|
||||
Getf("/permissions/%d/rules", h.roleID).
|
||||
Query("specific", "1").
|
||||
Query("resource", fmt.Sprintf("corteza::system:user/%d", testID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Len(`$.response`, 2)).
|
||||
End()
|
||||
}
|
||||
|
||||
func TestPermissionsUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
helpers.AllowMe(h, types.ComponentRbacResource(), "grant")
|
||||
|
||||
Reference in New Issue
Block a user