2026-04-06 15:48:37 +05:30
|
|
|
// Copyright IBM Corp. 2020, 2026
|
2021-05-04 19:57:35 -05:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-02-02 17:15:17 -08:00
|
|
|
|
2021-05-04 19:57:35 -05:00
|
|
|
package perms
|
|
|
|
|
|
|
|
|
|
import (
|
2023-06-06 16:13:44 -04:00
|
|
|
"context"
|
2021-05-04 19:57:35 -05:00
|
|
|
"testing"
|
|
|
|
|
|
2026-10-12 10:33:41 -07:00
|
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/globals"
|
|
|
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/types/action"
|
|
|
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/types/resource"
|
|
|
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/types/scope"
|
2021-05-04 19:57:35 -05:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func Test_OutputFields(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
type input struct {
|
|
|
|
|
name string
|
|
|
|
|
fields []string
|
2023-01-06 17:27:19 -06:00
|
|
|
startMap *OutputFields
|
|
|
|
|
resMap *OutputFields
|
2021-05-04 19:57:35 -05:00
|
|
|
resStar bool
|
|
|
|
|
}
|
|
|
|
|
tests := []input{
|
|
|
|
|
{
|
2023-01-06 17:27:19 -06:00
|
|
|
name: "nil map, add nil",
|
|
|
|
|
resMap: &OutputFields{},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "nil map, add empty fields",
|
|
|
|
|
fields: []string{},
|
|
|
|
|
resMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{},
|
|
|
|
|
},
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "nil map, add fields",
|
|
|
|
|
fields: []string{"id", "version"},
|
2023-01-06 17:27:19 -06:00
|
|
|
resMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"id": true,
|
|
|
|
|
"version": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
{
|
2023-01-06 17:27:19 -06:00
|
|
|
name: "existing map, add nil",
|
|
|
|
|
startMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"id": true,
|
|
|
|
|
"version": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
resMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"id": true,
|
|
|
|
|
"version": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
{
|
2023-01-06 17:27:19 -06:00
|
|
|
name: "existing with star, add nil",
|
|
|
|
|
startMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"*": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
resMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"*": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
resStar: true,
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
{
|
2023-01-06 17:27:19 -06:00
|
|
|
name: "existing with star, add new",
|
|
|
|
|
fields: []string{"id", "version"},
|
|
|
|
|
startMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"*": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
resMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"*": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
resStar: true,
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
{
|
2023-01-06 17:27:19 -06:00
|
|
|
name: "existing without star, add new",
|
|
|
|
|
fields: []string{"id", "version"},
|
|
|
|
|
startMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"name": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
resMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"id": true,
|
|
|
|
|
"version": true,
|
|
|
|
|
"name": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
{
|
2023-01-06 17:27:19 -06:00
|
|
|
name: "existing without star, add star",
|
|
|
|
|
fields: []string{"id", "*"},
|
|
|
|
|
startMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"name": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
resMap: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"*": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
resStar: true,
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
|
out := test.startMap.AddFields(test.fields)
|
2023-01-06 17:27:19 -06:00
|
|
|
assert.True(out.Has("*") == test.resStar)
|
2021-05-04 19:57:35 -05:00
|
|
|
assert.Equal(test.resMap, out)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Test_ACLOutputFields(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
2023-06-06 16:13:44 -04:00
|
|
|
ctx := context.Background()
|
|
|
|
|
|
2021-05-04 19:57:35 -05:00
|
|
|
type input struct {
|
|
|
|
|
name string
|
|
|
|
|
grants []string
|
|
|
|
|
resource Resource
|
|
|
|
|
action action.Type
|
|
|
|
|
fields []string
|
|
|
|
|
authorized bool
|
|
|
|
|
}
|
|
|
|
|
tests := []input{
|
|
|
|
|
{
|
|
|
|
|
name: "default",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
action: action.Read,
|
2024-01-17 23:54:49 -08:00
|
|
|
grants: []string{"ids=u_bar;actions=read,update"},
|
2021-05-04 19:57:35 -05:00
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "single value",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2024-01-17 23:54:49 -08:00
|
|
|
grants: []string{"ids=u_bar;actions=read,update;output_fields=id"},
|
2021-05-04 19:57:35 -05:00
|
|
|
action: action.Read,
|
|
|
|
|
fields: []string{"id"},
|
|
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "compound no overlap",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
grants: []string{
|
2024-01-17 23:54:49 -08:00
|
|
|
"ids=u_bar;actions=read,update;output_fields=id",
|
|
|
|
|
"ids=*;type=host-catalog;actions=read,update;output_fields=version",
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
action: action.Read,
|
|
|
|
|
fields: []string{"id"},
|
|
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "compound",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
grants: []string{
|
2024-01-17 23:54:49 -08:00
|
|
|
"ids=u_bar;actions=read,update;output_fields=id",
|
|
|
|
|
"ids=*;type=role;output_fields=version",
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
action: action.Read,
|
|
|
|
|
fields: []string{"id", "version"},
|
|
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "wildcard with type",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
grants: []string{
|
2024-01-17 23:54:49 -08:00
|
|
|
"ids=u_bar;actions=read,update;output_fields=read",
|
|
|
|
|
"ids=*;type=role;output_fields=*",
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
action: action.Read,
|
|
|
|
|
fields: []string{"*"},
|
|
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "wildcard with wildcard type",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
grants: []string{
|
2024-01-17 23:54:49 -08:00
|
|
|
"ids=u_bar;actions=read,update;output_fields=read",
|
|
|
|
|
"ids=*;type=*;output_fields=*",
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
action: action.Read,
|
|
|
|
|
fields: []string{"*"},
|
|
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "subaction exact",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
grants: []string{
|
2024-01-17 23:54:49 -08:00
|
|
|
"ids=u_bar;actions=read:self,update;output_fields=version",
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
action: action.ReadSelf,
|
|
|
|
|
fields: []string{"version"},
|
|
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// If the action is a subaction, parent output fields will apply, in
|
|
|
|
|
// addition to subaction. This matches authorization.
|
|
|
|
|
name: "subaction parent action",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
grants: []string{
|
2024-01-17 23:54:49 -08:00
|
|
|
"ids=u_bar;actions=read,update;output_fields=version",
|
|
|
|
|
"ids=u_bar;actions=read:self;output_fields=id",
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
action: action.ReadSelf,
|
|
|
|
|
fields: []string{"id", "version"},
|
|
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// The inverse isn't true. Similarly to authorization, if you have
|
|
|
|
|
// specific output fields on a self action, they don't apply to
|
|
|
|
|
// non-self actions. This is useful to allow more visibility to self
|
|
|
|
|
// actions and less in the general case.
|
|
|
|
|
name: "subaction child action",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
grants: []string{
|
2024-01-17 23:54:49 -08:00
|
|
|
"ids=u_bar;actions=read:self,update;output_fields=version",
|
|
|
|
|
"ids=u_bar;actions=read;output_fields=id",
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
action: action.Read,
|
|
|
|
|
fields: []string{"id"},
|
|
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "initial grant unauthorized with star",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
grants: []string{
|
2024-01-17 23:54:49 -08:00
|
|
|
"ids=u_bar;output_fields=*",
|
|
|
|
|
"ids=u_bar;actions=delete;output_fields=id",
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
action: action.Delete,
|
|
|
|
|
fields: []string{"*"},
|
|
|
|
|
authorized: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
2021-09-02 12:54:02 -04:00
|
|
|
name: "unauthorized id only",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Id: "u_bar", Type: resource.Role},
|
2021-05-04 19:57:35 -05:00
|
|
|
grants: []string{
|
2024-01-17 23:54:49 -08:00
|
|
|
"ids=u_bar;output_fields=name",
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
action: action.Delete,
|
|
|
|
|
fields: []string{"name"},
|
|
|
|
|
},
|
2021-09-02 12:54:02 -04:00
|
|
|
{
|
|
|
|
|
name: "unauthorized type only",
|
2024-09-17 10:07:43 -04:00
|
|
|
resource: Resource{ScopeId: "o_myorg", ParentScopeId: scope.Global.String(), Type: resource.Role},
|
2021-09-02 12:54:02 -04:00
|
|
|
grants: []string{
|
|
|
|
|
"type=role;output_fields=name",
|
|
|
|
|
},
|
|
|
|
|
action: action.List,
|
|
|
|
|
fields: []string{"name"},
|
|
|
|
|
},
|
2021-05-04 19:57:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
|
var grants []Grant
|
|
|
|
|
for _, g := range test.grants {
|
2024-09-17 10:07:43 -04:00
|
|
|
grant, err := Parse(ctx, GrantTuple{RoleScopeId: "o_myorg", GrantScopeId: "o_myorg", Grant: g})
|
2021-05-04 19:57:35 -05:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
grants = append(grants, grant)
|
|
|
|
|
}
|
|
|
|
|
acl := NewACL(grants...)
|
2022-10-28 15:52:06 -04:00
|
|
|
results := acl.Allowed(test.resource, test.action, globals.AnonymousUserId, WithSkipAnonymousUserRestrictions(true))
|
2023-01-06 17:27:19 -06:00
|
|
|
fields, _ := results.OutputFields.Fields()
|
|
|
|
|
assert.ElementsMatch(t, fields, test.fields)
|
2021-05-04 19:57:35 -05:00
|
|
|
assert.True(t, test.authorized == results.Authorized)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Test_ACLSelfOrDefault(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
type input struct {
|
|
|
|
|
name string
|
2023-01-06 17:27:19 -06:00
|
|
|
input *OutputFields
|
|
|
|
|
output *OutputFields
|
2021-05-04 19:57:35 -05:00
|
|
|
userId string
|
|
|
|
|
}
|
|
|
|
|
tests := []input{
|
|
|
|
|
{
|
2023-01-06 17:27:19 -06:00
|
|
|
name: "nil, no user ID, set but empty",
|
|
|
|
|
output: &OutputFields{
|
|
|
|
|
fields: map[string]bool{},
|
|
|
|
|
},
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
{
|
2023-01-06 17:27:19 -06:00
|
|
|
name: "nil, non anon id",
|
|
|
|
|
output: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"*": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-05-04 19:57:35 -05:00
|
|
|
userId: "u_abc123",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "nil, anon id",
|
2023-01-06 17:27:19 -06:00
|
|
|
output: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
globals.IdField: true,
|
|
|
|
|
globals.ScopeField: true,
|
|
|
|
|
globals.ScopeIdField: true,
|
|
|
|
|
globals.PluginField: true,
|
|
|
|
|
globals.PluginIdField: true,
|
|
|
|
|
globals.NameField: true,
|
|
|
|
|
globals.DescriptionField: true,
|
|
|
|
|
globals.TypeField: true,
|
|
|
|
|
globals.IsPrimaryField: true,
|
|
|
|
|
globals.PrimaryAuthMethodIdField: true,
|
|
|
|
|
globals.AuthorizedActionsField: true,
|
|
|
|
|
globals.AuthorizedCollectionActionsField: true,
|
|
|
|
|
},
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
2022-10-28 15:52:06 -04:00
|
|
|
userId: globals.AnonymousUserId,
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
{
|
2023-01-06 17:27:19 -06:00
|
|
|
name: "not nil",
|
|
|
|
|
input: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"foo": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
output: &OutputFields{
|
|
|
|
|
fields: map[string]bool{
|
|
|
|
|
"foo": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-05-04 19:57:35 -05:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
for _, test := range tests {
|
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
|
assert.Equal(t, test.output, test.input.SelfOrDefaults(test.userId))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|