Files
threshold/internal/daemon/controller/handlers/roles/role_service_test.go
T
Todd b58dada40d Move the server daemons into their own package (#2061)
* Move the server daemons into their own package

With this change the internal/server package now is dedicated to the server domain (workers and controllers) and not to the server daemons.
The hope is that this will make it easier to keep the daemons decoupled from the representation of the servers in the database.
2022-05-09 12:45:10 -07:00

2033 lines
66 KiB
Go

package roles_test
import (
"context"
"errors"
"fmt"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/boundary/internal/auth/oidc"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/roles"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/perms"
"github.com/hashicorp/boundary/internal/types/scope"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/roles"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
"github.com/kr/pretty"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var testAuthorizedActions = []string{"no-op", "read", "update", "delete", "add-principals", "set-principals", "remove-principals", "add-grants", "set-grants", "remove-grants"}
func createDefaultRolesAndRepo(t *testing.T) (*iam.Role, *iam.Role, func() (*iam.Repository, error)) {
t.Helper()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
o, p := iam.TestScopes(t, iamRepo)
or := iam.TestRole(t, conn, o.GetPublicId(), iam.WithDescription("default"), iam.WithName("default"), iam.WithGrantScopeId(p.GetPublicId()))
pr := iam.TestRole(t, conn, p.GetPublicId(), iam.WithDescription("default"), iam.WithName("default"))
return or, pr, repoFn
}
func equalPrincipals(role *pb.Role, principals []string) bool {
if len(role.Principals) != len(principals) {
return false
}
for _, principal := range principals {
var foundInPrincipals bool
var foundInPrincipalIds bool
for _, v := range role.Principals {
if v.Id == principal {
foundInPrincipals = true
}
}
for _, v := range role.PrincipalIds {
if v == principal {
foundInPrincipalIds = true
}
}
if !foundInPrincipals || !foundInPrincipalIds {
return false
}
}
return true
}
func TestGet(t *testing.T) {
assert, require := assert.New(t), require.New(t)
or, pr, repoFn := createDefaultRolesAndRepo(t)
toMerge := &pbs.GetRoleRequest{
Id: or.GetPublicId(),
}
wantOrgRole := &pb.Role{
Id: or.GetPublicId(),
ScopeId: or.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: or.GetScopeId(), Type: scope.Org.String(), ParentScopeId: scope.Global.String()},
Name: &wrapperspb.StringValue{Value: or.GetName()},
Description: &wrapperspb.StringValue{Value: or.GetDescription()},
GrantScopeId: &wrapperspb.StringValue{Value: pr.GetGrantScopeId()},
CreatedTime: or.CreateTime.GetTimestamp(),
UpdatedTime: or.UpdateTime.GetTimestamp(),
Version: or.GetVersion(),
AuthorizedActions: testAuthorizedActions,
}
wantProjRole := &pb.Role{
Id: pr.GetPublicId(),
ScopeId: pr.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: pr.GetScopeId(), Type: scope.Project.String(), ParentScopeId: or.GetScopeId()},
Name: &wrapperspb.StringValue{Value: pr.GetName()},
Description: &wrapperspb.StringValue{Value: pr.GetDescription()},
GrantScopeId: &wrapperspb.StringValue{Value: pr.GetGrantScopeId()},
CreatedTime: pr.CreateTime.GetTimestamp(),
UpdatedTime: pr.UpdateTime.GetTimestamp(),
Version: pr.GetVersion(),
AuthorizedActions: testAuthorizedActions,
}
cases := []struct {
name string
scopeId string
req *pbs.GetRoleRequest
res *pbs.GetRoleResponse
err error
}{
{
name: "Get an Existing Role",
scopeId: or.GetScopeId(),
req: &pbs.GetRoleRequest{Id: or.GetPublicId()},
res: &pbs.GetRoleResponse{Item: wantOrgRole},
},
{
name: "Get a non existant Role",
req: &pbs.GetRoleRequest{Id: iam.RolePrefix + "_DoesntExis"},
res: nil,
err: handlers.ApiErrorWithCode(codes.NotFound),
},
{
name: "Wrong id prefix",
req: &pbs.GetRoleRequest{Id: "j_1234567890"},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "space in id",
req: &pbs.GetRoleRequest{Id: iam.RolePrefix + "_1 23456789"},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Project Scoped Get an Existing Role",
scopeId: pr.GetScopeId(),
req: &pbs.GetRoleRequest{Id: pr.GetPublicId()},
res: &pbs.GetRoleResponse{Item: wantProjRole},
},
{
name: "Project Scoped Get a non existant Role",
scopeId: pr.GetScopeId(),
req: &pbs.GetRoleRequest{Id: iam.RolePrefix + "_DoesntExis"},
res: nil,
err: handlers.ApiErrorWithCode(codes.NotFound),
},
{
name: "Project Scoped Wrong id prefix",
scopeId: pr.GetScopeId(),
req: &pbs.GetRoleRequest{Id: "j_1234567890"},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Project Scoped space in id",
scopeId: pr.GetScopeId(),
req: &pbs.GetRoleRequest{Id: iam.RolePrefix + "_1 23456789"},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
req := proto.Clone(toMerge).(*pbs.GetRoleRequest)
proto.Merge(req, tc.req)
s, err := roles.NewService(repoFn)
require.NoError(err, "Couldn't create new role service.")
got, gErr := s.GetRole(auth.DisabledAuthTestContext(repoFn, tc.scopeId), req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "GetRole(%+v) got error %v, wanted %v", req, gErr, tc.err)
}
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "GetRole(%q) got response\n%q, wanted\n%q", req, got, tc.res)
})
}
}
func TestList(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
oNoRoles, pWithRoles := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true))
oWithRoles, pNoRoles := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true))
var wantOrgRoles []*pb.Role
var wantProjRoles []*pb.Role
var totalRoles []*pb.Role
for i := 0; i < 10; i++ {
or := iam.TestRole(t, conn, oWithRoles.GetPublicId())
wantOrgRoles = append(wantOrgRoles, &pb.Role{
Id: or.GetPublicId(),
ScopeId: or.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: or.GetScopeId(), Type: scope.Org.String(), ParentScopeId: scope.Global.String()},
CreatedTime: or.GetCreateTime().GetTimestamp(),
UpdatedTime: or.GetUpdateTime().GetTimestamp(),
GrantScopeId: &wrapperspb.StringValue{Value: or.GetGrantScopeId()},
Version: or.GetVersion(),
AuthorizedActions: testAuthorizedActions,
})
totalRoles = append(totalRoles, wantOrgRoles[i])
pr := iam.TestRole(t, conn, pWithRoles.GetPublicId())
wantProjRoles = append(wantProjRoles, &pb.Role{
Id: pr.GetPublicId(),
ScopeId: pr.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: pr.GetScopeId(), Type: scope.Project.String(), ParentScopeId: oNoRoles.GetPublicId()},
CreatedTime: pr.GetCreateTime().GetTimestamp(),
UpdatedTime: pr.GetUpdateTime().GetTimestamp(),
GrantScopeId: &wrapperspb.StringValue{Value: pr.GetGrantScopeId()},
Version: pr.GetVersion(),
AuthorizedActions: testAuthorizedActions,
})
totalRoles = append(totalRoles, wantProjRoles[i])
}
cases := []struct {
name string
req *pbs.ListRolesRequest
res *pbs.ListRolesResponse
err error
}{
{
name: "List Many Role",
req: &pbs.ListRolesRequest{ScopeId: oWithRoles.GetPublicId()},
res: &pbs.ListRolesResponse{Items: wantOrgRoles},
},
{
name: "List No Roles",
req: &pbs.ListRolesRequest{ScopeId: oNoRoles.GetPublicId()},
res: &pbs.ListRolesResponse{},
},
{
name: "List Many Project Role",
req: &pbs.ListRolesRequest{ScopeId: pWithRoles.GetPublicId()},
res: &pbs.ListRolesResponse{Items: wantProjRoles},
},
{
name: "List No Project Roles",
req: &pbs.ListRolesRequest{ScopeId: pNoRoles.GetPublicId()},
res: &pbs.ListRolesResponse{},
},
{
name: "List proj roles recursively",
req: &pbs.ListRolesRequest{ScopeId: pWithRoles.GetPublicId(), Recursive: true},
res: &pbs.ListRolesResponse{
Items: wantProjRoles,
},
},
{
name: "List org roles recursively",
req: &pbs.ListRolesRequest{ScopeId: oNoRoles.GetPublicId(), Recursive: true},
res: &pbs.ListRolesResponse{
Items: wantProjRoles,
},
},
{
name: "List global roles recursively",
req: &pbs.ListRolesRequest{ScopeId: "global", Recursive: true},
res: &pbs.ListRolesResponse{
Items: totalRoles,
},
},
{
name: "Filter to org roles",
req: &pbs.ListRolesRequest{ScopeId: "global", Recursive: true, Filter: fmt.Sprintf(`"/item/scope/id"==%q`, oWithRoles.GetPublicId())},
res: &pbs.ListRolesResponse{Items: wantOrgRoles},
},
{
name: "Filter to proj roles",
req: &pbs.ListRolesRequest{ScopeId: "global", Recursive: true, Filter: fmt.Sprintf(`"/item/scope/id"==%q`, pWithRoles.GetPublicId())},
res: &pbs.ListRolesResponse{Items: wantProjRoles},
},
{
name: "Filter to no roles",
req: &pbs.ListRolesRequest{ScopeId: pWithRoles.GetPublicId(), Filter: `"/item/id"=="doesnt match"`},
res: &pbs.ListRolesResponse{},
},
{
name: "Filter Bad Format",
req: &pbs.ListRolesRequest{ScopeId: pWithRoles.GetPublicId(), Filter: `"//id/"=="bad"`},
err: handlers.InvalidArgumentErrorf("bad format", nil),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
s, err := roles.NewService(repoFn)
require.NoError(err, "Couldn't create new role service.")
// Test the non-anon case
got, gErr := s.ListRoles(auth.DisabledAuthTestContext(repoFn, tc.req.GetScopeId()), tc.req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err))
return
}
require.NoError(gErr)
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListRoles(%q) got response %q, wanted %q", tc.req, got, tc.res)
// Test the anon case
got, gErr = s.ListRoles(auth.DisabledAuthTestContext(repoFn, tc.req.GetScopeId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
require.NoError(gErr)
assert.Len(got.Items, len(tc.res.Items))
for _, item := range got.GetItems() {
require.Nil(item.CreatedTime)
require.Nil(item.PrincipalIds)
require.Nil(item.Principals)
require.Nil(item.Grants)
require.Nil(item.GrantStrings)
require.Nil(item.UpdatedTime)
require.Zero(item.Version)
}
})
}
}
func TestDelete(t *testing.T) {
or, pr, repoFn := createDefaultRolesAndRepo(t)
s, err := roles.NewService(repoFn)
require.NoError(t, err, "Error when getting new role service.")
cases := []struct {
name string
scopeId string
req *pbs.DeleteRoleRequest
res *pbs.DeleteRoleResponse
err error
}{
{
name: "Delete an Existing Role",
scopeId: or.GetScopeId(),
req: &pbs.DeleteRoleRequest{
Id: or.GetPublicId(),
},
},
{
name: "Delete bad role id",
scopeId: or.GetScopeId(),
req: &pbs.DeleteRoleRequest{
Id: iam.RolePrefix + "_doesntexis",
},
err: handlers.ApiErrorWithCode(codes.NotFound),
},
{
name: "Bad Role Id formatting",
scopeId: or.GetScopeId(),
req: &pbs.DeleteRoleRequest{
Id: "bad_format",
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Project Scoped Delete an Existing Role",
scopeId: pr.GetScopeId(),
req: &pbs.DeleteRoleRequest{
Id: pr.GetPublicId(),
},
},
{
name: "Project Scoped Delete bad Role id",
scopeId: pr.GetScopeId(),
req: &pbs.DeleteRoleRequest{
Id: iam.RolePrefix + "_doesntexis",
},
err: handlers.ApiErrorWithCode(codes.NotFound),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, gErr := s.DeleteRole(auth.DisabledAuthTestContext(repoFn, tc.scopeId), tc.req)
if tc.err != nil {
require.NotNil(gErr)
assert.True(errors.Is(gErr, tc.err), "DeleteRole(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
}
assert.EqualValuesf(tc.res, got, "DeleteRole(%q) got response %q, wanted %q", tc.req, got, tc.res)
})
}
}
func TestDelete_twice(t *testing.T) {
assert, require := assert.New(t), require.New(t)
or, pr, repoFn := createDefaultRolesAndRepo(t)
s, err := roles.NewService(repoFn)
require.NoError(err, "Error when getting new role service")
req := &pbs.DeleteRoleRequest{
Id: or.GetPublicId(),
}
ctx := auth.DisabledAuthTestContext(repoFn, or.GetScopeId())
_, gErr := s.DeleteRole(ctx, req)
assert.NoError(gErr, "First attempt")
_, gErr = s.DeleteRole(ctx, req)
assert.Error(gErr, "Second attempt")
assert.True(errors.Is(gErr, handlers.ApiErrorWithCode(codes.NotFound)), "Expected permission denied for the second delete.")
projReq := &pbs.DeleteRoleRequest{
Id: pr.GetPublicId(),
}
ctx = auth.DisabledAuthTestContext(repoFn, pr.GetScopeId())
_, gErr = s.DeleteRole(ctx, projReq)
assert.NoError(gErr, "First attempt")
_, gErr = s.DeleteRole(ctx, projReq)
assert.Error(gErr, "Second attempt")
assert.True(errors.Is(gErr, handlers.ApiErrorWithCode(codes.NotFound)), "Expected permission denied for the second delete.")
}
func TestCreate(t *testing.T) {
defaultOrgRole, defaultProjRole, repoFn := createDefaultRolesAndRepo(t)
defaultCreated := defaultOrgRole.GetCreateTime().GetTimestamp().AsTime()
toMerge := &pbs.CreateRoleRequest{}
cases := []struct {
name string
req *pbs.CreateRoleRequest
res *pbs.CreateRoleResponse
err error
}{
{
name: "Create a valid Role",
req: &pbs.CreateRoleRequest{Item: &pb.Role{
ScopeId: defaultOrgRole.GetScopeId(),
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
GrantScopeId: &wrapperspb.StringValue{Value: defaultProjRole.ScopeId},
}},
res: &pbs.CreateRoleResponse{
Uri: fmt.Sprintf("roles/%s_", iam.RolePrefix),
Item: &pb.Role{
ScopeId: defaultOrgRole.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: defaultOrgRole.GetScopeId(), Type: scope.Org.String(), ParentScopeId: scope.Global.String()},
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
GrantScopeId: &wrapperspb.StringValue{Value: defaultProjRole.ScopeId},
Version: 1,
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Create a valid Global Role",
req: &pbs.CreateRoleRequest{Item: &pb.Role{
ScopeId: scope.Global.String(),
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
GrantScopeId: &wrapperspb.StringValue{Value: defaultProjRole.ScopeId},
}},
res: &pbs.CreateRoleResponse{
Uri: fmt.Sprintf("roles/%s_", iam.RolePrefix),
Item: &pb.Role{
ScopeId: scope.Global.String(),
Scope: &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"},
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
GrantScopeId: &wrapperspb.StringValue{Value: defaultProjRole.ScopeId},
Version: 1,
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Create a valid Project Scoped Role",
req: &pbs.CreateRoleRequest{
Item: &pb.Role{
ScopeId: defaultProjRole.GetScopeId(),
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
},
},
res: &pbs.CreateRoleResponse{
Uri: fmt.Sprintf("roles/%s_", iam.RolePrefix),
Item: &pb.Role{
ScopeId: defaultProjRole.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: defaultProjRole.GetScopeId(), Type: scope.Project.String(), ParentScopeId: defaultOrgRole.GetScopeId()},
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
GrantScopeId: &wrapperspb.StringValue{Value: defaultProjRole.ScopeId},
Version: 1,
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Invalid grant scope ID",
req: &pbs.CreateRoleRequest{
Item: &pb.Role{
ScopeId: defaultProjRole.GetScopeId(),
Name: &wrapperspb.StringValue{Value: "name"},
Description: &wrapperspb.StringValue{Value: "desc"},
GrantScopeId: &wrapperspb.StringValue{Value: defaultOrgRole.GetScopeId()},
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Can't specify Id",
req: &pbs.CreateRoleRequest{Item: &pb.Role{
ScopeId: defaultProjRole.GetScopeId(),
Id: iam.RolePrefix + "_notallowed",
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Can't specify Created Time",
req: &pbs.CreateRoleRequest{Item: &pb.Role{
ScopeId: defaultProjRole.GetScopeId(),
CreatedTime: timestamppb.Now(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Can't specify Update Time",
req: &pbs.CreateRoleRequest{Item: &pb.Role{
ScopeId: defaultProjRole.GetScopeId(),
UpdatedTime: timestamppb.Now(),
}},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
req := proto.Clone(toMerge).(*pbs.CreateRoleRequest)
proto.Merge(req, tc.req)
s, err := roles.NewService(repoFn)
require.NoError(err, "Error when getting new role service.")
got, gErr := s.CreateRole(auth.DisabledAuthTestContext(repoFn, tc.req.GetItem().GetScopeId()), req)
if tc.err != nil {
require.NotNil(gErr)
assert.True(errors.Is(gErr, tc.err), "CreateRole(%+v) got error %v, wanted %v", req, gErr, tc.err)
}
if got != nil {
assert.Contains(got.GetUri(), tc.res.Uri)
assert.True(strings.HasPrefix(got.GetItem().GetId(), iam.RolePrefix+"_"), "Expected %q to have the prefix %q", got.GetItem().GetId(), iam.RolePrefix+"_")
gotCreateTime := got.GetItem().GetCreatedTime().AsTime()
gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime()
// Verify it is a role created after the test setup's default role
assert.True(gotCreateTime.After(defaultCreated), "New role should have been created after default role. Was created %v, which is after %v", gotCreateTime, defaultCreated)
assert.True(gotUpdateTime.After(defaultCreated), "New role should have been updated after default role. Was updated %v, which is after %v", gotUpdateTime, defaultCreated)
// Clear all values which are hard to compare against.
got.Uri, tc.res.Uri = "", ""
got.Item.Id, tc.res.Item.Id = "", ""
got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil
}
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "CreateRole(%q) got response\n%q, wanted\n%q", req, got, tc.res)
})
}
}
func TestUpdate(t *testing.T) {
grantString := "id=*;type=*;actions=*"
g, err := perms.Parse("global", grantString)
require.NoError(t, err)
_, actions := g.Actions()
grant := &pb.Grant{
Raw: grantString,
Canonical: g.CanonicalString(),
Json: &pb.GrantJson{
Id: g.Id(),
Type: g.Type().String(),
Actions: actions,
},
}
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
o, p := iam.TestScopes(t, iamRepo)
u := iam.TestUser(t, iamRepo, o.GetPublicId())
or := iam.TestRole(t, conn, o.GetPublicId(), iam.WithDescription("default"), iam.WithName("default"), iam.WithGrantScopeId(p.GetPublicId()))
_ = iam.TestRoleGrant(t, conn, or.GetPublicId(), grantString)
_ = iam.TestUserRole(t, conn, or.GetPublicId(), u.GetPublicId())
pr := iam.TestRole(t, conn, p.GetPublicId(), iam.WithDescription("default"), iam.WithName("default"))
_ = iam.TestRoleGrant(t, conn, pr.GetPublicId(), grantString)
_ = iam.TestUserRole(t, conn, pr.GetPublicId(), u.GetPublicId())
principal := &pb.Principal{
Id: u.GetPublicId(),
Type: iam.UserRoleType.String(),
ScopeId: u.GetScopeId(),
}
var orVersion uint32 = 1
var prVersion uint32 = 1
tested, err := roles.NewService(repoFn)
require.NoError(t, err, "Error when getting new role service.")
resetRoles := func(proj bool) {
repo, err := repoFn()
require.NoError(t, err, "Couldn't get a new repo")
if proj {
prVersion++
pr, _, _, _, err = repo.UpdateRole(context.Background(), pr, prVersion, []string{"Name", "Description"})
require.NoError(t, err, "Failed to reset the role")
prVersion++
} else {
orVersion++
or, _, _, _, err = repo.UpdateRole(context.Background(), or, orVersion, []string{"Name", "Description"})
require.NoError(t, err, "Failed to reset the role")
orVersion++
}
}
created := or.GetCreateTime().GetTimestamp().AsTime()
toMerge := &pbs.UpdateRoleRequest{
Id: or.GetPublicId(),
}
cases := []struct {
name string
scopeId string
req *pbs.UpdateRoleRequest
res *pbs.UpdateRoleResponse
err error
}{
{
name: "Update an Existing Role",
scopeId: or.GetScopeId(),
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name", "description", "grant_scope_id"},
},
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "desc"},
GrantScopeId: &wrapperspb.StringValue{Value: or.GetScopeId()},
},
},
res: &pbs.UpdateRoleResponse{
Item: &pb.Role{
Id: or.GetPublicId(),
ScopeId: or.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: or.GetScopeId(), Type: scope.Org.String(), ParentScopeId: scope.Global.String()},
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "desc"},
CreatedTime: or.GetCreateTime().GetTimestamp(),
GrantScopeId: &wrapperspb.StringValue{Value: or.GetScopeId()},
GrantStrings: []string{grant.GetRaw()},
Grants: []*pb.Grant{grant},
PrincipalIds: []string{u.GetPublicId()},
Principals: []*pb.Principal{principal},
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Multiple Paths in single string",
scopeId: or.GetScopeId(),
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name,description"},
},
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "desc"},
},
},
res: &pbs.UpdateRoleResponse{
Item: &pb.Role{
Id: or.GetPublicId(),
ScopeId: or.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: or.GetScopeId(), Type: scope.Org.String(), ParentScopeId: scope.Global.String()},
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "desc"},
CreatedTime: or.GetCreateTime().GetTimestamp(),
GrantScopeId: &wrapperspb.StringValue{Value: or.GetScopeId()},
GrantStrings: []string{grant.GetRaw()},
Grants: []*pb.Grant{grant},
PrincipalIds: []string{u.GetPublicId()},
Principals: []*pb.Principal{principal},
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Update an Existing Project Scoped Role",
scopeId: pr.GetScopeId(),
req: &pbs.UpdateRoleRequest{
Id: pr.GetPublicId(),
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name", "description"},
},
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "desc"},
},
},
res: &pbs.UpdateRoleResponse{
Item: &pb.Role{
Id: pr.GetPublicId(),
ScopeId: pr.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: pr.GetScopeId(), Type: scope.Project.String(), ParentScopeId: or.GetScopeId()},
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "desc"},
CreatedTime: pr.GetCreateTime().GetTimestamp(),
GrantScopeId: &wrapperspb.StringValue{Value: pr.GetScopeId()},
GrantStrings: []string{grant.GetRaw()},
Grants: []*pb.Grant{grant},
PrincipalIds: []string{u.GetPublicId()},
Principals: []*pb.Principal{principal},
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Multiple Paths in single string",
scopeId: pr.GetScopeId(),
req: &pbs.UpdateRoleRequest{
Id: pr.GetPublicId(),
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name,description"},
},
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "desc"},
},
},
res: &pbs.UpdateRoleResponse{
Item: &pb.Role{
Id: pr.GetPublicId(),
ScopeId: pr.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: pr.GetScopeId(), Type: scope.Project.String(), ParentScopeId: or.GetScopeId()},
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "desc"},
CreatedTime: pr.GetCreateTime().GetTimestamp(),
GrantScopeId: &wrapperspb.StringValue{Value: pr.GetScopeId()},
GrantStrings: []string{grant.GetRaw()},
Grants: []*pb.Grant{grant},
PrincipalIds: []string{u.GetPublicId()},
Principals: []*pb.Principal{principal},
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "No Update Mask",
req: &pbs.UpdateRoleRequest{
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "updated name"},
Description: &wrapperspb.StringValue{Value: "updated desc"},
},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "No Paths in Mask",
scopeId: or.GetScopeId(),
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{Paths: []string{}},
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "updated name"},
Description: &wrapperspb.StringValue{Value: "updated desc"},
},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Only non-existant paths in Mask",
scopeId: or.GetScopeId(),
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{Paths: []string{"nonexistant_field"}},
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "updated name"},
Description: &wrapperspb.StringValue{Value: "updated desc"},
},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Unset Name",
scopeId: or.GetScopeId(),
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name"},
},
Item: &pb.Role{
Description: &wrapperspb.StringValue{Value: "ignored"},
},
},
res: &pbs.UpdateRoleResponse{
Item: &pb.Role{
Id: or.GetPublicId(),
ScopeId: or.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: or.GetScopeId(), Type: scope.Org.String(), ParentScopeId: scope.Global.String()},
Description: &wrapperspb.StringValue{Value: "default"},
CreatedTime: or.GetCreateTime().GetTimestamp(),
GrantScopeId: &wrapperspb.StringValue{Value: or.GetScopeId()},
GrantStrings: []string{grant.GetRaw()},
Grants: []*pb.Grant{grant},
PrincipalIds: []string{u.GetPublicId()},
Principals: []*pb.Principal{principal},
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Update Only Name",
scopeId: or.GetScopeId(),
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name"},
},
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "updated"},
Description: &wrapperspb.StringValue{Value: "ignored"},
},
},
res: &pbs.UpdateRoleResponse{
Item: &pb.Role{
Id: or.GetPublicId(),
ScopeId: or.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: or.GetScopeId(), Type: scope.Org.String(), ParentScopeId: scope.Global.String()},
Name: &wrapperspb.StringValue{Value: "updated"},
Description: &wrapperspb.StringValue{Value: "default"},
CreatedTime: or.GetCreateTime().GetTimestamp(),
GrantScopeId: &wrapperspb.StringValue{Value: or.GetScopeId()},
GrantStrings: []string{grant.GetRaw()},
Grants: []*pb.Grant{grant},
PrincipalIds: []string{u.GetPublicId()},
Principals: []*pb.Principal{principal},
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Update Only Description",
scopeId: or.GetScopeId(),
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"description"},
},
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "ignored"},
Description: &wrapperspb.StringValue{Value: "notignored"},
},
},
res: &pbs.UpdateRoleResponse{
Item: &pb.Role{
Id: or.GetPublicId(),
ScopeId: or.GetScopeId(),
Scope: &scopes.ScopeInfo{Id: or.GetScopeId(), Type: scope.Org.String(), ParentScopeId: scope.Global.String()},
Name: &wrapperspb.StringValue{Value: "default"},
Description: &wrapperspb.StringValue{Value: "notignored"},
CreatedTime: or.GetCreateTime().GetTimestamp(),
GrantScopeId: &wrapperspb.StringValue{Value: or.GetScopeId()},
GrantStrings: []string{grant.GetRaw()},
Grants: []*pb.Grant{grant},
PrincipalIds: []string{u.GetPublicId()},
Principals: []*pb.Principal{principal},
AuthorizedActions: testAuthorizedActions,
},
},
},
{
name: "Update a Non Existing Role",
req: &pbs.UpdateRoleRequest{
Id: iam.RolePrefix + "_DoesntExis",
UpdateMask: &field_mask.FieldMask{
Paths: []string{"description"},
},
Item: &pb.Role{
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "desc"},
},
},
err: handlers.ApiErrorWithCode(codes.NotFound),
},
{
name: "Cant change Id",
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"id"},
},
Item: &pb.Role{
Id: iam.RolePrefix + "_somethinge",
Name: &wrapperspb.StringValue{Value: "new"},
Description: &wrapperspb.StringValue{Value: "new desc"},
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Cant specify Created Time",
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"created_time"},
},
Item: &pb.Role{
CreatedTime: timestamppb.Now(),
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Cant specify Updated Time",
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"updated_time"},
},
Item: &pb.Role{
UpdatedTime: timestamppb.Now(),
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Cant specify grants",
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"grants"},
},
Item: &pb.Role{
GrantStrings: []string{"anything"},
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Cant specify principals",
req: &pbs.UpdateRoleRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"principal_ids"},
},
Item: &pb.Role{
PrincipalIds: []string{"u_0987654321"},
},
},
res: nil,
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
ver := orVersion
if tc.req.Id == pr.PublicId {
ver = prVersion
}
tc.req.Item.Version = ver
assert, require := assert.New(t), require.New(t)
req := proto.Clone(toMerge).(*pbs.UpdateRoleRequest)
proto.Merge(req, tc.req)
// Test with bad version (too high, too low)
req.Item.Version = ver + 2
_, gErr := tested.UpdateRole(auth.DisabledAuthTestContext(repoFn, tc.scopeId), req)
require.Error(gErr)
req.Item.Version = ver - 1
_, gErr = tested.UpdateRole(auth.DisabledAuthTestContext(repoFn, tc.scopeId), req)
require.Error(gErr)
req.Item.Version = ver
got, gErr := tested.UpdateRole(auth.DisabledAuthTestContext(repoFn, tc.scopeId), req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "UpdateRole(%+v) got error %v, wanted %v", req, gErr, tc.err)
}
if tc.err == nil {
defer resetRoles(req.Id == pr.PublicId)
}
if got != nil {
assert.NotNilf(tc.res, "Expected UpdateRole response to be nil, but was %v", got)
gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime()
require.NoError(err, "Error converting proto to timestamp")
// Verify it is a role updated after it was created
assert.True(gotUpdateTime.After(created), "Updated role should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created)
// Clear all values which are hard to compare against.
got.Item.UpdatedTime, tc.res.Item.UpdatedTime = nil, nil
assert.Equal(ver+1, got.GetItem().GetVersion())
tc.res.Item.Version = ver + 1
}
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "UpdateRole(%q) got response\n%q,\nwanted\n%q", req, got, tc.res)
})
}
}
func TestAddPrincipal(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
o, p := iam.TestScopes(t, iamRepo)
s, err := roles.NewService(repoFn)
require.NoError(t, err, "Error when getting new role service.")
ctx := context.Background()
kmsCache := kms.TestKms(t, conn, wrap)
databaseWrapper, err := kmsCache.GetWrapper(ctx, o.PublicId, kms.KeyPurposeDatabase)
require.NoError(t, err)
authMethod := oidc.TestAuthMethod(
t, conn, databaseWrapper, o.GetPublicId(), oidc.ActivePrivateState,
"alice-rp", "fido",
oidc.WithSigningAlgs(oidc.RS256),
oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://www.alice.com")[0]),
oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://www.alice.com/callback")[0]),
)
users := []*iam.User{
iam.TestUser(t, iamRepo, o.GetPublicId()),
iam.TestUser(t, iamRepo, o.GetPublicId()),
iam.TestUser(t, iamRepo, o.GetPublicId()),
}
groups := []*iam.Group{
iam.TestGroup(t, conn, o.GetPublicId()),
iam.TestGroup(t, conn, o.GetPublicId()),
iam.TestGroup(t, conn, o.GetPublicId()),
}
managedGroups := []*oidc.ManagedGroup{
oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter),
oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter),
oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter),
}
addCases := []struct {
name string
setup func(*iam.Role)
addUsers []string
addGroups []string
addManagedGroups []string
resultUsers []string
resultGroups []string
resultManagedGroups []string
wantErr bool
}{
{
name: "Add user on empty role",
setup: func(r *iam.Role) {},
addUsers: []string{users[1].GetPublicId()},
resultUsers: []string{users[1].GetPublicId()},
},
{
name: "Add user on populated role",
setup: func(r *iam.Role) {
iam.TestUserRole(t, conn, r.GetPublicId(), users[0].GetPublicId())
},
addUsers: []string{users[1].GetPublicId()},
resultUsers: []string{users[0].GetPublicId(), users[1].GetPublicId()},
},
{
name: "Add empty on populated role",
setup: func(r *iam.Role) {
iam.TestUserRole(t, conn, r.GetPublicId(), users[0].GetPublicId())
iam.TestUserRole(t, conn, r.GetPublicId(), users[1].GetPublicId())
},
wantErr: true,
},
{
name: "Add group on empty role",
setup: func(r *iam.Role) {},
addGroups: []string{groups[1].GetPublicId()},
resultGroups: []string{groups[1].GetPublicId()},
},
{
name: "Add group on populated role",
setup: func(r *iam.Role) {
iam.TestGroupRole(t, conn, r.GetPublicId(), groups[0].GetPublicId())
},
addGroups: []string{groups[1].GetPublicId()},
resultGroups: []string{groups[0].GetPublicId(), groups[1].GetPublicId()},
},
{
name: "Add duplicate group on populated role",
setup: func(r *iam.Role) {
iam.TestGroupRole(t, conn, r.GetPublicId(), groups[0].GetPublicId())
},
addGroups: []string{groups[1].GetPublicId(), groups[1].GetPublicId()},
resultGroups: []string{groups[0].GetPublicId(), groups[1].GetPublicId()},
},
{
name: "Add managed group on empty role",
setup: func(r *iam.Role) {},
addManagedGroups: []string{managedGroups[1].GetPublicId()},
resultManagedGroups: []string{managedGroups[1].GetPublicId()},
},
{
name: "Add managed group on populated role",
setup: func(r *iam.Role) {
iam.TestManagedGroupRole(t, conn, r.GetPublicId(), managedGroups[0].GetPublicId())
},
addManagedGroups: []string{managedGroups[1].GetPublicId()},
resultManagedGroups: []string{managedGroups[0].GetPublicId(), managedGroups[1].GetPublicId()},
},
{
name: "Add duplicate managed group on populated role",
setup: func(r *iam.Role) {
iam.TestManagedGroupRole(t, conn, r.GetPublicId(), managedGroups[0].GetPublicId())
},
addManagedGroups: []string{managedGroups[1].GetPublicId(), managedGroups[1].GetPublicId()},
resultManagedGroups: []string{managedGroups[0].GetPublicId(), managedGroups[1].GetPublicId()},
},
{
name: "Add invalid u_recovery on role",
setup: func(r *iam.Role) {},
addUsers: []string{"u_recovery"},
wantErr: true,
},
}
for _, tc := range addCases {
for _, scope := range []*iam.Scope{o, p} {
t.Run(tc.name+"_"+scope.GetType(), func(t *testing.T) {
role := iam.TestRole(t, conn, scope.GetPublicId())
tc.setup(role)
req := &pbs.AddRolePrincipalsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
PrincipalIds: append(tc.addUsers, append(tc.addGroups, tc.addManagedGroups...)...),
}
got, err := s.AddRolePrincipals(auth.DisabledAuthTestContext(repoFn, o.GetPublicId()), req)
if tc.wantErr {
assert.Error(t, err)
return
}
s, ok := status.FromError(err)
require.True(t, ok, fmt.Sprintf("Could not run FromError; input was %s", pretty.Sprint(err)))
require.NoError(t, err, "Got error: %v", s)
assert.True(t, equalPrincipals(got.GetItem(), append(tc.resultUsers, append(tc.resultGroups, tc.resultManagedGroups...)...)))
})
}
}
role := iam.TestRole(t, conn, p.GetPublicId())
failCases := []struct {
name string
req *pbs.AddRolePrincipalsRequest
err error
}{
{
name: "Bad Role Id",
req: &pbs.AddRolePrincipalsRequest{
Id: "bad id",
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Bad Principal Id",
req: &pbs.AddRolePrincipalsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
PrincipalIds: []string{"invalid"},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "u_recovery Id",
req: &pbs.AddRolePrincipalsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
PrincipalIds: []string{"u_recovery"},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range failCases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
_, gErr := s.AddRolePrincipals(auth.DisabledAuthTestContext(repoFn, p.GetPublicId()), tc.req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "AddRolePrincipals(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
}
})
}
}
func TestSetPrincipal(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
s, err := roles.NewService(repoFn)
require.NoError(t, err, "Error when getting new role service.")
o, p := iam.TestScopes(t, iamRepo)
ctx := context.Background()
kmsCache := kms.TestKms(t, conn, wrap)
databaseWrapper, err := kmsCache.GetWrapper(ctx, o.PublicId, kms.KeyPurposeDatabase)
require.NoError(t, err)
authMethod := oidc.TestAuthMethod(
t, conn, databaseWrapper, o.GetPublicId(), oidc.ActivePrivateState,
"alice-rp", "fido",
oidc.WithSigningAlgs(oidc.RS256),
oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://www.alice.com")[0]),
oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://www.alice.com/callback")[0]),
)
users := []*iam.User{
iam.TestUser(t, iamRepo, o.GetPublicId()),
iam.TestUser(t, iamRepo, o.GetPublicId()),
iam.TestUser(t, iamRepo, o.GetPublicId()),
}
groups := []*iam.Group{
iam.TestGroup(t, conn, o.GetPublicId()),
iam.TestGroup(t, conn, o.GetPublicId()),
iam.TestGroup(t, conn, o.GetPublicId()),
}
managedGroups := []*oidc.ManagedGroup{
oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter),
oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter),
oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter),
}
setCases := []struct {
name string
setup func(*iam.Role)
setUsers []string
setGroups []string
setManagedGroups []string
resultUsers []string
resultGroups []string
resultManagedGroups []string
wantErr bool
}{
{
name: "Set user on empty role",
setup: func(r *iam.Role) {},
setUsers: []string{users[1].GetPublicId()},
resultUsers: []string{users[1].GetPublicId()},
},
{
name: "Set user on populated role",
setup: func(r *iam.Role) {
iam.TestUserRole(t, conn, r.GetPublicId(), users[0].GetPublicId())
},
setUsers: []string{users[1].GetPublicId()},
resultUsers: []string{users[1].GetPublicId()},
},
{
name: "Set duplicate user on populated role",
setup: func(r *iam.Role) {
iam.TestUserRole(t, conn, r.GetPublicId(), users[0].GetPublicId())
},
setUsers: []string{users[1].GetPublicId(), users[1].GetPublicId()},
resultUsers: []string{users[1].GetPublicId()},
},
{
name: "Set empty on populated role",
setup: func(r *iam.Role) {
iam.TestUserRole(t, conn, r.GetPublicId(), users[0].GetPublicId())
iam.TestUserRole(t, conn, r.GetPublicId(), users[1].GetPublicId())
},
setUsers: []string{},
resultUsers: nil,
},
{
name: "Set group on empty role",
setup: func(r *iam.Role) {},
setGroups: []string{groups[1].GetPublicId()},
resultGroups: []string{groups[1].GetPublicId()},
},
{
name: "Set group on populated role",
setup: func(r *iam.Role) {
iam.TestGroupRole(t, conn, r.GetPublicId(), groups[0].GetPublicId())
},
setGroups: []string{groups[1].GetPublicId()},
resultGroups: []string{groups[1].GetPublicId()},
},
{
name: "Set managed group on empty role",
setup: func(r *iam.Role) {},
setManagedGroups: []string{managedGroups[1].GetPublicId()},
resultManagedGroups: []string{managedGroups[1].GetPublicId()},
},
{
name: "Set managed group on populated role",
setup: func(r *iam.Role) {
iam.TestManagedGroupRole(t, conn, r.GetPublicId(), managedGroups[0].GetPublicId())
},
setManagedGroups: []string{managedGroups[1].GetPublicId()},
resultManagedGroups: []string{managedGroups[1].GetPublicId()},
},
{
name: "Set invalid u_recovery on role",
setup: func(r *iam.Role) {},
setUsers: []string{"u_recovery"},
wantErr: true,
},
}
for _, tc := range setCases {
for _, scope := range []*iam.Scope{o, p} {
t.Run(tc.name+"_"+scope.GetType(), func(t *testing.T) {
role := iam.TestRole(t, conn, scope.GetPublicId())
tc.setup(role)
req := &pbs.SetRolePrincipalsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
PrincipalIds: append(tc.setUsers, append(tc.setGroups, tc.setManagedGroups...)...),
}
got, err := s.SetRolePrincipals(auth.DisabledAuthTestContext(repoFn, o.GetPublicId()), req)
if tc.wantErr {
assert.Error(t, err)
return
}
s, ok := status.FromError(err)
require.True(t, ok, fmt.Sprintf("Could not run FromError; input was %s", pretty.Sprint(err)))
require.NoError(t, err, "Got error: %v", s)
assert.True(t, equalPrincipals(got.GetItem(), append(tc.resultUsers, append(tc.resultGroups, tc.resultManagedGroups...)...)))
})
}
}
role := iam.TestRole(t, conn, p.GetPublicId())
failCases := []struct {
name string
req *pbs.SetRolePrincipalsRequest
err error
}{
{
name: "Bad Role Id",
req: &pbs.SetRolePrincipalsRequest{
Id: "bad id",
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Bad Principal Id",
req: &pbs.SetRolePrincipalsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
PrincipalIds: []string{"invalid"},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "u_recovery",
req: &pbs.SetRolePrincipalsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
PrincipalIds: []string{"u_recovery"},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range failCases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
_, gErr := s.SetRolePrincipals(auth.DisabledAuthTestContext(repoFn, p.GetPublicId()), tc.req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "SetRolePrincipals(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
}
})
}
}
func TestRemovePrincipal(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
s, err := roles.NewService(repoFn)
require.NoError(t, err, "Error when getting new role service.")
o, p := iam.TestScopes(t, iamRepo)
ctx := context.Background()
kmsCache := kms.TestKms(t, conn, wrap)
databaseWrapper, err := kmsCache.GetWrapper(ctx, o.PublicId, kms.KeyPurposeDatabase)
require.NoError(t, err)
authMethod := oidc.TestAuthMethod(
t, conn, databaseWrapper, o.GetPublicId(), oidc.ActivePrivateState,
"alice-rp", "fido",
oidc.WithSigningAlgs(oidc.RS256),
oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://www.alice.com")[0]),
oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://www.alice.com/callback")[0]),
)
users := []*iam.User{
iam.TestUser(t, iamRepo, o.GetPublicId()),
iam.TestUser(t, iamRepo, o.GetPublicId()),
iam.TestUser(t, iamRepo, o.GetPublicId()),
}
groups := []*iam.Group{
iam.TestGroup(t, conn, o.GetPublicId()),
iam.TestGroup(t, conn, o.GetPublicId()),
iam.TestGroup(t, conn, o.GetPublicId()),
}
managedGroups := []*oidc.ManagedGroup{
oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter),
oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter),
oidc.TestManagedGroup(t, conn, authMethod, oidc.TestFakeManagedGroupFilter),
}
addCases := []struct {
name string
setup func(*iam.Role)
removeUsers []string
removeGroups []string
removeManagedGroups []string
resultUsers []string
resultGroups []string
resultManagedGroups []string
wantErr bool
}{
{
name: "Remove user on empty role",
setup: func(r *iam.Role) {},
removeUsers: []string{users[1].GetPublicId()},
wantErr: true,
},
{
name: "Remove 1 of 2 users from role",
setup: func(r *iam.Role) {
iam.TestUserRole(t, conn, r.GetPublicId(), users[0].GetPublicId())
iam.TestUserRole(t, conn, r.GetPublicId(), users[1].GetPublicId())
},
removeUsers: []string{users[1].GetPublicId()},
resultUsers: []string{users[0].GetPublicId()},
},
{
name: "Remove 1 duplicate user of 2 users from role",
setup: func(r *iam.Role) {
iam.TestUserRole(t, conn, r.GetPublicId(), users[0].GetPublicId())
iam.TestUserRole(t, conn, r.GetPublicId(), users[1].GetPublicId())
},
removeUsers: []string{users[1].GetPublicId(), users[1].GetPublicId()},
resultUsers: []string{users[0].GetPublicId()},
},
{
name: "Remove all users from role",
setup: func(r *iam.Role) {
iam.TestUserRole(t, conn, r.GetPublicId(), users[0].GetPublicId())
iam.TestUserRole(t, conn, r.GetPublicId(), users[1].GetPublicId())
},
removeUsers: []string{users[0].GetPublicId(), users[1].GetPublicId()},
resultUsers: []string{},
},
{
name: "Remove empty on populated role",
setup: func(r *iam.Role) {
iam.TestUserRole(t, conn, r.GetPublicId(), users[0].GetPublicId())
},
wantErr: true,
},
{
name: "Remove group on empty role",
setup: func(r *iam.Role) {},
removeGroups: []string{groups[1].GetPublicId()},
wantErr: true,
},
{
name: "Remove 1 of 2 groups from role",
setup: func(r *iam.Role) {
iam.TestGroupRole(t, conn, r.GetPublicId(), groups[0].GetPublicId())
iam.TestGroupRole(t, conn, r.GetPublicId(), groups[1].GetPublicId())
},
removeGroups: []string{groups[1].GetPublicId()},
resultGroups: []string{groups[0].GetPublicId()},
},
{
name: "Remove 1 duplicate group of 2 groups from role",
setup: func(r *iam.Role) {
iam.TestGroupRole(t, conn, r.GetPublicId(), groups[0].GetPublicId())
iam.TestGroupRole(t, conn, r.GetPublicId(), groups[1].GetPublicId())
},
removeGroups: []string{groups[1].GetPublicId(), groups[1].GetPublicId()},
resultGroups: []string{groups[0].GetPublicId()},
},
{
name: "Remove all groups from role",
setup: func(r *iam.Role) {
iam.TestGroupRole(t, conn, r.GetPublicId(), groups[0].GetPublicId())
iam.TestGroupRole(t, conn, r.GetPublicId(), groups[1].GetPublicId())
},
removeGroups: []string{groups[0].GetPublicId(), groups[1].GetPublicId()},
resultGroups: []string{},
},
{
name: "Remove managed group on empty role",
setup: func(r *iam.Role) {},
removeGroups: []string{groups[1].GetPublicId()},
wantErr: true,
},
{
name: "Remove 1 of 2 managed groups from role",
setup: func(r *iam.Role) {
iam.TestManagedGroupRole(t, conn, r.GetPublicId(), managedGroups[0].GetPublicId())
iam.TestManagedGroupRole(t, conn, r.GetPublicId(), managedGroups[1].GetPublicId())
},
removeManagedGroups: []string{managedGroups[1].GetPublicId()},
resultManagedGroups: []string{managedGroups[0].GetPublicId()},
},
{
name: "Remove 1 duplicate managed group of 2 managed groups from role",
setup: func(r *iam.Role) {
iam.TestManagedGroupRole(t, conn, r.GetPublicId(), managedGroups[0].GetPublicId())
iam.TestManagedGroupRole(t, conn, r.GetPublicId(), managedGroups[1].GetPublicId())
},
removeManagedGroups: []string{managedGroups[1].GetPublicId(), managedGroups[1].GetPublicId()},
resultManagedGroups: []string{managedGroups[0].GetPublicId()},
},
{
name: "Remove all managed groups from role",
setup: func(r *iam.Role) {
iam.TestManagedGroupRole(t, conn, r.GetPublicId(), managedGroups[0].GetPublicId())
iam.TestManagedGroupRole(t, conn, r.GetPublicId(), managedGroups[1].GetPublicId())
},
removeManagedGroups: []string{managedGroups[0].GetPublicId(), managedGroups[1].GetPublicId()},
resultManagedGroups: []string{},
},
}
for _, tc := range addCases {
for _, scope := range []*iam.Scope{o, p} {
t.Run(tc.name+"_"+scope.GetType(), func(t *testing.T) {
role := iam.TestRole(t, conn, scope.GetPublicId())
tc.setup(role)
req := &pbs.RemoveRolePrincipalsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
PrincipalIds: append(tc.removeUsers, append(tc.removeGroups, tc.removeManagedGroups...)...),
}
got, err := s.RemoveRolePrincipals(auth.DisabledAuthTestContext(repoFn, o.GetPublicId()), req)
if tc.wantErr {
assert.Error(t, err)
return
}
s, ok := status.FromError(err)
require.True(t, ok)
require.NoError(t, err, "Got error: %v", s)
assert.True(t, equalPrincipals(got.GetItem(), append(tc.resultUsers, append(tc.resultGroups, tc.resultManagedGroups...)...)))
})
}
}
role := iam.TestRole(t, conn, p.GetPublicId())
failCases := []struct {
name string
req *pbs.RemoveRolePrincipalsRequest
err error
}{
{
name: "Bad Role Id",
req: &pbs.RemoveRolePrincipalsRequest{
Id: "bad id",
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Bad User Id",
req: &pbs.RemoveRolePrincipalsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
PrincipalIds: []string{"g_validgroup", "invaliduser"},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Bad Group Id",
req: &pbs.RemoveRolePrincipalsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
PrincipalIds: []string{"u_validuser", "invalidgroup"},
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range failCases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
_, gErr := s.RemoveRolePrincipals(auth.DisabledAuthTestContext(repoFn, p.GetPublicId()), tc.req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "AddRolePrincipals(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
}
})
}
}
func checkEqualGrants(t *testing.T, expected []string, got *pb.Role) {
require, assert := require.New(t), assert.New(t)
require.Equal(len(expected), len(got.GrantStrings))
require.Equal(len(expected), len(got.Grants))
for i, v := range expected {
parsed, err := perms.Parse("o_abc123", v)
require.NoError(err)
assert.Equal(expected[i], got.GrantStrings[i])
assert.Equal(expected[i], got.Grants[i].GetRaw())
assert.Equal(v, got.Grants[i].GetCanonical())
j := got.Grants[i].GetJson()
require.NotNil(j)
assert.Equal(parsed.Id(), j.GetId())
assert.Equal(parsed.Type().String(), j.GetType())
_, acts := parsed.Actions()
assert.Equal(acts, j.GetActions())
}
}
func TestAddGrants(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
s, err := roles.NewService(repoFn)
require.NoError(t, err, "Error when getting new role service.")
addCases := []struct {
name string
existing []string
add []string
result []string
wantErr bool
}{
{
name: "Add grant on empty role",
add: []string{"id=*;type=*;actions=delete"},
result: []string{"id=*;type=*;actions=delete"},
},
{
name: "Add grant on role with grant",
existing: []string{"id=1;actions=read"},
add: []string{"id=*;type=*;actions=delete"},
result: []string{"id=1;actions=read", "id=*;type=*;actions=delete"},
},
{
name: "Add duplicate grant on role with grant",
existing: []string{"id=aA1;actions=read"},
add: []string{"id=*;type=*;actions=delete", "id=*;type=*;actions=delete"},
result: []string{"id=aA1;actions=read", "id=*;type=*;actions=delete"},
},
{
name: "Add grant matching existing grant",
existing: []string{"id=1;actions=read", "id=*;type=*;actions=delete"},
add: []string{"id=*;type=*;actions=delete"},
wantErr: true,
},
}
for _, tc := range addCases {
o, p := iam.TestScopes(t, iamRepo)
for _, scope := range []*iam.Scope{o, p} {
t.Run(tc.name+"_"+scope.Type, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
role := iam.TestRole(t, conn, scope.GetPublicId())
for _, e := range tc.existing {
_ = iam.TestRoleGrant(t, conn, role.GetPublicId(), e)
}
req := &pbs.AddRoleGrantsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
}
scopeId := o.GetPublicId()
if o != scope {
scopeId = p.GetPublicId()
}
req.GrantStrings = tc.add
got, err := s.AddRoleGrants(auth.DisabledAuthTestContext(repoFn, scopeId), req)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
checkEqualGrants(t, tc.result, got.GetItem())
})
}
}
_, p := iam.TestScopes(t, iamRepo)
role := iam.TestRole(t, conn, p.GetPublicId())
failCases := []struct {
name string
req *pbs.AddRoleGrantsRequest
err error
}{
{
name: "Bad Version",
req: &pbs.AddRoleGrantsRequest{
Id: role.GetPublicId(),
GrantStrings: []string{"id=*;type=*;actions=create"},
Version: role.GetVersion() + 2,
},
err: handlers.ApiErrorWithCode(codes.Internal),
},
{
name: "Bad Role Id",
req: &pbs.AddRoleGrantsRequest{
Id: "bad id",
GrantStrings: []string{"id=*;type=*;actions=create"},
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Unparseable Grant",
req: &pbs.AddRoleGrantsRequest{
Id: role.GetPublicId(),
GrantStrings: []string{"id=*;type=*;actions=create", "unparseable"},
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Empty Grant",
req: &pbs.AddRoleGrantsRequest{
Id: role.GetPublicId(),
GrantStrings: []string{"id=*;type=*;actions=create", ""},
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range failCases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
_, gErr := s.AddRoleGrants(auth.DisabledAuthTestContext(repoFn, p.GetPublicId()), tc.req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "AddRoleGrants(%+v) got error %#v, wanted %#v", tc.req, gErr, tc.err)
}
})
}
}
func TestSetGrants(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
s, err := roles.NewService(repoFn)
require.NoError(t, err, "Error when getting new role service.")
setCases := []struct {
name string
existing []string
set []string
result []string
wantErr bool
}{
{
name: "Set grant on empty role",
set: []string{"id=*;type=*;actions=delete"},
result: []string{"id=*;type=*;actions=delete"},
},
{
name: "Set grant on role with grant",
existing: []string{"id=1;actions=read"},
set: []string{"id=*;type=*;actions=delete"},
result: []string{"id=*;type=*;actions=delete"},
},
{
name: "Set grant matching existing grant",
existing: []string{"id=1;actions=read", "id=*;type=*;actions=delete"},
set: []string{"id=*;type=*;actions=delete"},
result: []string{"id=*;type=*;actions=delete"},
},
{
name: "Set duplicate grant matching existing grant",
existing: []string{"id=1;actions=read", "id=*;type=*;actions=delete"},
set: []string{"id=*;type=*;actions=delete", "id=*;type=*;actions=delete"},
result: []string{"id=*;type=*;actions=delete"},
},
{
name: "Set empty on role",
existing: []string{"id=1;type=*;actions=read", "id=*;type=*;actions=delete"},
set: nil,
result: nil,
},
}
for _, tc := range setCases {
o, p := iam.TestScopes(t, iamRepo)
for _, scope := range []*iam.Scope{o, p} {
t.Run(tc.name+"_"+scope.Type, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
role := iam.TestRole(t, conn, scope.GetPublicId())
for _, e := range tc.existing {
_ = iam.TestRoleGrant(t, conn, role.GetPublicId(), e)
}
req := &pbs.SetRoleGrantsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
}
scopeId := o.GetPublicId()
if o != scope {
scopeId = p.GetPublicId()
}
req.GrantStrings = tc.set
got, err := s.SetRoleGrants(auth.DisabledAuthTestContext(repoFn, scopeId), req)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err, "Got error %v", err)
checkEqualGrants(t, tc.result, got.GetItem())
})
}
}
_, p := iam.TestScopes(t, iamRepo)
role := iam.TestRole(t, conn, p.GetPublicId())
failCases := []struct {
name string
req *pbs.SetRoleGrantsRequest
err error
}{
{
name: "Bad Version",
req: &pbs.SetRoleGrantsRequest{
Id: role.GetPublicId(),
GrantStrings: []string{"id=*;type=*;actions=create"},
Version: role.GetVersion() + 2,
},
err: handlers.ApiErrorWithCode(codes.Internal),
},
{
name: "Bad Role Id",
req: &pbs.SetRoleGrantsRequest{
Id: "bad id",
GrantStrings: []string{"id=*;type=*;actions=create"},
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Unparsable grant",
req: &pbs.SetRoleGrantsRequest{
Id: role.GetPublicId(),
GrantStrings: []string{"id=*;type=*;actions=create", "unparseable"},
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range failCases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
_, gErr := s.SetRoleGrants(auth.DisabledAuthTestContext(repoFn, p.GetPublicId()), tc.req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "SetRoleGrants(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
}
})
}
}
func TestRemoveGrants(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
s, err := roles.NewService(repoFn)
require.NoError(t, err, "Error when getting new role service.")
removeCases := []struct {
name string
existing []string
remove []string
result []string
wantErr bool
}{
{
name: "Remove all",
existing: []string{"id=1;type=*;actions=read"},
remove: []string{"id=1;type=*;actions=read"},
},
{
name: "Remove partial",
existing: []string{"id=1;type=*;actions=read", "id=2;type=*;actions=delete"},
remove: []string{"id=1;type=*;actions=read"},
result: []string{"id=2;type=*;actions=delete"},
},
{
name: "Remove duplicate",
existing: []string{"id=1;type=*;actions=read", "id=2;type=*;actions=delete"},
remove: []string{"id=1;type=*;actions=read", "id=1;type=*;actions=read"},
result: []string{"id=2;type=*;actions=delete"},
},
{
name: "Remove non existant",
existing: []string{"id=2;type=*;actions=delete"},
remove: []string{"id=1;type=*;actions=read"},
result: []string{"id=2;type=*;actions=delete"},
},
{
name: "Remove from empty role",
existing: []string{},
remove: []string{"id=1;type=*;actions=read"},
result: nil,
},
}
for _, tc := range removeCases {
o, p := iam.TestScopes(t, iamRepo)
for _, scope := range []*iam.Scope{o, p} {
t.Run(tc.name+"_"+scope.Type, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
role := iam.TestRole(t, conn, scope.GetPublicId())
for _, e := range tc.existing {
_ = iam.TestRoleGrant(t, conn, role.GetPublicId(), e)
}
req := &pbs.RemoveRoleGrantsRequest{
Id: role.GetPublicId(),
Version: role.GetVersion(),
}
scopeId := o.GetPublicId()
if o != scope {
scopeId = p.GetPublicId()
}
req.GrantStrings = tc.remove
got, err := s.RemoveRoleGrants(auth.DisabledAuthTestContext(repoFn, scopeId), req)
if tc.wantErr {
assert.Error(err)
return
}
s, ok := status.FromError(err)
assert.True(ok)
require.NoError(err, "Got error %v", s)
checkEqualGrants(t, tc.result, got.GetItem())
})
}
}
_, p := iam.TestScopes(t, iamRepo)
role := iam.TestRole(t, conn, p.GetPublicId())
failCases := []struct {
name string
req *pbs.RemoveRoleGrantsRequest
err error
}{
{
name: "Bad Version",
req: &pbs.RemoveRoleGrantsRequest{
Id: role.GetPublicId(),
GrantStrings: []string{"id=2;type=*;actions=create"},
Version: role.GetVersion() + 2,
},
err: handlers.ApiErrorWithCode(codes.Internal),
},
{
name: "Bad Role Id",
req: &pbs.RemoveRoleGrantsRequest{
Id: "bad id",
GrantStrings: []string{"id=*;type=*;actions=create"},
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Empty Grant",
req: &pbs.RemoveRoleGrantsRequest{
Id: role.GetPublicId(),
GrantStrings: []string{"id=*;type=*;actions=create", ""},
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
{
name: "Unparseable Grant",
req: &pbs.RemoveRoleGrantsRequest{
Id: role.GetPublicId(),
GrantStrings: []string{"id=*;type=*;actions=create", ";unparsable=2"},
Version: role.GetVersion(),
},
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
},
}
for _, tc := range failCases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
_, gErr := s.RemoveRoleGrants(auth.DisabledAuthTestContext(repoFn, p.GetPublicId()), tc.req)
if tc.err != nil {
require.Error(gErr)
assert.True(errors.Is(gErr, tc.err), "RemoveRoleGrants(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
}
})
}
}