From 1c2c078e0a5097d627a6d554c38bc3711c78730f Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Fri, 4 Sep 2020 14:48:11 -0700 Subject: [PATCH] Adding Authz checks that support new pathing (#328) --- api/accounts/account_test.go | 9 +- api/authtokens/authtoken_test.go | 11 +- api/groups/group_test.go | 9 +- api/hostcatalogs/host_catalog_test.go | 9 +- api/hosts/host_test.go | 9 +- api/hostsets/host_set_test.go | 5 +- api/roles/role_test.go | 9 +- api/scopes/project_test.go | 2 +- api/users/user_test.go | 8 +- internal/auth/auth.go | 1 + internal/auth/option.go | 8 + internal/auth/subtype.go | 5 +- internal/gen/controller.swagger.json | 60 +-- .../api/services/role_service.pb.go | 493 +++++++++--------- .../api/services/role_service.pb.gw.go | 216 ++++---- internal/host/subtype.go | 8 +- internal/iam/repository_group.go | 4 + internal/iam/repository_group_test.go | 3 +- internal/iam/repository_role.go | 4 + internal/iam/repository_role_test.go | 3 +- internal/iam/repository_user.go | 4 + internal/iam/repository_user_test.go | 3 +- internal/iam/user_test.go | 3 +- .../api/services/v1/role_service.proto | 36 +- internal/servers/controller/handler.go | 6 +- .../handlers/accounts/account_service.go | 116 +++-- .../handlers/accounts/account_service_test.go | 20 +- .../authenticate/authenticate_service.go | 37 +- .../authmethods/authmethod_service.go | 101 +++- .../authmethods/authmethod_service_test.go | 50 +- .../handlers/authtokens/authtoken_service.go | 81 ++- .../authtokens/authtoken_service_test.go | 49 +- .../handlers/groups/group_service.go | 112 ++-- .../handlers/groups/group_service_test.go | 50 +- .../host_catalogs/host_catalog_service.go | 94 +++- .../host_catalog_service_test.go | 93 ++-- .../handlers/host_sets/host_set_service.go | 170 ++---- .../controller/handlers/hosts/host_service.go | 111 ++-- .../handlers/hosts/host_service_test.go | 13 +- .../controller/handlers/roles/role_service.go | 173 +++--- .../handlers/roles/role_service_test.go | 50 +- .../handlers/scopes/scope_service.go | 116 +++-- .../handlers/scopes/scope_service_test.go | 26 +- .../handlers/targets/target_service.go | 107 ++-- .../controller/handlers/users/user_service.go | 86 ++- .../handlers/users/user_service_test.go | 15 +- 46 files changed, 1433 insertions(+), 1165 deletions(-) diff --git a/api/accounts/account_test.go b/api/accounts/account_test.go index b75f6a697..6e34c8fd4 100644 --- a/api/accounts/account_test.go +++ b/api/accounts/account_test.go @@ -124,11 +124,6 @@ func TestAccount_Crud(t *testing.T) { require.NoError(err) assert.Nil(apiErr) assert.True(existed, "Expected existing account when deleted, but it wasn't.") - - existed, apiErr, err = accountClient.Delete(tc.Context(), amId, u.Id) - require.NoError(err) - assert.Nil(apiErr) - assert.False(existed, "Expected account to not exist when deleted, but it did.") } func TestAccount_CustomMethods(t *testing.T) { @@ -198,12 +193,12 @@ func TestAccount_Errors(t *testing.T) { _, apiErr, err = accountClient.Read(tc.Context(), amId, password.AccountPrefix+"_doesntexis") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusNotFound, apiErr.Status) + assert.EqualValues(http.StatusForbidden, apiErr.Status) _, apiErr, err = accountClient.Read(tc.Context(), amId, "invalid id") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) _, apiErr, err = accountClient.Update(tc.Context(), amId, u.Id, u.Version) require.NoError(err) diff --git a/api/authtokens/authtoken_test.go b/api/authtokens/authtoken_test.go index 494ecc9d4..60f4cc0fb 100644 --- a/api/authtokens/authtoken_test.go +++ b/api/authtokens/authtoken_test.go @@ -119,12 +119,7 @@ func TestAuthToken_Crud(t *testing.T) { existed, _, err := tokens.Delete(tc.Context(), at.Id) require.NoError(err) assert.Nil(apiErr) - assert.True(existed, "Expected existing user when deleted, but it wasn't.") - - existed, apiErr, err = tokens.Delete(tc.Context(), at.Id) - require.NoError(err) - assert.Nil(apiErr) - assert.False(existed, "Expected user to not exist when deleted, but it did.") + assert.True(existed, "Expected existing token when deleted, but it wasn't.") } func TestAuthToken_Errors(t *testing.T) { @@ -144,10 +139,10 @@ func TestAuthToken_Errors(t *testing.T) { _, apiErr, err := tokens.Read(tc.Context(), authtoken.AuthTokenPrefix+"_doesntexis") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusNotFound, apiErr.Status) + assert.EqualValues(http.StatusForbidden, apiErr.Status) _, apiErr, err = tokens.Read(tc.Context(), "invalid id") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) } diff --git a/api/groups/group_test.go b/api/groups/group_test.go index 4a6632d5f..6aad74a81 100644 --- a/api/groups/group_test.go +++ b/api/groups/group_test.go @@ -170,8 +170,9 @@ func TestGroup_Crud(t *testing.T) { assert.True(existed, "Expected existing user when deleted, but it wasn't.") existed, apiErr, err = groupsClient.Delete(tc.Context(), g.Id) - assert.NoError(err) - assert.False(existed, "Expected user to not exist when deleted, but it did.") + require.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusForbidden, apiErr.Status) }) } } @@ -224,12 +225,12 @@ func TestGroup_Errors(t *testing.T) { _, apiErr, err = groupClient.Read(tc.Context(), iam.GroupPrefix+"_doesntexis") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusNotFound, apiErr.Status) + assert.EqualValues(http.StatusForbidden, apiErr.Status) _, apiErr, err = groupClient.Read(tc.Context(), "invalid id") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) _, apiErr, err = groupClient.Update(tc.Context(), g.Id, g.Version) require.NoError(err) diff --git a/api/hostcatalogs/host_catalog_test.go b/api/hostcatalogs/host_catalog_test.go index 9e47e47ce..666134c2c 100644 --- a/api/hostcatalogs/host_catalog_test.go +++ b/api/hostcatalogs/host_catalog_test.go @@ -125,8 +125,9 @@ func TestCatalogs_Crud(t *testing.T) { assert.True(existed, "Expected existing catalog when deleted, but it wasn't.") existed, apiErr, err = hcClient.Delete(tc.Context(), hc.Id) - assert.NoError(err) - assert.False(existed, "Expected catalog to not exist when deleted, but it did.") + require.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusForbidden, apiErr.Status) } // TODO: Get better coverage for expected errors and error formats. @@ -159,10 +160,10 @@ func TestCatalogs_Errors(t *testing.T) { require.NoError(err) // TODO: Should this be nil instead of just a catalog that has no values set assert.NotNil(apiErr) - assert.EqualValues(apiErr.Status, http.StatusNotFound) + assert.EqualValues(apiErr.Status, http.StatusForbidden) _, apiErr, err = pc.Read(tc.Context(), "invalid id") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) } diff --git a/api/hosts/host_test.go b/api/hosts/host_test.go index 6fb43e0bf..616a030c2 100644 --- a/api/hosts/host_test.go +++ b/api/hosts/host_test.go @@ -137,8 +137,9 @@ func TestHost_Crud(t *testing.T) { assert.True(existed, "Expected existing catalog when deleted, but it wasn't.") existed, apiErr, err = hClient.Delete(tc.Context(), hc.Id, h.Id) - assert.NoError(err) - assert.False(existed, "Expected catalog to not exist when deleted, but it did.") + require.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusForbidden, apiErr.Status) } // TODO: Get better coverage for expected errors and error formats. @@ -176,10 +177,10 @@ func TestHost_Errors(t *testing.T) { _, apiErr, err = hClient.Read(tc.Context(), hc.Id, static.HostPrefix+"_doesntexis") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(apiErr.Status, http.StatusNotFound) + assert.EqualValues(apiErr.Status, http.StatusForbidden) _, apiErr, err = hClient.Read(tc.Context(), hc.Id, "invalid id") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) } diff --git a/api/hostsets/host_set_test.go b/api/hostsets/host_set_test.go index 38a19505c..b61c1259f 100644 --- a/api/hostsets/host_set_test.go +++ b/api/hostsets/host_set_test.go @@ -237,8 +237,9 @@ func TestSet_Crud(t *testing.T) { } else { existed, apiErr, err = hClient.Delete(tc.Context(), hc.Id, h.Id) } - assert.NoError(err) - assert.False(existed, "Expected catalog to not exist when deleted, but it did.") + require.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusForbidden, apiErr.Status) }) } } diff --git a/api/roles/role_test.go b/api/roles/role_test.go index fef860b33..db5966458 100644 --- a/api/roles/role_test.go +++ b/api/roles/role_test.go @@ -274,11 +274,6 @@ func TestRole_Crud(t *testing.T) { require.NoError(err) assert.Nil(apiErr) assert.True(existed, "Expected existing user when deleted, but it wasn't.") - - existed, apiErr, err = roleClient.Delete(tc.Context(), g.Id) - require.NoError(err) - assert.Nil(apiErr) - assert.False(existed, "Expected user to not exist when deleted, but it did.") }) } } @@ -330,12 +325,12 @@ func TestRole_Errors(t *testing.T) { _, apiErr, err = roleClient.Read(tc.Context(), iam.RolePrefix+"_doesntexis") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusNotFound, apiErr.Status) + assert.EqualValues(http.StatusForbidden, apiErr.Status) _, apiErr, err = roleClient.Read(tc.Context(), "invalid id") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) _, apiErr, err = roleClient.Update(tc.Context(), u.Id, u.Version) require.NoError(err) diff --git a/api/scopes/project_test.go b/api/scopes/project_test.go index e801490de..4100a773d 100644 --- a/api/scopes/project_test.go +++ b/api/scopes/project_test.go @@ -147,5 +147,5 @@ func TestProject_Errors(t *testing.T) { _, apiErr, err = scps.Read(tc.Context(), "invalid id") assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) } diff --git a/api/users/user_test.go b/api/users/user_test.go index 99458e9c9..c9bff7418 100644 --- a/api/users/user_test.go +++ b/api/users/user_test.go @@ -123,8 +123,8 @@ func TestUser_Crud(t *testing.T) { existed, apiErr, err = userClient.Delete(tc.Context(), u.Id) require.NoError(err) - assert.Nil(apiErr) - assert.False(existed, "Expected user to not exist when deleted, but it did.") + assert.NotNil(apiErr) + assert.EqualValues(http.StatusForbidden, apiErr.Status) } func TestUser_Errors(t *testing.T) { @@ -156,12 +156,12 @@ func TestUser_Errors(t *testing.T) { _, apiErr, err = userClient.Read(tc.Context(), iam.UserPrefix+"_doesntexis") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusNotFound, apiErr.Status) + assert.EqualValues(http.StatusForbidden, apiErr.Status) _, apiErr, err = userClient.Read(tc.Context(), "invalid id") require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) _, apiErr, err = userClient.Update(tc.Context(), u.Id, u.Version) require.NoError(err) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 60837d7d3..01a0bd4a2 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -156,6 +156,7 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { ScopeId: opts.withScopeId, Id: opts.withId, Pin: opts.withPin, + Type: opts.withType, } } else { // This remains for legacy diff --git a/internal/auth/option.go b/internal/auth/option.go index 46cc964ee..a5ec1f4a9 100644 --- a/internal/auth/option.go +++ b/internal/auth/option.go @@ -3,6 +3,7 @@ package auth import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" ) func getOpts(opt ...Option) options { @@ -22,6 +23,7 @@ type options struct { withPin string withId string withAction action.Type + withType resource.Type withUserId string withKms *kms.Kms } @@ -57,6 +59,12 @@ func WithAction(action action.Type) Option { } } +func WithType(rt resource.Type) Option { + return func(o *options) { + o.withType = rt + } +} + func WithUserId(id string) Option { return func(o *options) { o.withUserId = id diff --git a/internal/auth/subtype.go b/internal/auth/subtype.go index 78bfa2344..33ef624b1 100644 --- a/internal/auth/subtype.go +++ b/internal/auth/subtype.go @@ -36,9 +36,8 @@ func SubtypeFromType(t string) SubType { // Returns UnknownSubtype if no SubType with this id's prefix is found. func SubtypeFromId(id string) SubType { switch { - case strings.HasPrefix(strings.TrimSpace(id), password.AuthMethodPrefix): - fallthrough - case strings.HasPrefix(strings.TrimSpace(id), password.AccountPrefix): + case strings.HasPrefix(strings.TrimSpace(id), password.AuthMethodPrefix), + strings.HasPrefix(strings.TrimSpace(id), password.AccountPrefix): return PasswordSubtype } return UnknownSubtype diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 3625ac3ca..41a28a458 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -1425,7 +1425,7 @@ ] } }, - "/v1/roles/{role_id}:add-grants": { + "/v1/roles/{id}:add-grants": { "post": { "summary": "Adds grants to a role", "operationId": "RoleService_AddRoleGrants", @@ -1439,7 +1439,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -1458,7 +1458,7 @@ ] } }, - "/v1/roles/{role_id}:add-principals": { + "/v1/roles/{id}:add-principals": { "post": { "summary": "Adds Users and/or Groups to a Role", "operationId": "RoleService_AddRolePrincipals", @@ -1472,7 +1472,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -1491,7 +1491,7 @@ ] } }, - "/v1/roles/{role_id}:remove-grants": { + "/v1/roles/{id}:remove-grants": { "post": { "summary": "Removes grants from a role.", "operationId": "RoleService_RemoveRoleGrants", @@ -1505,7 +1505,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -1524,7 +1524,7 @@ ] } }, - "/v1/roles/{role_id}:remove-principals": { + "/v1/roles/{id}:remove-principals": { "post": { "summary": "Removes the specified Users and/or Groups from a Role.", "operationId": "RoleService_RemoveRolePrincipals", @@ -1538,7 +1538,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -1557,7 +1557,7 @@ ] } }, - "/v1/roles/{role_id}:set-grants": { + "/v1/roles/{id}:set-grants": { "post": { "summary": "Set grants for a role, removing any grants that are not specified in the request.", "operationId": "RoleService_SetRoleGrants", @@ -1571,7 +1571,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -1590,7 +1590,7 @@ ] } }, - "/v1/roles/{role_id}:set-principals": { + "/v1/roles/{id}:set-principals": { "post": { "summary": "Set Users and/or Groups to a Role, removing any principals that are not specified in the request.", "operationId": "RoleService_SetRolePrincipals", @@ -1604,7 +1604,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -2900,7 +2900,7 @@ ] } }, - "/v1/scopes/*/roles/{role_id}:add-grants": { + "/v1/scopes/*/roles/{id}:add-grants": { "post": { "summary": "Adds grants to a role", "operationId": "RoleService_AddRoleGrants2", @@ -2914,7 +2914,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -2933,7 +2933,7 @@ ] } }, - "/v1/scopes/*/roles/{role_id}:add-principals": { + "/v1/scopes/*/roles/{id}:add-principals": { "post": { "summary": "Adds Users and/or Groups to a Role", "operationId": "RoleService_AddRolePrincipals2", @@ -2947,7 +2947,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -2966,7 +2966,7 @@ ] } }, - "/v1/scopes/*/roles/{role_id}:remove-grants": { + "/v1/scopes/*/roles/{id}:remove-grants": { "post": { "summary": "Removes grants from a role.", "operationId": "RoleService_RemoveRoleGrants2", @@ -2980,7 +2980,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -2999,7 +2999,7 @@ ] } }, - "/v1/scopes/*/roles/{role_id}:remove-principals": { + "/v1/scopes/*/roles/{id}:remove-principals": { "post": { "summary": "Removes the specified Users and/or Groups from a Role.", "operationId": "RoleService_RemoveRolePrincipals2", @@ -3013,7 +3013,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -3032,7 +3032,7 @@ ] } }, - "/v1/scopes/*/roles/{role_id}:set-grants": { + "/v1/scopes/*/roles/{id}:set-grants": { "post": { "summary": "Set grants for a role, removing any grants that are not specified in the request.", "operationId": "RoleService_SetRoleGrants2", @@ -3046,7 +3046,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -3065,7 +3065,7 @@ ] } }, - "/v1/scopes/*/roles/{role_id}:set-principals": { + "/v1/scopes/*/roles/{id}:set-principals": { "post": { "summary": "Set Users and/or Groups to a Role, removing any principals that are not specified in the request.", "operationId": "RoleService_SetRolePrincipals2", @@ -3079,7 +3079,7 @@ }, "parameters": [ { - "name": "role_id", + "name": "id", "in": "path", "required": true, "type": "string" @@ -4867,7 +4867,7 @@ "controller.api.services.v1.AddRoleGrantsRequest": { "type": "object", "properties": { - "role_id": { + "id": { "type": "string" }, "version": { @@ -4894,7 +4894,7 @@ "controller.api.services.v1.AddRolePrincipalsRequest": { "type": "object", "properties": { - "role_id": { + "id": { "type": "string" }, "version": { @@ -5476,7 +5476,7 @@ "controller.api.services.v1.RemoveRoleGrantsRequest": { "type": "object", "properties": { - "role_id": { + "id": { "type": "string" }, "version": { @@ -5503,7 +5503,7 @@ "controller.api.services.v1.RemoveRolePrincipalsRequest": { "type": "object", "properties": { - "role_id": { + "id": { "type": "string" }, "version": { @@ -5635,7 +5635,7 @@ "controller.api.services.v1.SetRoleGrantsRequest": { "type": "object", "properties": { - "role_id": { + "id": { "type": "string" }, "version": { @@ -5662,7 +5662,7 @@ "controller.api.services.v1.SetRolePrincipalsRequest": { "type": "object", "properties": { - "role_id": { + "id": { "type": "string" }, "version": { diff --git a/internal/gen/controller/api/services/role_service.pb.go b/internal/gen/controller/api/services/role_service.pb.go index 0ac5f6a7a..8a16434be 100644 --- a/internal/gen/controller/api/services/role_service.pb.go +++ b/internal/gen/controller/api/services/role_service.pb.go @@ -550,7 +550,7 @@ type AddRolePrincipalsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RoleId string `protobuf:"bytes,1,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` @@ -589,9 +589,9 @@ func (*AddRolePrincipalsRequest) Descriptor() ([]byte, []int) { return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{10} } -func (x *AddRolePrincipalsRequest) GetRoleId() string { +func (x *AddRolePrincipalsRequest) GetId() string { if x != nil { - return x.RoleId + return x.Id } return "" } @@ -662,7 +662,7 @@ type SetRolePrincipalsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RoleId string `protobuf:"bytes,1,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` @@ -701,9 +701,9 @@ func (*SetRolePrincipalsRequest) Descriptor() ([]byte, []int) { return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{12} } -func (x *SetRolePrincipalsRequest) GetRoleId() string { +func (x *SetRolePrincipalsRequest) GetId() string { if x != nil { - return x.RoleId + return x.Id } return "" } @@ -774,7 +774,7 @@ type RemoveRolePrincipalsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RoleId string `protobuf:"bytes,1,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` @@ -813,9 +813,9 @@ func (*RemoveRolePrincipalsRequest) Descriptor() ([]byte, []int) { return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{14} } -func (x *RemoveRolePrincipalsRequest) GetRoleId() string { +func (x *RemoveRolePrincipalsRequest) GetId() string { if x != nil { - return x.RoleId + return x.Id } return "" } @@ -886,7 +886,7 @@ type AddRoleGrantsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RoleId string `protobuf:"bytes,1,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` @@ -925,9 +925,9 @@ func (*AddRoleGrantsRequest) Descriptor() ([]byte, []int) { return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{16} } -func (x *AddRoleGrantsRequest) GetRoleId() string { +func (x *AddRoleGrantsRequest) GetId() string { if x != nil { - return x.RoleId + return x.Id } return "" } @@ -998,7 +998,7 @@ type SetRoleGrantsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RoleId string `protobuf:"bytes,1,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` @@ -1037,9 +1037,9 @@ func (*SetRoleGrantsRequest) Descriptor() ([]byte, []int) { return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{18} } -func (x *SetRoleGrantsRequest) GetRoleId() string { +func (x *SetRoleGrantsRequest) GetId() string { if x != nil { - return x.RoleId + return x.Id } return "" } @@ -1110,7 +1110,7 @@ type RemoveRoleGrantsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RoleId string `protobuf:"bytes,1,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` @@ -1149,9 +1149,9 @@ func (*RemoveRoleGrantsRequest) Descriptor() ([]byte, []int) { return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{20} } -func (x *RemoveRoleGrantsRequest) GetRoleId() string { +func (x *RemoveRoleGrantsRequest) GetId() string { if x != nil { - return x.RoleId + return x.Id } return "" } @@ -1286,163 +1286,159 @@ var file_controller_api_services_v1_role_service_proto_rawDesc = []byte{ 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2e, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, - 0x78, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x73, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, + 0x78, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x6a, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, - 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, - 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x19, 0x41, - 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x73, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, + 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, + 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, + 0x64, 0x73, 0x22, 0x58, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, + 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x6a, 0x0a, 0x18, + 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, + 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x52, + 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, + 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x22, 0x6d, 0x0a, 0x1b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, - 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, - 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x19, 0x53, 0x65, - 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x22, 0x76, 0x0a, 0x1b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, - 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, - 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, - 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x5b, 0x0a, 0x1c, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, - 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x6f, 0x0a, 0x14, 0x41, 0x64, 0x64, - 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, 0x61, - 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x54, 0x0a, 0x15, 0x41, 0x64, - 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x22, 0x6f, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x67, - 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x73, 0x22, 0x54, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, - 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x72, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, - 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x57, 0x0a, 0x18, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x32, 0xaf, 0x15, 0x0a, 0x0b, 0x52, 0x6f, 0x6c, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0xb8, 0x01, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, - 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x54, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x37, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, - 0x7d, 0x5a, 0x1f, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, - 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x14, 0x12, 0x12, 0x47, 0x65, 0x74, - 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, - 0xae, 0x01, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x2a, 0x12, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x5a, 0x1d, 0x12, - 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x7b, 0x73, 0x63, 0x6f, - 0x70, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x92, 0x41, 0x11, 0x12, - 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x73, - 0x12, 0xd4, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, - 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x22, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, - 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x5a, 0x2e, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x73, 0x63, 0x6f, 0x70, - 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, - 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x17, - 0x12, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, - 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0xc9, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x70, + 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, + 0x73, 0x22, 0x5b, 0x0a, 0x1c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, + 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x66, + 0x0a, 0x14, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x24, 0x0a, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x54, 0x0a, 0x15, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, + 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x66, 0x0a, 0x14, + 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, + 0x0a, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x73, 0x22, 0x54, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, + 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x69, 0x0a, 0x17, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x24, 0x0a, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x57, 0x0a, 0x18, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, + 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0xf2, + 0x14, 0x0a, 0x0b, 0x52, 0x6f, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xb8, + 0x01, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x54, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x12, 0x0e, 0x2f, 0x76, 0x31, + 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x5a, 0x1f, 0x12, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x92, 0x41, 0x14, 0x12, 0x12, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, + 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0xae, 0x01, 0x0a, 0x09, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x43, 0x32, 0x0e, 0x2f, - 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x5a, 0x25, 0x32, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x92, 0x41, 0x10, 0x12, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x52, - 0x6f, 0x6c, 0x65, 0x12, 0xb1, 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, - 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x2a, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x72, - 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x5a, 0x19, 0x2a, 0x17, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, - 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x10, 0x12, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, - 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x95, 0x02, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x52, - 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x34, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x12, 0x09, 0x2f, 0x76, + 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x5a, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x7b, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x92, 0x41, 0x11, 0x12, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x73, + 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0xd4, 0x01, 0x0a, 0x0a, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, + 0x22, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x5a, 0x2e, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, + 0x7b, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, + 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, + 0x65, 0x12, 0xc9, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, + 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x5c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x43, 0x32, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, + 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x5a, 0x25, 0x32, + 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, + 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x10, 0x12, 0x0e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0xb1, 0x01, + 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x2b, 0x2a, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x5a, 0x19, 0x2a, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, + 0x10, 0x12, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, + 0x65, 0x12, 0x8b, 0x02, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, + 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, + 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, - 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, - 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x92, 0x01, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x65, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, - 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x70, 0x72, 0x69, - 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x36, 0x22, 0x2b, 0x2f, 0x76, - 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, - 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x70, + 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x88, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5b, 0x22, 0x1d, 0x2f, + 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, + 0x64, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x5a, + 0x31, 0x22, 0x26, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, + 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x24, 0x12, 0x22, 0x41, 0x64, 0x64, 0x73, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, - 0xd4, 0x02, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, + 0xca, 0x02, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, @@ -1450,96 +1446,93 @@ var file_controller_api_services_v1_role_service_proto_rawDesc = []byte{ 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0xd1, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x65, 0x22, 0x22, 0x2f, 0x76, 0x31, - 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, - 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, - 0x01, 0x2a, 0x5a, 0x36, 0x22, 0x2b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, - 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, - 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, - 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x92, 0x41, 0x63, 0x12, 0x61, 0x53, 0x65, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, - 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x6f, 0x20, - 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, - 0x20, 0x61, 0x6e, 0x79, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x12, 0xb8, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, - 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, - 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0xac, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x6b, 0x22, 0x25, 0x2f, 0x76, 0x31, - 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, - 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, - 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x39, 0x22, 0x2e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, - 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, - 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72, 0x69, + 0x73, 0x65, 0x22, 0xc7, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5b, 0x22, 0x1d, 0x2f, 0x76, 0x31, + 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, + 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x31, 0x22, + 0x26, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, + 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x38, 0x12, 0x36, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, - 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, - 0x2e, 0x12, 0xf3, 0x01, 0x0a, 0x0d, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, - 0x6e, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x63, 0x12, 0x61, 0x53, 0x65, 0x74, 0x20, 0x55, + 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, + 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63, + 0x69, 0x70, 0x61, 0x6c, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x12, 0xae, 0x02, 0x0a, + 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, + 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5d, - 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, - 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, - 0x3a, 0x01, 0x2a, 0x5a, 0x32, 0x22, 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, - 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, - 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x17, - 0x12, 0x15, 0x41, 0x64, 0x64, 0x73, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, - 0x20, 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0xb0, 0x02, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x52, - 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, - 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, + 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa2, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x61, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, + 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x34, 0x22, 0x29, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, + 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, + 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x92, 0x41, 0x38, 0x12, 0x36, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x55, 0x73, 0x65, + 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x12, 0xe9, 0x01, + 0x0a, 0x0d, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, + 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, + 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x53, 0x22, 0x19, 0x2f, 0x76, + 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, + 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x2d, 0x22, 0x22, 0x2f, 0x76, + 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, + 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, + 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, + 0x41, 0x17, 0x12, 0x15, 0x41, 0x64, 0x64, 0x73, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, + 0x74, 0x6f, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0xa6, 0x02, 0x0a, 0x0d, 0x53, 0x65, + 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, - 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb9, - 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5d, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, - 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, - 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x32, 0x22, 0x27, 0x2f, 0x76, - 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, - 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x67, - 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x53, 0x12, 0x51, 0x53, 0x65, 0x74, 0x20, 0x67, 0x72, 0x61, - 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2c, 0x20, - 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x67, 0x72, 0x61, - 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, - 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x12, 0x89, 0x02, 0x0a, 0x10, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, - 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x89, 0x01, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x63, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, - 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, - 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x35, 0x22, 0x2a, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, - 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x73, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, - 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2e, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, + 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0xaf, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x53, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, + 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x67, 0x72, + 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x2d, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x53, 0x12, + 0x51, 0x53, 0x65, 0x74, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, + 0x20, 0x61, 0x6e, 0x79, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, + 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x12, 0xfe, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, + 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, + 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x7f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x59, 0x22, 0x1c, 0x2f, 0x76, 0x31, 0x2f, + 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x30, 0x22, 0x25, 0x2f, + 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x67, 0x72, + 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, + 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x72, 0x6f, + 0x6c, 0x65, 0x2e, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, + 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/gen/controller/api/services/role_service.pb.gw.go b/internal/gen/controller/api/services/role_service.pb.gw.go index 9afa3c8b5..2c6917ea9 100644 --- a/internal/gen/controller/api/services/role_service.pb.gw.go +++ b/internal/gen/controller/api/services/role_service.pb.gw.go @@ -700,14 +700,14 @@ func request_RoleService_AddRolePrincipals_0(ctx context.Context, marshaler runt _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.AddRolePrincipals(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -734,14 +734,14 @@ func local_request_RoleService_AddRolePrincipals_0(ctx context.Context, marshale _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.AddRolePrincipals(ctx, &protoReq) @@ -768,14 +768,14 @@ func request_RoleService_AddRolePrincipals_1(ctx context.Context, marshaler runt _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.AddRolePrincipals(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -802,14 +802,14 @@ func local_request_RoleService_AddRolePrincipals_1(ctx context.Context, marshale _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.AddRolePrincipals(ctx, &protoReq) @@ -836,14 +836,14 @@ func request_RoleService_SetRolePrincipals_0(ctx context.Context, marshaler runt _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.SetRolePrincipals(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -870,14 +870,14 @@ func local_request_RoleService_SetRolePrincipals_0(ctx context.Context, marshale _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.SetRolePrincipals(ctx, &protoReq) @@ -904,14 +904,14 @@ func request_RoleService_SetRolePrincipals_1(ctx context.Context, marshaler runt _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.SetRolePrincipals(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -938,14 +938,14 @@ func local_request_RoleService_SetRolePrincipals_1(ctx context.Context, marshale _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.SetRolePrincipals(ctx, &protoReq) @@ -972,14 +972,14 @@ func request_RoleService_RemoveRolePrincipals_0(ctx context.Context, marshaler r _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.RemoveRolePrincipals(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -1006,14 +1006,14 @@ func local_request_RoleService_RemoveRolePrincipals_0(ctx context.Context, marsh _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.RemoveRolePrincipals(ctx, &protoReq) @@ -1040,14 +1040,14 @@ func request_RoleService_RemoveRolePrincipals_1(ctx context.Context, marshaler r _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.RemoveRolePrincipals(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -1074,14 +1074,14 @@ func local_request_RoleService_RemoveRolePrincipals_1(ctx context.Context, marsh _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.RemoveRolePrincipals(ctx, &protoReq) @@ -1108,14 +1108,14 @@ func request_RoleService_AddRoleGrants_0(ctx context.Context, marshaler runtime. _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.AddRoleGrants(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -1142,14 +1142,14 @@ func local_request_RoleService_AddRoleGrants_0(ctx context.Context, marshaler ru _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.AddRoleGrants(ctx, &protoReq) @@ -1176,14 +1176,14 @@ func request_RoleService_AddRoleGrants_1(ctx context.Context, marshaler runtime. _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.AddRoleGrants(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -1210,14 +1210,14 @@ func local_request_RoleService_AddRoleGrants_1(ctx context.Context, marshaler ru _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.AddRoleGrants(ctx, &protoReq) @@ -1244,14 +1244,14 @@ func request_RoleService_SetRoleGrants_0(ctx context.Context, marshaler runtime. _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.SetRoleGrants(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -1278,14 +1278,14 @@ func local_request_RoleService_SetRoleGrants_0(ctx context.Context, marshaler ru _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.SetRoleGrants(ctx, &protoReq) @@ -1312,14 +1312,14 @@ func request_RoleService_SetRoleGrants_1(ctx context.Context, marshaler runtime. _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.SetRoleGrants(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -1346,14 +1346,14 @@ func local_request_RoleService_SetRoleGrants_1(ctx context.Context, marshaler ru _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.SetRoleGrants(ctx, &protoReq) @@ -1380,14 +1380,14 @@ func request_RoleService_RemoveRoleGrants_0(ctx context.Context, marshaler runti _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.RemoveRoleGrants(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -1414,14 +1414,14 @@ func local_request_RoleService_RemoveRoleGrants_0(ctx context.Context, marshaler _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.RemoveRoleGrants(ctx, &protoReq) @@ -1448,14 +1448,14 @@ func request_RoleService_RemoveRoleGrants_1(ctx context.Context, marshaler runti _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := client.RemoveRoleGrants(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -1482,14 +1482,14 @@ func local_request_RoleService_RemoveRoleGrants_1(ctx context.Context, marshaler _ = err ) - val, ok = pathParams["role_id"] + val, ok = pathParams["id"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "role_id") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") } - protoReq.RoleId, err = runtime.String(val) + protoReq.Id, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "role_id", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) } msg, err := server.RemoveRoleGrants(ctx, &protoReq) @@ -2610,29 +2610,29 @@ var ( pattern_RoleService_DeleteRole_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "id"}, "")) - pattern_RoleService_AddRolePrincipals_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "role_id"}, "add-principals")) + pattern_RoleService_AddRolePrincipals_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "add-principals")) - pattern_RoleService_AddRolePrincipals_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "role_id"}, "add-principals")) + pattern_RoleService_AddRolePrincipals_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "id"}, "add-principals")) - pattern_RoleService_SetRolePrincipals_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "role_id"}, "set-principals")) + pattern_RoleService_SetRolePrincipals_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "set-principals")) - pattern_RoleService_SetRolePrincipals_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "role_id"}, "set-principals")) + pattern_RoleService_SetRolePrincipals_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "id"}, "set-principals")) - pattern_RoleService_RemoveRolePrincipals_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "role_id"}, "remove-principals")) + pattern_RoleService_RemoveRolePrincipals_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "remove-principals")) - pattern_RoleService_RemoveRolePrincipals_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "role_id"}, "remove-principals")) + pattern_RoleService_RemoveRolePrincipals_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "id"}, "remove-principals")) - pattern_RoleService_AddRoleGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "role_id"}, "add-grants")) + pattern_RoleService_AddRoleGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "add-grants")) - pattern_RoleService_AddRoleGrants_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "role_id"}, "add-grants")) + pattern_RoleService_AddRoleGrants_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "id"}, "add-grants")) - pattern_RoleService_SetRoleGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "role_id"}, "set-grants")) + pattern_RoleService_SetRoleGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "set-grants")) - pattern_RoleService_SetRoleGrants_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "role_id"}, "set-grants")) + pattern_RoleService_SetRoleGrants_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "id"}, "set-grants")) - pattern_RoleService_RemoveRoleGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "role_id"}, "remove-grants")) + pattern_RoleService_RemoveRoleGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "remove-grants")) - pattern_RoleService_RemoveRoleGrants_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "role_id"}, "remove-grants")) + pattern_RoleService_RemoveRoleGrants_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "roles", "id"}, "remove-grants")) ) var ( diff --git a/internal/host/subtype.go b/internal/host/subtype.go index cfdbdbca1..300a465d9 100644 --- a/internal/host/subtype.go +++ b/internal/host/subtype.go @@ -32,11 +32,9 @@ func SubtypeFromType(t string) SubType { func SubtypeFromId(id string) SubType { switch { - case strings.HasPrefix(strings.TrimSpace(id), static.HostPrefix): - fallthrough - case strings.HasPrefix(strings.TrimSpace(id), static.HostSetPrefix): - fallthrough - case strings.HasPrefix(strings.TrimSpace(id), static.HostCatalogPrefix): + case strings.HasPrefix(strings.TrimSpace(id), static.HostPrefix), + strings.HasPrefix(strings.TrimSpace(id), static.HostSetPrefix), + strings.HasPrefix(strings.TrimSpace(id), static.HostCatalogPrefix): return StaticSubtype } return UnknownSubtype diff --git a/internal/iam/repository_group.go b/internal/iam/repository_group.go index fab6e8777..b440990ad 100644 --- a/internal/iam/repository_group.go +++ b/internal/iam/repository_group.go @@ -2,6 +2,7 @@ package iam import ( "context" + "errors" "fmt" "strings" @@ -139,6 +140,9 @@ func (r *Repository) LookupGroup(ctx context.Context, withPublicId string, opt . }, ) if err != nil { + if errors.Is(err, db.ErrRecordNotFound) { + return nil, nil, nil + } return nil, nil, fmt.Errorf("lookup group: failed %w for %s", err, withPublicId) } return &g, members, nil diff --git a/internal/iam/repository_group_test.go b/internal/iam/repository_group_test.go index 65829044f..876eef244 100644 --- a/internal/iam/repository_group_test.go +++ b/internal/iam/repository_group_test.go @@ -538,9 +538,8 @@ func TestRepository_DeleteGroup(t *testing.T) { assert.NoError(err) assert.Equal(tt.wantRowsDeleted, deletedRows) foundGroup, _, err := repo.LookupGroup(context.Background(), tt.args.group.PublicId) - assert.Error(err) + assert.NoError(err) assert.Nil(foundGroup) - assert.True(errors.Is(err, db.ErrRecordNotFound)) err = db.TestVerifyOplog(t, rw, tt.args.group.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) assert.NoError(err) diff --git a/internal/iam/repository_role.go b/internal/iam/repository_role.go index 5daaf2973..b02e6db7c 100644 --- a/internal/iam/repository_role.go +++ b/internal/iam/repository_role.go @@ -2,6 +2,7 @@ package iam import ( "context" + "errors" "fmt" "strings" @@ -150,6 +151,9 @@ func (r *Repository) LookupRole(ctx context.Context, withPublicId string, opt .. }, ) if err != nil { + if errors.Is(err, db.ErrRecordNotFound) { + return nil, nil, nil, nil + } return nil, nil, nil, err } return &role, pr, rg, nil diff --git a/internal/iam/repository_role_test.go b/internal/iam/repository_role_test.go index 336588173..7a56d7335 100644 --- a/internal/iam/repository_role_test.go +++ b/internal/iam/repository_role_test.go @@ -535,9 +535,8 @@ func TestRepository_DeleteRole(t *testing.T) { assert.NoError(err) assert.Equal(tt.wantRowsDeleted, deletedRows) foundRole, _, _, err := repo.LookupRole(context.Background(), tt.args.role.PublicId) - assert.Error(err) + assert.NoError(err) assert.Nil(foundRole) - assert.True(errors.Is(err, db.ErrRecordNotFound)) err = db.TestVerifyOplog(t, rw, tt.args.role.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) assert.NoError(err) diff --git a/internal/iam/repository_user.go b/internal/iam/repository_user.go index 590145f24..12b3d5304 100644 --- a/internal/iam/repository_user.go +++ b/internal/iam/repository_user.go @@ -2,6 +2,7 @@ package iam import ( "context" + "errors" "fmt" "strings" @@ -89,6 +90,9 @@ func (r *Repository) LookupUser(ctx context.Context, withPublicId string, opt .. user := allocUser() user.PublicId = withPublicId if err := r.reader.LookupByPublicId(ctx, &user); err != nil { + if errors.Is(err, db.ErrRecordNotFound) { + return nil, nil + } return nil, fmt.Errorf("lookup user: failed %w for %s", err, withPublicId) } return &user, nil diff --git a/internal/iam/repository_user_test.go b/internal/iam/repository_user_test.go index b95d426b7..797278eb7 100644 --- a/internal/iam/repository_user_test.go +++ b/internal/iam/repository_user_test.go @@ -437,9 +437,8 @@ func TestRepository_DeleteUser(t *testing.T) { require.NoError(err) assert.Equal(tt.wantRowsDeleted, deletedRows) foundUser, err := repo.LookupUser(context.Background(), tt.args.user.PublicId) - require.Error(err) + require.NoError(err) assert.Nil(foundUser) - assert.True(errors.Is(err, db.ErrRecordNotFound)) err = db.TestVerifyOplog(t, rw, tt.args.user.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) require.NoError(err) diff --git a/internal/iam/user_test.go b/internal/iam/user_test.go index 5ae5750a4..cfc7ca433 100644 --- a/internal/iam/user_test.go +++ b/internal/iam/user_test.go @@ -2,7 +2,6 @@ package iam import ( "context" - "errors" "testing" "github.com/hashicorp/boundary/internal/db" @@ -293,7 +292,7 @@ func Test_UserDelete(t *testing.T) { require.NoError(err) assert.Equal(tt.wantRowsDeleted, deletedRows) foundUser, err := repo.LookupUser(context.Background(), tt.user.GetPublicId()) - assert.True(errors.Is(err, db.ErrRecordNotFound)) + assert.NoError(err) assert.Nil(foundUser) }) } diff --git a/internal/proto/local/controller/api/services/v1/role_service.proto b/internal/proto/local/controller/api/services/v1/role_service.proto index 55b550b99..55d734ab3 100644 --- a/internal/proto/local/controller/api/services/v1/role_service.proto +++ b/internal/proto/local/controller/api/services/v1/role_service.proto @@ -113,11 +113,11 @@ service RoleService { // non-existing resources, an error is returned. rpc AddRolePrincipals(AddRolePrincipalsRequest) returns (AddRolePrincipalsResponse) { option (google.api.http) = { - post: "/v1/roles/{role_id}:add-principals" + post: "/v1/roles/{id}:add-principals" body: "*" response_body: "item" additional_bindings { - post: "/v1/scopes/*/roles/{role_id}:add-principals" + post: "/v1/scopes/*/roles/{id}:add-principals" body: "*" response_body: "item" } @@ -135,11 +135,11 @@ service RoleService { // returned. rpc SetRolePrincipals(SetRolePrincipalsRequest) returns (SetRolePrincipalsResponse) { option (google.api.http) = { - post: "/v1/roles/{role_id}:set-principals" + post: "/v1/roles/{id}:set-principals" body: "*" response_body: "item" additional_bindings { - post: "/v1/scopes/*/roles/{role_id}:set-principals" + post: "/v1/scopes/*/roles/{id}:set-principals" body: "*" response_body: "item" } @@ -156,11 +156,11 @@ service RoleService { // is not an error to have duplicate user/group ids. rpc RemoveRolePrincipals(RemoveRolePrincipalsRequest) returns (RemoveRolePrincipalsResponse) { option (google.api.http) = { - post: "/v1/roles/{role_id}:remove-principals" + post: "/v1/roles/{id}:remove-principals" body: "*" response_body: "item" additional_bindings { - post: "/v1/scopes/*/roles/{role_id}:remove-principals" + post: "/v1/scopes/*/roles/{id}:remove-principals" body: "*" response_body: "item" } @@ -176,11 +176,11 @@ service RoleService { // an error is returned. It is not an error to have duplicate grants. rpc AddRoleGrants(AddRoleGrantsRequest) returns (AddRoleGrantsResponse) { option (google.api.http) = { - post: "/v1/roles/{role_id}:add-grants" + post: "/v1/roles/{id}:add-grants" body: "*" response_body: "item" additional_bindings { - post: "/v1/scopes/*/roles/{role_id}:add-grants" + post: "/v1/scopes/*/roles/{id}:add-grants" body: "*" response_body: "item" } @@ -197,11 +197,11 @@ service RoleService { // error is returned. It is not an error to have duplicate grants. rpc SetRoleGrants(SetRoleGrantsRequest) returns (SetRoleGrantsResponse) { option (google.api.http) = { - post: "/v1/roles/{role_id}:set-grants" + post: "/v1/roles/{id}:set-grants" body: "*" response_body: "item" additional_bindings { - post: "/v1/scopes/*/roles/{role_id}:set-grants" + post: "/v1/scopes/*/roles/{id}:set-grants" body: "*" response_body: "item" } @@ -218,11 +218,11 @@ service RoleService { // have duplicate grants. rpc RemoveRoleGrants(RemoveRoleGrantsRequest) returns (RemoveRoleGrantsResponse) { option (google.api.http) = { - post: "/v1/roles/{role_id}:remove-grants" + post: "/v1/roles/{id}:remove-grants" body: "*" response_body: "item" additional_bindings { - post: "/v1/scopes/*/roles/{role_id}:remove-grants" + post: "/v1/scopes/*/roles/{id}:remove-grants" body: "*" response_body: "item" } @@ -281,7 +281,7 @@ message DeleteRoleResponse { } message AddRolePrincipalsRequest { - string role_id = 1; + string id = 1; // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. uint32 version = 2; @@ -293,7 +293,7 @@ message AddRolePrincipalsResponse { } message SetRolePrincipalsRequest { - string role_id = 1; + string id = 1; // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. uint32 version = 2; @@ -305,7 +305,7 @@ message SetRolePrincipalsResponse { } message RemoveRolePrincipalsRequest { - string role_id = 1; + string id = 1; // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. uint32 version = 2; @@ -317,7 +317,7 @@ message RemoveRolePrincipalsResponse { } message AddRoleGrantsRequest { - string role_id = 1; + string id = 1; // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. uint32 version = 2; @@ -329,7 +329,7 @@ message AddRoleGrantsResponse { } message SetRoleGrantsRequest { - string role_id = 1; + string id = 1; // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. uint32 version = 2; @@ -341,7 +341,7 @@ message SetRoleGrantsResponse { } message RemoveRoleGrantsRequest { - string role_id = 1; + string id = 1; // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. uint32 version = 2; diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index 0ab0d3843..68855a2a6 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -93,7 +93,7 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er runtime.WithErrorHandler(handlers.ErrorHandler(c.logger)), runtime.WithForwardResponseOption(handlers.OutgoingInterceptor), ) - hcs, err := host_catalogs.NewService(c.StaticHostRepoFn) + hcs, err := host_catalogs.NewService(c.StaticHostRepoFn, c.IamRepoFn) if err != nil { return nil, fmt.Errorf("failed to create host catalog handler service: %w", err) } @@ -128,14 +128,14 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er if err := services.RegisterAuthenticationServiceHandlerServer(ctx, mux, auths); err != nil { return nil, fmt.Errorf("failed to register authenticate service handler: %w", err) } - authMethods, err := authmethods.NewService(c.PasswordAuthRepoFn) + authMethods, err := authmethods.NewService(c.PasswordAuthRepoFn, c.IamRepoFn) if err != nil { return nil, fmt.Errorf("failed to create auth method handler service: %w", err) } if err := services.RegisterAuthMethodServiceHandlerServer(ctx, mux, authMethods); err != nil { return nil, fmt.Errorf("failed to register auth method service handler: %w", err) } - authtoks, err := authtokens.NewService(c.AuthTokenRepoFn) + authtoks, err := authtokens.NewService(c.AuthTokenRepoFn, c.IamRepoFn) if err != nil { return nil, fmt.Errorf("failed to create auth token handler service: %w", err) } diff --git a/internal/servers/controller/handlers/accounts/account_service.go b/internal/servers/controller/handlers/accounts/account_service.go index afd6a04b1..a84af6ec3 100644 --- a/internal/servers/controller/handlers/accounts/account_service.go +++ b/internal/servers/controller/handlers/accounts/account_service.go @@ -11,7 +11,10 @@ import ( "github.com/hashicorp/boundary/internal/db" pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/accounts" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" @@ -30,11 +33,11 @@ func init() { // Service handles request as described by the pbs.AccountServiceServer interface. type Service struct { - repoFn func() (*password.Repository, error) + repoFn common.PasswordAuthRepoFactory } // NewService returns a user service which handles user related requests to boundary. -func NewService(repo func() (*password.Repository, error)) (Service, error) { +func NewService(repo common.PasswordAuthRepoFactory) (Service, error) { if repo == nil { return Service{}, fmt.Errorf("nil password repository provided") } @@ -43,17 +46,15 @@ func NewService(repo func() (*password.Repository, error)) (Service, error) { var _ pbs.AccountServiceServer = Service{} -// TODO(ICU-407): Validate that the provided auth method and account are in the provided scope. - // ListAccounts implements the interface pbs.AccountServiceServer. func (s Service) ListAccounts(ctx context.Context, req *pbs.ListAccountsRequest) (*pbs.ListAccountsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateListRequest(req); err != nil { return nil, err } + _, authResults := s.parentAndAuthResult(ctx, req.GetAuthMethodId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } ul, err := s.listFromRepo(ctx, req.GetAuthMethodId()) if err != nil { return nil, err @@ -66,13 +67,13 @@ func (s Service) ListAccounts(ctx context.Context, req *pbs.ListAccountsRequest) // GetAccount implements the interface pbs.AccountServiceServer. func (s Service) GetAccount(ctx context.Context, req *pbs.GetAccountRequest) (*pbs.GetAccountResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateGetRequest(req); err != nil { return nil, err } + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -83,14 +84,14 @@ func (s Service) GetAccount(ctx context.Context, req *pbs.GetAccountRequest) (*p // CreateAccount implements the interface pbs.AccountServiceServer. func (s Service) CreateAccount(ctx context.Context, req *pbs.CreateAccountRequest) (*pbs.CreateAccountResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateCreateRequest(req); err != nil { return nil, err } - u, err := s.createInRepo(ctx, req.GetItem().GetAuthMethodId(), authResults.Scope.GetId(), req.GetItem()) + authMeth, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetAuthMethodId(), action.Create) + if authResults.Error != nil { + return nil, authResults.Error + } + u, err := s.createInRepo(ctx, authMeth.GetPublicId(), authResults.Scope.GetId(), req.GetItem()) if err != nil { return nil, err } @@ -100,14 +101,14 @@ func (s Service) CreateAccount(ctx context.Context, req *pbs.CreateAccountReques // UpdateAccount implements the interface pbs.AccountServiceServer. func (s Service) UpdateAccount(ctx context.Context, req *pbs.UpdateAccountRequest) (*pbs.UpdateAccountResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateUpdateRequest(req); err != nil { return nil, err } - u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + authMeth, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update) + if authResults.Error != nil { + return nil, authResults.Error + } + u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), authMeth.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) if err != nil { return nil, err } @@ -117,13 +118,13 @@ func (s Service) UpdateAccount(ctx context.Context, req *pbs.UpdateAccountReques // DeleteAccount implements the interface pbs.AccountServiceServer. func (s Service) DeleteAccount(ctx context.Context, req *pbs.DeleteAccountRequest) (*pbs.DeleteAccountResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateDeleteRequest(req); err != nil { return nil, err } + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } existed, err := s.deleteFromRepo(ctx, authResults.Scope.GetId(), req.GetId()) if err != nil { return nil, err @@ -133,13 +134,13 @@ func (s Service) DeleteAccount(ctx context.Context, req *pbs.DeleteAccountReques // ChangePassword implements the interface pbs.AccountServiceServer. func (s Service) ChangePassword(ctx context.Context, req *pbs.ChangePasswordRequest) (*pbs.ChangePasswordResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateChangePasswordRequest(req); err != nil { return nil, err } + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.ChangePassword) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.changePasswordInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetVersion(), req.GetOldPassword(), req.GetNewPassword()) if err != nil { return nil, err @@ -150,13 +151,13 @@ func (s Service) ChangePassword(ctx context.Context, req *pbs.ChangePasswordRequ // SetPassword implements the interface pbs.AccountServiceServer. func (s Service) SetPassword(ctx context.Context, req *pbs.SetPasswordRequest) (*pbs.SetPasswordResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateSetPasswordRequest(req); err != nil { return nil, err } + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.SetPassword) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.setPasswordInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetVersion(), req.GetPassword()) if err != nil { return nil, err @@ -218,7 +219,7 @@ func (s Service) createInRepo(ctx context.Context, authMethodId, scopeId string, return toProto(out) } -func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Account) (*pb.Account, error) { +func (s Service) updateInRepo(ctx context.Context, scopeId, authMethId, id string, mask []string, item *pb.Account) (*pb.Account, error) { var opts []password.Option if desc := item.GetDescription(); desc != nil { opts = append(opts, password.WithDescription(desc.GetValue())) @@ -226,7 +227,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st if name := item.GetName(); name != nil { opts = append(opts, password.WithName(name.GetValue())) } - u, err := password.NewAccount("ignored", opts...) + u, err := password.NewAccount(authMethId, opts...) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to build auth method for update: %v.", err) } @@ -269,7 +270,7 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, if errors.Is(err, db.ErrRecordNotFound) { return false, nil } - return false, status.Errorf(codes.Internal, "Unable to delete user: %v.", err) + return false, status.Errorf(codes.Internal, "Unable to delete account: %v.", err) } return rows > 0, nil } @@ -321,6 +322,47 @@ func (s Service) setPasswordInRepo(ctx context.Context, scopeId, id string, vers return toProto(out) } +func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (*password.AuthMethod, auth.VerifyResults) { + res := auth.VerifyResults{} + repo, err := s.repoFn() + if err != nil { + res.Error = err + return nil, res + } + + var parentId string + var authMeth *password.AuthMethod + opts := []auth.Option{auth.WithType(resource.Account), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + default: + acct, err := repo.LookupAccount(ctx, id) + if err != nil { + res.Error = err + return nil, res + } + if acct == nil { + res.Error = handlers.ForbiddenError() + return nil, res + } + parentId = acct.GetAuthMethodId() + opts = append(opts, auth.WithId(id)) + } + + authMeth, err = repo.LookupAuthMethod(ctx, parentId) + if err != nil { + res.Error = err + return nil, res + } + if authMeth == nil { + res.Error = handlers.ForbiddenError() + return nil, res + } + opts = append(opts, auth.WithScopeId(authMeth.GetScopeId()), auth.WithPin(parentId)) + return authMeth, auth.Verify(ctx, opts...) +} + func toProto(in *password.Account) (*pb.Account, error) { out := pb.Account{ Id: in.GetPublicId(), diff --git a/internal/servers/controller/handlers/accounts/account_service_test.go b/internal/servers/controller/handlers/accounts/account_service_test.go index 2be8ea32b..a832866d3 100644 --- a/internal/servers/controller/handlers/accounts/account_service_test.go +++ b/internal/servers/controller/handlers/accounts/account_service_test.go @@ -72,7 +72,7 @@ func TestGet(t *testing.T) { name: "Get a non existing account", req: &pbs.GetAccountRequest{Id: password.AccountPrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Wrong id prefix", @@ -162,12 +162,10 @@ func TestList(t *testing.T) { res: &pbs.ListAccountsResponse{}, errCode: codes.OK, }, - // TODO: When an auth method doesn't exist, we should return a 404 instead of an empty list. { name: "Unfound Auth Method", authMethod: password.AuthMethodPrefix + "_DoesntExis", - res: &pbs.ListAccountsResponse{}, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, } for _, tc := range cases { @@ -220,17 +218,13 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteAccountRequest{ Id: password.AccountPrefix + "_doesntexis", }, - res: &pbs.DeleteAccountResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, { name: "Bad account id formatting", req: &pbs.DeleteAccountRequest{ Id: "bad_format", }, - res: nil, errCode: codes.InvalidArgument, }, } @@ -267,8 +261,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteAccount(auth.DisabledAuthTestContext(auth.WithScopeId(o.GetPublicId())), req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") } func TestCreate(t *testing.T) { @@ -689,8 +683,6 @@ func TestUpdate(t *testing.T) { }, errCode: codes.OK, }, - // TODO: Updating a non existant auth_method should result in a NotFound exception but currently results in - // the repoFn returning an internal error. { name: "Update a Non Existing Account", req: &pbs.UpdateAccountRequest{ @@ -703,7 +695,7 @@ func TestUpdate(t *testing.T) { Description: &wrapperspb.StringValue{Value: "desc"}, }, }, - errCode: codes.Internal, + errCode: codes.PermissionDenied, }, { name: "Cant change Id", diff --git a/internal/servers/controller/handlers/authenticate/authenticate_service.go b/internal/servers/controller/handlers/authenticate/authenticate_service.go index 5ea8ac316..8d39faedb 100644 --- a/internal/servers/controller/handlers/authenticate/authenticate_service.go +++ b/internal/servers/controller/handlers/authenticate/authenticate_service.go @@ -15,6 +15,8 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -58,13 +60,13 @@ var _ pbs.AuthenticationServiceServer = Service{} // Authenticate implements the interface pbs.AuthenticationServiceServer. func (s Service) Authenticate(ctx context.Context, req *pbs.AuthenticateRequest) (*pbs.AuthenticateResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateAuthenticateRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetAuthMethodId(), action.Authenticate) + if authResults.Error != nil { + return nil, authResults.Error + } creds := req.GetCredentials().GetFields() tok, err := s.authenticateWithRepo(ctx, authResults.Scope.GetId(), req.GetAuthMethodId(), creds[loginNameKey].GetStringValue(), creds[pwKey].GetStringValue()) if err != nil { @@ -133,6 +135,32 @@ func (s Service) authenticateWithRepo(ctx context.Context, scopeId, authMethodId return prot, nil } +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} + + repo, err := s.pwRepo() + if err != nil { + res.Error = err + return res + } + authMeth, err := repo.LookupAuthMethod(ctx, id) + if err != nil { + res.Error = err + return res + } + if authMeth == nil { + res.Error = handlers.ForbiddenError() + return res + } + + authResults := auth.Verify(ctx, + auth.WithAction(a), + auth.WithType(resource.AuthMethod), + auth.WithScopeId(authMeth.GetScopeId()), + auth.WithId(id)) + return authResults +} + func toProto(t *authtoken.AuthToken) *pba.AuthToken { return &pba.AuthToken{ Id: t.GetPublicId(), @@ -169,7 +197,6 @@ func validateAuthenticateRequest(req *pbs.AuthenticateRequest) error { if _, ok := creds[pwKey]; !ok { badFields["credentials.password"] = "This is a required field." } - // TODO: Update this when we enable split cookie token types. tType := strings.ToLower(strings.TrimSpace(req.GetTokenType())) if tType != "" && tType != "token" && tType != "cookie" { badFields["token_type"] = "The only accepted type is 'token'." diff --git a/internal/servers/controller/handlers/authmethods/authmethod_service.go b/internal/servers/controller/handlers/authmethods/authmethod_service.go index a5c693f70..10141b44b 100644 --- a/internal/servers/controller/handlers/authmethods/authmethod_service.go +++ b/internal/servers/controller/handlers/authmethods/authmethod_service.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "regexp" "github.com/hashicorp/boundary/internal/auth" "github.com/hashicorp/boundary/internal/auth/password" @@ -12,7 +11,10 @@ import ( "github.com/hashicorp/boundary/internal/db" pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/authmethods" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -21,7 +23,6 @@ import ( var ( maskManager handlers.MaskManager - reInvalidID = regexp.MustCompile("[^A-Za-z0-9]") ) func init() { @@ -33,28 +34,32 @@ func init() { // Service handles request as described by the pbs.AuthMethodServiceServer interface. type Service struct { - repoFn func() (*password.Repository, error) + repoFn common.PasswordAuthRepoFactory + iamRepoFn common.IamRepoFactory } // NewService returns a auth method service which handles auth method related requests to boundary. -func NewService(repo func() (*password.Repository, error)) (Service, error) { +func NewService(repo common.PasswordAuthRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) { if repo == nil { + return Service{}, fmt.Errorf("nil password repository provided") + } + if iamRepoFn == nil { return Service{}, fmt.Errorf("nil iam repository provided") } - return Service{repoFn: repo}, nil + return Service{repoFn: repo, iamRepoFn: iamRepoFn}, nil } var _ pbs.AuthMethodServiceServer = Service{} // ListAuthMethods implements the interface pbs.AuthMethodServiceServer. func (s Service) ListAuthMethods(ctx context.Context, req *pbs.ListAuthMethodsRequest) (*pbs.ListAuthMethodsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateListRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetScopeId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } ul, err := s.listFromRepo(ctx, authResults.Scope.GetId()) if err != nil { return nil, err @@ -67,13 +72,13 @@ func (s Service) ListAuthMethods(ctx context.Context, req *pbs.ListAuthMethodsRe // GetAuthMethod implements the interface pbs.AuthMethodServiceServer. func (s Service) GetAuthMethod(ctx context.Context, req *pbs.GetAuthMethodRequest) (*pbs.GetAuthMethodResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateGetRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -84,14 +89,14 @@ func (s Service) GetAuthMethod(ctx context.Context, req *pbs.GetAuthMethodReques // CreateAuthMethod implements the interface pbs.AuthMethodServiceServer. func (s Service) CreateAuthMethod(ctx context.Context, req *pbs.CreateAuthMethodRequest) (*pbs.CreateAuthMethodResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateCreateRequest(req); err != nil { return nil, err } - u, err := s.createInRepo(ctx, req.GetItem().GetScopeId(), req.GetItem()) + authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create) + if authResults.Error != nil { + return nil, authResults.Error + } + u, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem()) if err != nil { return nil, err } @@ -101,13 +106,13 @@ func (s Service) CreateAuthMethod(ctx context.Context, req *pbs.CreateAuthMethod // UpdateAuthMethod implements the interface pbs.AuthMethodServiceServer. func (s Service) UpdateAuthMethod(ctx context.Context, req *pbs.UpdateAuthMethodRequest) (*pbs.UpdateAuthMethodResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateUpdateRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Update) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) if err != nil { return nil, err @@ -118,13 +123,13 @@ func (s Service) UpdateAuthMethod(ctx context.Context, req *pbs.UpdateAuthMethod // DeleteAuthMethod implements the interface pbs.AuthMethodServiceServer. func (s Service) DeleteAuthMethod(ctx context.Context, req *pbs.DeleteAuthMethodRequest) (*pbs.DeleteAuthMethodResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateDeleteRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } existed, err := s.deleteFromRepo(ctx, authResults.Scope.GetId(), req.GetId()) if err != nil { return nil, err @@ -255,6 +260,50 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, return rows > 0, nil } +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} + + var parentId string + opts := []auth.Option{auth.WithType(resource.AuthMethod), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + iamRepo, err := s.iamRepoFn() + if err != nil { + res.Error = err + return res + } + scp, err := iamRepo.LookupScope(ctx, parentId) + if err != nil { + res.Error = err + return res + } + if scp == nil { + res.Error = handlers.ForbiddenError() + return res + } + default: + repo, err := s.repoFn() + if err != nil { + res.Error = err + return res + } + authMeth, err := repo.LookupAuthMethod(ctx, id) + if err != nil { + res.Error = err + return res + } + if authMeth == nil { + res.Error = handlers.ForbiddenError() + return res + } + parentId = authMeth.GetScopeId() + opts = append(opts, auth.WithId(id)) + } + opts = append(opts, auth.WithScopeId(parentId)) + return auth.Verify(ctx, opts...) +} + func toProto(in *password.AuthMethod) (*pb.AuthMethod, error) { out := pb.AuthMethod{ Id: in.GetPublicId(), diff --git a/internal/servers/controller/handlers/authmethods/authmethod_service_test.go b/internal/servers/controller/handlers/authmethods/authmethod_service_test.go index 494f82eb0..5cd6154ff 100644 --- a/internal/servers/controller/handlers/authmethods/authmethod_service_test.go +++ b/internal/servers/controller/handlers/authmethods/authmethod_service_test.go @@ -33,6 +33,9 @@ func TestGet(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } repoFn := func() (*password.Repository, error) { return password.NewRepository(rw, rw, kms) } @@ -77,7 +80,7 @@ func TestGet(t *testing.T) { scopeId: o.GetPublicId(), req: &pbs.GetAuthMethodRequest{Id: password.AuthMethodPrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Wrong id prefix", @@ -98,7 +101,7 @@ func TestGet(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := authmethods.NewService(repoFn) + s, err := authmethods.NewService(repoFn, iamRepoFn) require.NoError(err, "Couldn't create new auth_method service.") got, gErr := s.GetAuthMethod(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), tc.req) @@ -113,6 +116,9 @@ func TestList(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } repoFn := func() (*password.Repository, error) { return password.NewRepository(rw, rw, kms) } @@ -184,17 +190,16 @@ func TestList(t *testing.T) { { name: "Unfound Auth Method", scopeId: password.AuthMethodPrefix + "_DoesntExis", - res: &pbs.ListAuthMethodsResponse{}, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := authmethods.NewService(repoFn) + s, err := authmethods.NewService(repoFn, iamRepoFn) require.NoError(err, "Couldn't create new auth_method service.") - got, gErr := s.ListAuthMethods(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), &pbs.ListAuthMethodsRequest{}) + got, gErr := s.ListAuthMethods(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), &pbs.ListAuthMethodsRequest{ScopeId: tc.scopeId}) assert.Equal(tc.errCode, status.Code(gErr), "ListAuthMethods() for scope %q got error %v, wanted %v", tc.scopeId, gErr, tc.errCode) assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListAuthMethods() for scope %q got response %q, wanted %q", tc.scopeId, got, tc.res) }) @@ -206,6 +211,9 @@ func TestDelete(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } repoFn := func() (*password.Repository, error) { return password.NewRepository(rw, rw, kms) } @@ -214,7 +222,7 @@ func TestDelete(t *testing.T) { o, _ := iam.TestScopes(t, iamRepo) am := password.TestAuthMethods(t, conn, o.GetPublicId(), 1)[0] - s, err := authmethods.NewService(repoFn) + s, err := authmethods.NewService(repoFn, iamRepoFn) require.NoError(t, err, "Error when getting new auth_method service.") cases := []struct { @@ -238,10 +246,7 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteAuthMethodRequest{ Id: password.AuthMethodPrefix + "_doesntexis", }, - res: &pbs.DeleteAuthMethodResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, { name: "Bad AuthMethod Id formatting", @@ -268,6 +273,9 @@ func TestDelete_twice(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } repoFn := func() (*password.Repository, error) { return password.NewRepository(rw, rw, kms) } @@ -276,7 +284,7 @@ func TestDelete_twice(t *testing.T) { o, _ := iam.TestScopes(t, iamRepo) am := password.TestAuthMethods(t, conn, o.GetPublicId(), 1)[0] - s, err := authmethods.NewService(repoFn) + s, err := authmethods.NewService(repoFn, iamRepoFn) require.NoError(err, "Error when getting new auth_method service.") req := &pbs.DeleteAuthMethodRequest{ @@ -286,8 +294,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteAuthMethod(auth.DisabledAuthTestContext(auth.WithScopeId(o.GetPublicId())), req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") } func TestCreate(t *testing.T) { @@ -295,6 +303,9 @@ func TestCreate(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } repoFn := func() (*password.Repository, error) { return password.NewRepository(rw, rw, kms) } @@ -436,7 +447,7 @@ func TestCreate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := authmethods.NewService(repoFn) + s, err := authmethods.NewService(repoFn, iamRepoFn) require.NoError(err, "Error when getting new auth_method service.") got, gErr := s.CreateAuthMethod(auth.DisabledAuthTestContext(auth.WithScopeId(tc.req.GetItem().GetScopeId())), tc.req) @@ -470,13 +481,16 @@ func TestUpdate(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } repoFn := func() (*password.Repository, error) { return password.NewRepository(rw, rw, kms) } iamRepo := iam.TestRepo(t, conn, wrapper) o, _ := iam.TestScopes(t, iamRepo) - tested, err := authmethods.NewService(repoFn) + tested, err := authmethods.NewService(repoFn, iamRepoFn) require.NoError(t, err, "Error when getting new auth_method service.") defaultScopeInfo := &scopepb.ScopeInfo{Id: o.GetPublicId(), Type: o.GetType()} @@ -666,8 +680,6 @@ func TestUpdate(t *testing.T) { }, errCode: codes.OK, }, - // TODO: Updating a non existant auth_method should result in a NotFound exception but currently results in - // the repoFn returning an internal error. { name: "Update a Non Existing AuthMethod", req: &pbs.UpdateAuthMethodRequest{ @@ -680,7 +692,7 @@ func TestUpdate(t *testing.T) { Description: &wrapperspb.StringValue{Value: "desc"}, }, }, - errCode: codes.Internal, + errCode: codes.PermissionDenied, }, { name: "Cant change Id", diff --git a/internal/servers/controller/handlers/authtokens/authtoken_service.go b/internal/servers/controller/handlers/authtokens/authtoken_service.go index 27f21f56d..3cf23b6a0 100644 --- a/internal/servers/controller/handlers/authtokens/authtoken_service.go +++ b/internal/servers/controller/handlers/authtokens/authtoken_service.go @@ -10,7 +10,10 @@ import ( "github.com/hashicorp/boundary/internal/db" pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/authtokens" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -18,28 +21,32 @@ import ( // Service handles request as described by the pbs.AuthTokenServiceServer interface. type Service struct { - repoFn func() (*authtoken.Repository, error) + repoFn common.AuthTokenRepoFactory + iamRepoFn common.IamRepoFactory } // NewService returns a user service which handles user related requests to boundary. -func NewService(repo func() (*authtoken.Repository, error)) (Service, error) { +func NewService(repo common.AuthTokenRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) { if repo == nil { + return Service{}, fmt.Errorf("nil auth token repository provided") + } + if iamRepoFn == nil { return Service{}, fmt.Errorf("nil iam repository provided") } - return Service{repoFn: repo}, nil + return Service{repoFn: repo, iamRepoFn: iamRepoFn}, nil } var _ pbs.AuthTokenServiceServer = Service{} // ListAuthTokens implements the interface pbs.AuthTokenServiceServer. func (s Service) ListAuthTokens(ctx context.Context, req *pbs.ListAuthTokensRequest) (*pbs.ListAuthTokensResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateListRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetScopeId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } ul, err := s.listFromRepo(ctx, req.GetScopeId()) if err != nil { return nil, err @@ -52,13 +59,13 @@ func (s Service) ListAuthTokens(ctx context.Context, req *pbs.ListAuthTokensRequ // GetAuthToken implements the interface pbs.AuthTokenServiceServer. func (s Service) GetAuthToken(ctx context.Context, req *pbs.GetAuthTokenRequest) (*pbs.GetAuthTokenResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateGetRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -69,13 +76,13 @@ func (s Service) GetAuthToken(ctx context.Context, req *pbs.GetAuthTokenRequest) // DeleteAuthToken implements the interface pbs.AuthTokenServiceServer. func (s Service) DeleteAuthToken(ctx context.Context, req *pbs.DeleteAuthTokenRequest) (*pbs.DeleteAuthTokenResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateDeleteRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } existed, err := s.deleteFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -133,6 +140,50 @@ func (s Service) listFromRepo(ctx context.Context, orgId string) ([]*pb.AuthToke return outUl, nil } +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} + + var parentId string + opts := []auth.Option{auth.WithType(resource.AuthToken), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + iamRepo, err := s.iamRepoFn() + if err != nil { + res.Error = err + return res + } + scp, err := iamRepo.LookupScope(ctx, parentId) + if err != nil { + res.Error = err + return res + } + if scp == nil { + res.Error = handlers.ForbiddenError() + return res + } + default: + repo, err := s.repoFn() + if err != nil { + res.Error = err + return res + } + authTok, err := repo.LookupAuthToken(ctx, id) + if err != nil { + res.Error = err + return res + } + if authTok == nil { + res.Error = handlers.ForbiddenError() + return res + } + parentId = authTok.GetScopeId() + opts = append(opts, auth.WithId(id)) + } + opts = append(opts, auth.WithScopeId(parentId)) + return auth.Verify(ctx, opts...) +} + func toProto(in *authtoken.AuthToken) *pb.AuthToken { out := pb.AuthToken{ Id: in.GetPublicId(), diff --git a/internal/servers/controller/handlers/authtokens/authtoken_service_test.go b/internal/servers/controller/handlers/authtokens/authtoken_service_test.go index 65fc1fcfd..c29f4ae73 100644 --- a/internal/servers/controller/handlers/authtokens/authtoken_service_test.go +++ b/internal/servers/controller/handlers/authtokens/authtoken_service_test.go @@ -27,11 +27,14 @@ func TestGet(t *testing.T) { rw := db.New(conn) wrap := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrap) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrap), nil + } repoFn := func() (*authtoken.Repository, error) { return authtoken.NewRepository(rw, rw, kms) } - s, err := authtokens.NewService(repoFn) + s, err := authtokens.NewService(repoFn, iamRepoFn) require.NoError(t, err, "Couldn't create new auth token service.") org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrap)) @@ -65,7 +68,7 @@ func TestGet(t *testing.T) { name: "Get a non existing auth token", req: &pbs.GetAuthTokenRequest{Id: authtoken.AuthTokenPrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Wrong id prefix", @@ -95,6 +98,9 @@ func TestList(t *testing.T) { rw := db.New(conn) wrap := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrap) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrap), nil + } repoFn := func() (*authtoken.Repository, error) { return authtoken.NewRepository(rw, rw, kms) } @@ -164,13 +170,12 @@ func TestList(t *testing.T) { { name: "Unfound Org", scope: scope.Org.Prefix() + "_DoesntExis", - res: &pbs.ListAuthTokensResponse{}, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - s, err := authtokens.NewService(repoFn) + s, err := authtokens.NewService(repoFn, iamRepoFn) require.NoError(t, err, "Couldn't create new user service.") got, gErr := s.ListAuthTokens(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scope)), &pbs.ListAuthTokensRequest{ScopeId: tc.scope}) @@ -185,17 +190,18 @@ func TestDelete(t *testing.T) { rw := db.New(conn) wrap := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrap) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrap), nil + } repoFn := func() (*authtoken.Repository, error) { return authtoken.NewRepository(rw, rw, kms) } iamRepo := iam.TestRepo(t, conn, wrap) - wrongOrg, _ := iam.TestScopes(t, iamRepo) org, _ := iam.TestScopes(t, iamRepo) at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) - atForWrongOrg := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) - s, err := authtokens.NewService(repoFn) + s, err := authtokens.NewService(repoFn, iamRepoFn) require.NoError(t, err, "Error when getting new user service.") cases := []struct { @@ -216,29 +222,13 @@ func TestDelete(t *testing.T) { }, errCode: codes.OK, }, - { - name: "Delete token from wrong scope", - scope: wrongOrg.GetPublicId(), - req: &pbs.DeleteAuthTokenRequest{ - Id: atForWrongOrg.GetPublicId(), - }, - // TODO(toddknight): This should return Existed:false. Figure out if this test is testing something valid - // and if so make it pass. - res: &pbs.DeleteAuthTokenResponse{ - Existed: true, - }, - errCode: codes.OK, - }, { name: "Delete bad token id", scope: org.GetPublicId(), req: &pbs.DeleteAuthTokenRequest{ Id: authtoken.AuthTokenPrefix + "_doesntexis", }, - res: &pbs.DeleteAuthTokenResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, { name: "Bad token id formatting", @@ -266,6 +256,9 @@ func TestDelete_twice(t *testing.T) { rw := db.New(conn) wrap := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrap) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrap), nil + } repoFn := func() (*authtoken.Repository, error) { return authtoken.NewRepository(rw, rw, kms) } @@ -274,7 +267,7 @@ func TestDelete_twice(t *testing.T) { org, _ := iam.TestScopes(t, iamRepo) at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) - s, err := authtokens.NewService(repoFn) + s, err := authtokens.NewService(repoFn, iamRepoFn) require.NoError(t, err, "Error when getting new user service") req := &pbs.DeleteAuthTokenRequest{ Id: at.GetPublicId(), @@ -283,6 +276,6 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteAuthToken(auth.DisabledAuthTestContext(auth.WithScopeId(at.GetScopeId())), req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") } diff --git a/internal/servers/controller/handlers/groups/group_service.go b/internal/servers/controller/handlers/groups/group_service.go index 43f4f47f4..d53434917 100644 --- a/internal/servers/controller/handlers/groups/group_service.go +++ b/internal/servers/controller/handlers/groups/group_service.go @@ -11,7 +11,10 @@ import ( pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/iam/store" + "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -31,11 +34,11 @@ func init() { // Service handles request as described by the pbs.GroupServiceServer interface. type Service struct { - repoFn func() (*iam.Repository, error) + repoFn common.IamRepoFactory } // NewService returns a group service which handles group related requests to boundary. -func NewService(repo func() (*iam.Repository, error)) (Service, error) { +func NewService(repo common.IamRepoFactory) (Service, error) { if repo == nil { return Service{}, fmt.Errorf("nil iam repository provided") } @@ -46,13 +49,13 @@ var _ pbs.GroupServiceServer = Service{} // ListGroups implements the interface pbs.GroupServiceServer. func (s Service) ListGroups(ctx context.Context, req *pbs.ListGroupsRequest) (*pbs.ListGroupsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateListRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetScopeId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } gl, err := s.listFromRepo(ctx, req.GetScopeId()) if err != nil { return nil, err @@ -65,13 +68,13 @@ func (s Service) ListGroups(ctx context.Context, req *pbs.ListGroupsRequest) (*p // GetGroups implements the interface pbs.GroupServiceServer. func (s Service) GetGroup(ctx context.Context, req *pbs.GetGroupRequest) (*pbs.GetGroupResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateGetRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -82,14 +85,14 @@ func (s Service) GetGroup(ctx context.Context, req *pbs.GetGroupRequest) (*pbs.G // CreateGroup implements the interface pbs.GroupServiceServer. func (s Service) CreateGroup(ctx context.Context, req *pbs.CreateGroupRequest) (*pbs.CreateGroupResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateCreateRequest(req); err != nil { return nil, err } - u, err := s.createInRepo(ctx, req.GetItem().GetScopeId(), req.GetItem()) + authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create) + if authResults.Error != nil { + return nil, authResults.Error + } + u, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem()) if err != nil { return nil, err } @@ -99,13 +102,13 @@ func (s Service) CreateGroup(ctx context.Context, req *pbs.CreateGroupRequest) ( // UpdateGroup implements the interface pbs.GroupServiceServer. func (s Service) UpdateGroup(ctx context.Context, req *pbs.UpdateGroupRequest) (*pbs.UpdateGroupResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateUpdateRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Update) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) if err != nil { return nil, err @@ -116,13 +119,13 @@ func (s Service) UpdateGroup(ctx context.Context, req *pbs.UpdateGroupRequest) ( // DeleteGroup implements the interface pbs.GroupServiceServer. func (s Service) DeleteGroup(ctx context.Context, req *pbs.DeleteGroupRequest) (*pbs.DeleteGroupResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateDeleteRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } existed, err := s.deleteFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -132,13 +135,13 @@ func (s Service) DeleteGroup(ctx context.Context, req *pbs.DeleteGroupRequest) ( // AddGroupMembers implements the interface pbs.GroupServiceServer. func (s Service) AddGroupMembers(ctx context.Context, req *pbs.AddGroupMembersRequest) (*pbs.AddGroupMembersResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateAddGroupMembersRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.AddMembers) + if authResults.Error != nil { + return nil, authResults.Error + } g, err := s.addMembersInRepo(ctx, req.GetId(), req.GetMemberIds(), req.GetVersion()) if err != nil { return nil, err @@ -148,13 +151,13 @@ func (s Service) AddGroupMembers(ctx context.Context, req *pbs.AddGroupMembersRe // SetGroupMembers implements the interface pbs.GroupServiceServer. func (s Service) SetGroupMembers(ctx context.Context, req *pbs.SetGroupMembersRequest) (*pbs.SetGroupMembersResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateSetGroupMembersRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.SetMembers) + if authResults.Error != nil { + return nil, authResults.Error + } g, err := s.setMembersInRepo(ctx, req.GetId(), req.GetMemberIds(), req.GetVersion()) if err != nil { return nil, err @@ -164,13 +167,13 @@ func (s Service) SetGroupMembers(ctx context.Context, req *pbs.SetGroupMembersRe // RemoveGroupMembers implements the interface pbs.GroupServiceServer. func (s Service) RemoveGroupMembers(ctx context.Context, req *pbs.RemoveGroupMembersRequest) (*pbs.RemoveGroupMembersResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateRemoveGroupMembersRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.RemoveMembers) + if authResults.Error != nil { + return nil, authResults.Error + } g, err := s.removeMembersInRepo(ctx, req.GetId(), req.GetMemberIds(), req.GetVersion()) if err != nil { return nil, err @@ -342,6 +345,45 @@ func (s Service) removeMembersInRepo(ctx context.Context, groupId string, userId return toProto(out, m), nil } +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} + repo, err := s.repoFn() + if err != nil { + res.Error = err + return res + } + + var parentId string + opts := []auth.Option{auth.WithType(resource.Group), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + scp, err := repo.LookupScope(ctx, parentId) + if err != nil { + res.Error = err + return res + } + if scp == nil { + res.Error = handlers.ForbiddenError() + return res + } + default: + grp, _, err := repo.LookupGroup(ctx, id) + if err != nil { + res.Error = err + return res + } + if grp == nil { + res.Error = handlers.ForbiddenError() + return res + } + parentId = grp.GetScopeId() + opts = append(opts, auth.WithId(id)) + } + opts = append(opts, auth.WithScopeId(parentId)) + return auth.Verify(ctx, opts...) +} + func toProto(in *iam.Group, members []*iam.GroupMember) *pb.Group { out := pb.Group{ Id: in.GetPublicId(), diff --git a/internal/servers/controller/handlers/groups/group_service_test.go b/internal/servers/controller/handlers/groups/group_service_test.go index 2c9cdd5cc..d3be1276a 100644 --- a/internal/servers/controller/handlers/groups/group_service_test.go +++ b/internal/servers/controller/handlers/groups/group_service_test.go @@ -142,7 +142,7 @@ func TestGet(t *testing.T) { name: "Get a non existant Group", req: &pbs.GetGroupRequest{Id: iam.GroupPrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Wrong id prefix", @@ -168,7 +168,7 @@ func TestGet(t *testing.T) { scopeId: pg.GetScopeId(), req: &pbs.GetGroupRequest{Id: iam.GroupPrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Project Scoped Wrong id prefix", @@ -308,21 +308,8 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteGroupRequest{ Id: iam.GroupPrefix + "_doesntexis", }, - res: &pbs.DeleteGroupResponse{ - Existed: false, - }, - errCode: codes.OK, - }, - { - name: "Delete bad org id", - scopeId: "o_doesntexist", - req: &pbs.DeleteGroupRequest{ - Id: og.GetPublicId(), - }, - res: &pbs.DeleteGroupResponse{ - Existed: false, - }, - errCode: codes.OK, + res: nil, + errCode: codes.PermissionDenied, }, { name: "Bad Group Id formatting", @@ -350,21 +337,8 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteGroupRequest{ Id: iam.GroupPrefix + "_doesntexis", }, - res: &pbs.DeleteGroupResponse{ - Existed: false, - }, - errCode: codes.OK, - }, - { - name: "Project Scoped Delete bad project id", - scopeId: "p_doesntexis", - req: &pbs.DeleteGroupRequest{ - Id: pg.GetPublicId(), - }, - res: &pbs.DeleteGroupResponse{ - Existed: false, - }, - errCode: codes.OK, + res: nil, + errCode: codes.PermissionDenied, }, } for _, tc := range cases { @@ -393,8 +367,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteGroup(ctx, req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") scopeId = pg.GetScopeId() projReq := &pbs.DeleteGroupRequest{ @@ -405,8 +379,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteGroup(ctx, projReq) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") } func TestCreate(t *testing.T) { @@ -840,8 +814,6 @@ func TestUpdate(t *testing.T) { }, errCode: codes.OK, }, - // TODO: Updating a non existant group should result in a NotFound exception but currently results in - // the repoFn returning an internal error. { name: "Update a Non Existing Group", req: &pbs.UpdateGroupRequest{ @@ -854,7 +826,7 @@ func TestUpdate(t *testing.T) { Description: &wrapperspb.StringValue{Value: "desc"}, }, }, - errCode: codes.Internal, + errCode: codes.PermissionDenied, }, { name: "Cant change Id", diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go index af2664f32..8e5e42ef5 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service.go @@ -12,6 +12,8 @@ import ( "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" @@ -30,27 +32,31 @@ func init() { type Service struct { staticRepoFn common.StaticRepoFactory + iamRepoFn common.IamRepoFactory } var _ pbs.HostCatalogServiceServer = Service{} // NewService returns a host catalog Service which handles host catalog related requests to boundary and uses the provided // repositories for storage and retrieval. -func NewService(repoFn common.StaticRepoFactory) (Service, error) { +func NewService(repoFn common.StaticRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) { if repoFn == nil { return Service{}, fmt.Errorf("nil static repository provided") } - return Service{staticRepoFn: repoFn}, nil + if iamRepoFn == nil { + return Service{}, fmt.Errorf("nil iam repository provided") + } + return Service{staticRepoFn: repoFn, iamRepoFn: iamRepoFn}, nil } func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogsRequest) (*pbs.ListHostCatalogsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateListRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetScopeId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } ul, err := s.listFromRepo(ctx, authResults.Scope.GetId()) if err != nil { return nil, err @@ -63,13 +69,13 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs // GetHostCatalog implements the interface pbs.HostCatalogServiceServer. func (s Service) GetHostCatalog(ctx context.Context, req *pbs.GetHostCatalogRequest) (*pbs.GetHostCatalogResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateGetRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } hc, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -80,13 +86,13 @@ func (s Service) GetHostCatalog(ctx context.Context, req *pbs.GetHostCatalogRequ // CreateHostCatalog implements the interface pbs.HostCatalogServiceServer. func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatalogRequest) (*pbs.CreateHostCatalogResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateCreateRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create) + if authResults.Error != nil { + return nil, authResults.Error + } hc, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem()) if err != nil { return nil, err @@ -100,13 +106,13 @@ func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatal // UpdateHostCatalog implements the interface pbs.HostCatalogServiceServer. func (s Service) UpdateHostCatalog(ctx context.Context, req *pbs.UpdateHostCatalogRequest) (*pbs.UpdateHostCatalogResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateUpdateRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Update) + if authResults.Error != nil { + return nil, authResults.Error + } hc, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) if err != nil { return nil, err @@ -117,13 +123,13 @@ func (s Service) UpdateHostCatalog(ctx context.Context, req *pbs.UpdateHostCatal // DeleteHostCatalog implements the interface pbs.HostCatalogServiceServer. func (s Service) DeleteHostCatalog(ctx context.Context, req *pbs.DeleteHostCatalogRequest) (*pbs.DeleteHostCatalogResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateDeleteRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } existed, err := s.deleteFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -232,6 +238,50 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) { return rows > 0, nil } +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} + + var parentId string + opts := []auth.Option{auth.WithType(resource.HostCatalog), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + iamRepo, err := s.iamRepoFn() + if err != nil { + res.Error = err + return res + } + scp, err := iamRepo.LookupScope(ctx, parentId) + if err != nil { + res.Error = err + return res + } + if scp == nil { + res.Error = handlers.ForbiddenError() + return res + } + default: + repo, err := s.staticRepoFn() + if err != nil { + res.Error = err + return res + } + cat, err := repo.LookupCatalog(ctx, id) + if err != nil { + res.Error = err + return res + } + if cat == nil { + res.Error = handlers.ForbiddenError() + return res + } + parentId = cat.GetScopeId() + opts = append(opts, auth.WithId(id)) + } + opts = append(opts, auth.WithScopeId(parentId)) + return auth.Verify(ctx, opts...) +} + func toProto(in *static.HostCatalog) *pb.HostCatalog { out := pb.HostCatalog{ Id: in.GetPublicId(), diff --git a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go index 7bf0f9454..0aed2594f 100644 --- a/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go +++ b/internal/servers/controller/handlers/host_catalogs/host_catalog_service_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_catalogs" "github.com/hashicorp/boundary/internal/types/scope" "google.golang.org/genproto/protobuf/field_mask" @@ -29,7 +30,7 @@ import ( "github.com/stretchr/testify/require" ) -func createDefaultHostCatalogAndRepo(t *testing.T) (*static.HostCatalog, *iam.Scope, func() (*static.Repository, error)) { +func createDefaultHostCatalogAndRepo(t *testing.T) (*static.HostCatalog, *iam.Scope, common.StaticRepoFactory, common.IamRepoFactory) { t.Helper() require := require.New(t) conn, _ := db.TestSetup(t, "postgres") @@ -42,6 +43,9 @@ func createDefaultHostCatalogAndRepo(t *testing.T) (*static.HostCatalog, *iam.Sc repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } hc, err := static.NewHostCatalog(pRes.GetPublicId(), static.WithName("default"), static.WithDescription("default")) require.NoError(err, "Couldn't get new catalog.") @@ -50,12 +54,12 @@ func createDefaultHostCatalogAndRepo(t *testing.T) (*static.HostCatalog, *iam.Sc hcRes, err := repo.CreateCatalog(context.Background(), hc) require.NoError(err, "Couldn't persist new catalog.") - return hcRes, pRes, repoFn + return hcRes, pRes, repoFn, iamRepoFn } func TestGet(t *testing.T) { t.Parallel() - hc, proj, repo := createDefaultHostCatalogAndRepo(t) + hc, proj, repo, iamRepoFn := createDefaultHostCatalogAndRepo(t) toMerge := &pbs.GetHostCatalogRequest{ Id: hc.GetPublicId(), } @@ -87,7 +91,7 @@ func TestGet(t *testing.T) { name: "Get a non existing Host Catalog", req: &pbs.GetHostCatalogRequest{Id: static.HostCatalogPrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Wrong id prefix", @@ -108,7 +112,7 @@ func TestGet(t *testing.T) { req := proto.Clone(toMerge).(*pbs.GetHostCatalogRequest) proto.Merge(req, tc.req) - s, err := host_catalogs.NewService(repo) + s, err := host_catalogs.NewService(repo, iamRepoFn) require.NoError(t, err, "Couldn't create a new host catalog service.") got, gErr := s.GetHostCatalog(auth.DisabledAuthTestContext(auth.WithScopeId(proj.GetPublicId())), req) @@ -127,6 +131,9 @@ func TestList(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) + iamRepoFn := func() (*iam.Repository, error) { + return iam.TestRepo(t, conn, wrapper), nil + } repoFn := func() (*static.Repository, error) { return static.NewRepository(rw, rw, kms) } @@ -186,21 +193,19 @@ func TestList(t *testing.T) { res: &pbs.ListHostCatalogsResponse{}, errCode: codes.OK, }, - // TODO: When an auth method doesn't exist, we should return a 404 instead of an empty list. { name: "Unfound Catalogs", scopeId: scope.Project.Prefix() + "_DoesntExis", - res: &pbs.ListHostCatalogsResponse{}, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - s, err := host_catalogs.NewService(repoFn) + s, err := host_catalogs.NewService(repoFn, iamRepoFn) require.NoError(err, "Couldn't create new auth_method service.") - got, gErr := s.ListHostCatalogs(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), &pbs.ListHostCatalogsRequest{}) + got, gErr := s.ListHostCatalogs(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), &pbs.ListHostCatalogsRequest{ScopeId: tc.scopeId}) assert.Equal(tc.errCode, status.Code(gErr), "ListHostCatalogs() for scope %q got error %v, wanted %v", tc.scopeId, gErr, tc.errCode) assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHostCatalogs() for scope %q got response %q, wanted %q", tc.scopeId, got, tc.res) }) @@ -210,9 +215,9 @@ func TestList(t *testing.T) { func TestDelete(t *testing.T) { t.Parallel() require := require.New(t) - hc, proj, repo := createDefaultHostCatalogAndRepo(t) + hc, proj, repo, iamRepoFn := createDefaultHostCatalogAndRepo(t) - s, err := host_catalogs.NewService(repo) + s, err := host_catalogs.NewService(repo, iamRepoFn) require.NoError(err, "Couldn't create a new host catalog service.") cases := []struct { @@ -239,32 +244,7 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteHostCatalogRequest{ Id: static.HostCatalogPrefix + "_doesntexis", }, - res: &pbs.DeleteHostCatalogResponse{ - Existed: false, - }, - errCode: codes.OK, - }, - { - name: "Delete bad org id in HostCatalog", - scopeId: "o_doesntexis", - req: &pbs.DeleteHostCatalogRequest{ - Id: hc.GetPublicId(), - }, - res: &pbs.DeleteHostCatalogResponse{ - Existed: false, - }, - errCode: codes.OK, - }, - { - name: "Delete bad project id in HostCatalog", - scopeId: "p_doesntexis", - req: &pbs.DeleteHostCatalogRequest{ - Id: hc.GetPublicId(), - }, - res: &pbs.DeleteHostCatalogResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, { name: "Bad HostCatalog Id formatting", @@ -289,9 +269,9 @@ func TestDelete(t *testing.T) { func TestDelete_twice(t *testing.T) { t.Parallel() assert := assert.New(t) - hc, proj, repo := createDefaultHostCatalogAndRepo(t) + hc, proj, repo, iamRepoFn := createDefaultHostCatalogAndRepo(t) - s, err := host_catalogs.NewService(repo) + s, err := host_catalogs.NewService(repo, iamRepoFn) require.NoError(t, err, "Couldn't create a new host catalog service.") req := &pbs.DeleteHostCatalogRequest{ Id: hc.GetPublicId(), @@ -301,17 +281,15 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteHostCatalog(ctx, req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") } func TestCreate(t *testing.T) { t.Parallel() - require := require.New(t) - assert := assert.New(t) - defaultHc, proj, repo := createDefaultHostCatalogAndRepo(t) + defaultHc, proj, repo, iamRepoFn := createDefaultHostCatalogAndRepo(t) defaultHcCreated, err := ptypes.Timestamp(defaultHc.GetCreateTime().GetTimestamp()) - require.NoError(err, "Error converting proto to timestamp.") + require.NoError(t, err, "Error converting proto to timestamp.") toMerge := &pbs.CreateHostCatalogRequest{} cases := []struct { @@ -323,6 +301,7 @@ func TestCreate(t *testing.T) { { name: "Create a valid HostCatalog", req: &pbs.CreateHostCatalogRequest{Item: &pb.HostCatalog{ + ScopeId: proj.GetPublicId(), Name: &wrappers.StringValue{Value: "name"}, Description: &wrappers.StringValue{Value: "desc"}, Type: "static", @@ -383,10 +362,11 @@ func TestCreate(t *testing.T) { } 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.CreateHostCatalogRequest) proto.Merge(req, tc.req) - s, err := host_catalogs.NewService(repo) + s, err := host_catalogs.NewService(repo, iamRepoFn) require.NoError(err, "Failed to create a new host catalog service.") got, gErr := s.CreateHostCatalog(auth.DisabledAuthTestContext(auth.WithScopeId(proj.GetPublicId())), req) @@ -417,25 +397,23 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { t.Parallel() - require := require.New(t) - assert := assert.New(t) - hc, proj, repoFn := createDefaultHostCatalogAndRepo(t) - tested, err := host_catalogs.NewService(repoFn) - require.NoError(err, "Failed to create a new host catalog service.") + hc, proj, repoFn, iamRepoFn := createDefaultHostCatalogAndRepo(t) + tested, err := host_catalogs.NewService(repoFn, iamRepoFn) + require.NoError(t, err, "Failed to create a new host catalog service.") var version uint32 = 1 resetHostCatalog := func() { version++ repo, err := repoFn() - require.NoError(err, "Couldn't create new static repo.") + require.NoError(t, err, "Couldn't create new static repo.") hc, _, err = repo.UpdateCatalog(context.Background(), hc, version, []string{"Name", "Description"}) - require.NoError(err, "Failed to reset host catalog.") + require.NoError(t, err, "Failed to reset host catalog.") version++ } hcCreated, err := ptypes.Timestamp(hc.GetCreateTime().GetTimestamp()) - require.NoError(err, "Failed to convert proto to timestamp") + require.NoError(t, err, "Failed to convert proto to timestamp") toMerge := &pbs.UpdateHostCatalogRequest{ Id: hc.GetPublicId(), } @@ -618,8 +596,6 @@ func TestUpdate(t *testing.T) { }, errCode: codes.OK, }, - // TODO: Updating a non existing catalog should result in a NotFound exception but currently results in - // the repoFn returning an internal error. { name: "Update a Non Existing HostCatalog", req: &pbs.UpdateHostCatalogRequest{ @@ -633,7 +609,7 @@ func TestUpdate(t *testing.T) { Description: &wrappers.StringValue{Value: "desc"}, }, }, - errCode: codes.Internal, + errCode: codes.PermissionDenied, }, { name: "Cant change Id", @@ -693,6 +669,7 @@ func TestUpdate(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) tc.req.Item.Version = version req := proto.Clone(toMerge).(*pbs.UpdateHostCatalogRequest) diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index d6f4759c0..0538f0c68 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" @@ -44,51 +45,14 @@ func NewService(repoFn common.StaticRepoFactory) (Service, error) { return Service{staticRepoFn: repoFn}, nil } -func (s Service) getHostStructs(ctx context.Context, id string, isHostSetId bool) (*static.HostCatalog, *static.HostSet, error) { - repo, err := s.staticRepoFn() - if err != nil { - return nil, nil, err - } - hostCatalogId := id - var hostSet *static.HostSet - if isHostSetId { - hostSet, _, err = repo.LookupSet(ctx, id) - if err != nil { - return nil, nil, err - } - if hostSet == nil { - return nil, nil, handlers.ForbiddenError() - } - hostCatalogId = hostSet.GetCatalogId() - } - hostCatalog, err := repo.LookupCatalog(ctx, hostCatalogId) - if err != nil { - return nil, nil, err - } - if hostCatalog == nil { - return nil, nil, handlers.ForbiddenError() - } - return hostCatalog, hostSet, nil -} - func (s Service) ListHostSets(ctx context.Context, req *pbs.ListHostSetsRequest) (*pbs.ListHostSetsResponse, error) { if err := validateListRequest(req); err != nil { return nil, err } - - hostCatalog, _, err := s.getHostStructs(ctx, req.GetHostCatalogId(), false) - if err != nil { - return nil, err - } - authResults := auth.Verify(ctx, - auth.WithScopeId(hostCatalog.GetScopeId()), - auth.WithPin(hostCatalog.GetPublicId()), - auth.WithAction(action.List), - ) + _, authResults := s.parentAndAuthResult(ctx, req.GetHostCatalogId(), action.List) if authResults.Error != nil { return nil, authResults.Error } - hl, err := s.listFromRepo(ctx, req.GetHostCatalogId()) if err != nil { return nil, err @@ -104,21 +68,10 @@ func (s Service) GetHostSet(ctx context.Context, req *pbs.GetHostSetRequest) (*p if err := validateGetRequest(req); err != nil { return nil, err } - - hostCatalog, hostSet, err := s.getHostStructs(ctx, req.GetId(), true) - if err != nil { - return nil, err - } - authResults := auth.Verify(ctx, - auth.WithScopeId(hostCatalog.GetScopeId()), - auth.WithId(hostSet.GetPublicId()), - auth.WithPin(hostCatalog.GetPublicId()), - auth.WithAction(action.Read), - ) + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read) if authResults.Error != nil { return nil, authResults.Error } - hc, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -132,21 +85,11 @@ func (s Service) CreateHostSet(ctx context.Context, req *pbs.CreateHostSetReques if err := validateCreateRequest(req); err != nil { return nil, err } - - hostCatalog, _, err := s.getHostStructs(ctx, req.GetItem().GetHostCatalogId(), false) - if err != nil { - return nil, err - } - authResults := auth.Verify(ctx, - auth.WithScopeId(hostCatalog.GetScopeId()), - auth.WithPin(hostCatalog.GetPublicId()), - auth.WithAction(action.Create), - ) + cat, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetHostCatalogId(), action.Create) if authResults.Error != nil { return nil, authResults.Error } - - h, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem().GetHostCatalogId(), req.GetItem()) + h, err := s.createInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetItem()) if err != nil { return nil, err } @@ -162,22 +105,11 @@ func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetReques if err := validateUpdateRequest(req); err != nil { return nil, err } - - hostCatalog, hostSet, err := s.getHostStructs(ctx, req.GetId(), true) - if err != nil { - return nil, err - } - authResults := auth.Verify(ctx, - auth.WithScopeId(hostCatalog.GetScopeId()), - auth.WithId(hostSet.GetPublicId()), - auth.WithPin(hostCatalog.GetPublicId()), - auth.WithAction(action.Update), - ) + cat, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update) if authResults.Error != nil { return nil, authResults.Error } - - hc, err := s.updateInRepo(ctx, authResults.Scope.GetId(), hostCatalog.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + hc, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) if err != nil { return nil, err } @@ -190,20 +122,10 @@ func (s Service) DeleteHostSet(ctx context.Context, req *pbs.DeleteHostSetReques if err := validateDeleteRequest(req); err != nil { return nil, err } - hostCatalog, hostSet, err := s.getHostStructs(ctx, req.GetId(), true) - if err != nil { - return nil, err - } - authResults := auth.Verify(ctx, - auth.WithScopeId(hostCatalog.GetScopeId()), - auth.WithId(hostSet.GetPublicId()), - auth.WithPin(hostCatalog.GetPublicId()), - auth.WithAction(action.Delete), - ) + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete) if authResults.Error != nil { return nil, authResults.Error } - existed, err := s.deleteFromRepo(ctx, authResults.Scope.GetId(), req.GetId()) if err != nil { return nil, err @@ -216,20 +138,10 @@ func (s Service) AddHostSetHosts(ctx context.Context, req *pbs.AddHostSetHostsRe if err := validateAddRequest(req); err != nil { return nil, err } - hostCatalog, hostSet, err := s.getHostStructs(ctx, req.GetId(), true) - if err != nil { - return nil, err - } - authResults := auth.Verify(ctx, - auth.WithScopeId(hostCatalog.GetScopeId()), - auth.WithId(hostSet.GetPublicId()), - auth.WithPin(hostCatalog.GetPublicId()), - auth.WithAction(action.AddHosts), - ) + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.AddHostSets) if authResults.Error != nil { return nil, authResults.Error } - g, err := s.addInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetHostIds(), req.GetVersion()) if err != nil { return nil, err @@ -242,20 +154,10 @@ func (s Service) SetHostSetHosts(ctx context.Context, req *pbs.SetHostSetHostsRe if err := validateSetRequest(req); err != nil { return nil, err } - hostCatalog, hostSet, err := s.getHostStructs(ctx, req.GetId(), true) - if err != nil { - return nil, err - } - authResults := auth.Verify(ctx, - auth.WithScopeId(hostCatalog.GetScopeId()), - auth.WithId(hostSet.GetPublicId()), - auth.WithPin(hostCatalog.GetPublicId()), - auth.WithAction(action.SetHosts), - ) + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.SetHostSets) if authResults.Error != nil { return nil, authResults.Error } - g, err := s.setInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetHostIds(), req.GetVersion()) if err != nil { return nil, err @@ -268,20 +170,10 @@ func (s Service) RemoveHostSetHosts(ctx context.Context, req *pbs.RemoveHostSetH if err := validateRemoveRequest(req); err != nil { return nil, err } - hostCatalog, hostSet, err := s.getHostStructs(ctx, req.GetId(), true) - if err != nil { - return nil, err - } - authResults := auth.Verify(ctx, - auth.WithScopeId(hostCatalog.GetScopeId()), - auth.WithId(hostSet.GetPublicId()), - auth.WithPin(hostCatalog.GetPublicId()), - auth.WithAction(action.RemoveHosts), - ) + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.RemoveHostSets) if authResults.Error != nil { return nil, authResults.Error } - g, err := s.removeInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetHostIds(), req.GetVersion()) if err != nil { return nil, err @@ -447,6 +339,46 @@ func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostId return toProto(out, m), nil } +func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (*static.HostCatalog, auth.VerifyResults) { + res := auth.VerifyResults{} + repo, err := s.staticRepoFn() + if err != nil { + res.Error = err + return nil, res + } + + var parentId string + opts := []auth.Option{auth.WithType(resource.HostSet), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + default: + set, _, err := repo.LookupSet(ctx, id) + if err != nil { + res.Error = err + return nil, res + } + if set == nil { + res.Error = handlers.ForbiddenError() + return nil, res + } + parentId = set.GetCatalogId() + opts = append(opts, auth.WithId(id)) + } + + cat, err := repo.LookupCatalog(ctx, parentId) + if err != nil { + res.Error = err + return nil, res + } + if cat == nil { + res.Error = handlers.ForbiddenError() + return nil, res + } + opts = append(opts, auth.WithScopeId(cat.GetScopeId()), auth.WithPin(parentId)) + return cat, auth.Verify(ctx, opts...) +} + func toProto(in *static.HostSet, hs []*static.Host) *pb.HostSet { out := pb.HostSet{ Id: in.GetPublicId(), diff --git a/internal/servers/controller/handlers/hosts/host_service.go b/internal/servers/controller/handlers/hosts/host_service.go index e3c470363..d6151c730 100644 --- a/internal/servers/controller/handlers/hosts/host_service.go +++ b/internal/servers/controller/handlers/hosts/host_service.go @@ -3,7 +3,6 @@ package hosts import ( "context" "fmt" - "regexp" "github.com/hashicorp/boundary/internal/auth" pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/hosts" @@ -13,6 +12,8 @@ import ( "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" @@ -20,7 +21,6 @@ import ( var ( maskManager handlers.MaskManager - reInvalidID = regexp.MustCompile("[^A-Za-z0-9]") ) func init() { @@ -46,13 +46,13 @@ func NewService(repoFn common.StaticRepoFactory) (Service, error) { } func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs.ListHostsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateListRequest(req); err != nil { return nil, err } + _, authResults := s.parentAndAuthResult(ctx, req.GetHostCatalogId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } hl, err := s.listFromRepo(ctx, req.GetHostCatalogId()) if err != nil { return nil, err @@ -65,17 +65,13 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs // GetHost implements the interface pbs.HostServiceServer. func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.GetHostResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } - ct := host.SubtypeFromId(req.GetId()) - if ct == host.UnknownSubtype { - return nil, handlers.InvalidArgumentErrorf("Invalid argument provided.", map[string]string{"id": "Improperly formatted identifier used."}) - } if err := validateGetRequest(req); err != nil { return nil, err } + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } hc, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -86,13 +82,13 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get // CreateHost implements the interface pbs.HostServiceServer. func (s Service) CreateHost(ctx context.Context, req *pbs.CreateHostRequest) (*pbs.CreateHostResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateCreateRequest(req); err != nil { return nil, err } + _, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetHostCatalogId(), action.Create) + if authResults.Error != nil { + return nil, authResults.Error + } h, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem().GetHostCatalogId(), req.GetItem()) if err != nil { return nil, err @@ -106,18 +102,14 @@ func (s Service) CreateHost(ctx context.Context, req *pbs.CreateHostRequest) (*p // UpdateHost implements the interface pbs.HostServiceServer. func (s Service) UpdateHost(ctx context.Context, req *pbs.UpdateHostRequest) (*pbs.UpdateHostResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } - ct := host.SubtypeFromId(req.GetId()) - if ct == host.UnknownSubtype { - return nil, handlers.InvalidArgumentErrorf("Invalid argument provided.", map[string]string{"id": "Improperly formatted identifier used."}) - } if err := validateUpdateRequest(req); err != nil { return nil, err } - hc, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + cat, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update) + if authResults.Error != nil { + return nil, authResults.Error + } + hc, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) if err != nil { return nil, err } @@ -127,13 +119,13 @@ func (s Service) UpdateHost(ctx context.Context, req *pbs.UpdateHostRequest) (*p // DeleteHost implements the interface pbs.HostServiceServer. func (s Service) DeleteHost(ctx context.Context, req *pbs.DeleteHostRequest) (*pbs.DeleteHostResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateDeleteRequest(req); err != nil { return nil, err } + _, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } existed, err := s.deleteFromRepo(ctx, authResults.Scope.GetId(), req.GetId()) if err != nil { return nil, err @@ -190,7 +182,7 @@ func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, it return toProto(out, nil) } -func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Host) (*pb.Host, error) { +func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string, mask []string, item *pb.Host) (*pb.Host, error) { ha := &pb.StaticHostAttributes{} if err := handlers.StructToProto(item.GetAttributes(), ha); err != nil { return nil, status.Errorf(codes.Internal, "Failed converting attributes to subtype proto: %s", err) @@ -205,7 +197,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st if addr := ha.GetAddress(); addr != nil { opts = append(opts, static.WithAddress(addr.GetValue())) } - h, err := static.NewHost("ignored", opts...) + h, err := static.NewHost(catalogId, opts...) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to build host for update: %v.", err) } @@ -260,6 +252,46 @@ func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*pb.Host return outHl, nil } +func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (*static.HostCatalog, auth.VerifyResults) { + res := auth.VerifyResults{} + repo, err := s.staticRepoFn() + if err != nil { + res.Error = err + return nil, res + } + + var parentId string + opts := []auth.Option{auth.WithType(resource.Host), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + default: + h, err := repo.LookupHost(ctx, id) + if err != nil { + res.Error = err + return nil, res + } + if h == nil { + res.Error = handlers.ForbiddenError() + return nil, res + } + parentId = h.GetCatalogId() + opts = append(opts, auth.WithId(id)) + } + + cat, err := repo.LookupCatalog(ctx, parentId) + if err != nil { + res.Error = err + return nil, res + } + if cat == nil { + res.Error = handlers.ForbiddenError() + return nil, res + } + opts = append(opts, auth.WithScopeId(cat.GetScopeId()), auth.WithPin(parentId)) + return cat, auth.Verify(ctx, opts...) +} + func toProto(in *static.Host, members []*static.HostSet) (*pb.Host, error) { out := pb.Host{ Id: in.GetPublicId(), @@ -294,7 +326,14 @@ func toProto(in *static.Host, members []*static.HostSet) (*pb.Host, error) { // * The type asserted by the ID and/or field is known // * If relevant, the type derived from the id prefix matches what is claimed by the type field func validateGetRequest(req *pbs.GetHostRequest) error { - return handlers.ValidateGetRequest(static.HostPrefix, req, handlers.NoopValidatorFn) + return handlers.ValidateGetRequest(static.HostPrefix, req, func() map[string]string { + badFields := map[string]string{} + ct := host.SubtypeFromId(req.GetId()) + if ct == host.UnknownSubtype { + badFields["id"] = "Improperly formatted identifier used." + } + return badFields + }) } func validateCreateRequest(req *pbs.CreateHostRequest) error { @@ -323,6 +362,10 @@ func validateCreateRequest(req *pbs.CreateHostRequest) error { func validateUpdateRequest(req *pbs.UpdateHostRequest) error { return handlers.ValidateUpdateRequest(static.HostPrefix, req, req.GetItem(), func() map[string]string { badFields := map[string]string{} + ct := host.SubtypeFromId(req.GetId()) + if ct == host.UnknownSubtype { + badFields["id"] = "Improperly formatted identifier used." + } if req.GetItem().GetType() != "" { badFields["type"] = "This is a read only field and cannot be specified in an update request." } diff --git a/internal/servers/controller/handlers/hosts/host_service_test.go b/internal/servers/controller/handlers/hosts/host_service_test.go index 7051f0976..dd34a8f09 100644 --- a/internal/servers/controller/handlers/hosts/host_service_test.go +++ b/internal/servers/controller/handlers/hosts/host_service_test.go @@ -73,7 +73,7 @@ func TestGet(t *testing.T) { name: "Get a non existing Host Set", req: &pbs.GetHostRequest{Id: static.HostPrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Wrong id prefix", @@ -208,10 +208,7 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteHostRequest{ Id: static.HostPrefix + "_doesntexis", }, - res: &pbs.DeleteHostResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, { name: "Bad Host Id formatting", @@ -259,8 +256,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteHost(ctx, req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") } func TestCreate(t *testing.T) { @@ -666,7 +663,7 @@ func TestUpdate(t *testing.T) { Description: &wrappers.StringValue{Value: "desc"}, }, }, - errCode: codes.Internal, + errCode: codes.PermissionDenied, }, { name: "Cant change Id", diff --git a/internal/servers/controller/handlers/roles/role_service.go b/internal/servers/controller/handlers/roles/role_service.go index b1f858076..9e1fa5713 100644 --- a/internal/servers/controller/handlers/roles/role_service.go +++ b/internal/servers/controller/handlers/roles/role_service.go @@ -8,12 +8,14 @@ import ( "github.com/hashicorp/boundary/internal/auth" "github.com/hashicorp/boundary/internal/db" pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/roles" - "github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/iam/store" "github.com/hashicorp/boundary/internal/perms" + "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -33,11 +35,11 @@ func init() { // Service handles request as described by the pbs.RoleServiceServer interface. type Service struct { - repoFn func() (*iam.Repository, error) + repoFn common.IamRepoFactory } // NewService returns a role service which handles role related requests to boundary. -func NewService(repo func() (*iam.Repository, error)) (Service, error) { +func NewService(repo common.IamRepoFactory) (Service, error) { if repo == nil { return Service{}, fmt.Errorf("nil iam repository provided") } @@ -48,13 +50,13 @@ var _ pbs.RoleServiceServer = Service{} // ListRoles implements the interface pbs.RoleServiceServer. func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs.ListRolesResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateListRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetScopeId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } gl, err := s.listFromRepo(ctx, req.GetScopeId()) if err != nil { return nil, err @@ -67,13 +69,13 @@ func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs // GetRoles implements the interface pbs.RoleServiceServer. func (s Service) GetRole(ctx context.Context, req *pbs.GetRoleRequest) (*pbs.GetRoleResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateGetRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -84,14 +86,14 @@ func (s Service) GetRole(ctx context.Context, req *pbs.GetRoleRequest) (*pbs.Get // CreateRole implements the interface pbs.RoleServiceServer. func (s Service) CreateRole(ctx context.Context, req *pbs.CreateRoleRequest) (*pbs.CreateRoleResponse, error) { - authResults := auth.Verify(ctx) + if err := validateCreateRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create) if authResults.Error != nil { return nil, authResults.Error } - if err := validateCreateRequest(req, authResults.Scope); err != nil { - return nil, err - } - r, err := s.createInRepo(ctx, req.GetItem().GetScopeId(), req.GetItem()) + r, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem()) if err != nil { return nil, err } @@ -101,13 +103,13 @@ func (s Service) CreateRole(ctx context.Context, req *pbs.CreateRoleRequest) (*p // UpdateRole implements the interface pbs.RoleServiceServer. func (s Service) UpdateRole(ctx context.Context, req *pbs.UpdateRoleRequest) (*pbs.UpdateRoleResponse, error) { - authResults := auth.Verify(ctx) + if err := validateUpdateRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.Update) if authResults.Error != nil { return nil, authResults.Error } - if err := validateUpdateRequest(req, authResults.Scope); err != nil { - return nil, err - } u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) if err != nil { return nil, err @@ -118,13 +120,13 @@ func (s Service) UpdateRole(ctx context.Context, req *pbs.UpdateRoleRequest) (*p // DeleteRole implements the interface pbs.RoleServiceServer. func (s Service) DeleteRole(ctx context.Context, req *pbs.DeleteRoleRequest) (*pbs.DeleteRoleResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateDeleteRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } existed, err := s.deleteFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -134,14 +136,14 @@ func (s Service) DeleteRole(ctx context.Context, req *pbs.DeleteRoleRequest) (*p // AddRolePrincipals implements the interface pbs.RoleServiceServer. func (s Service) AddRolePrincipals(ctx context.Context, req *pbs.AddRolePrincipalsRequest) (*pbs.AddRolePrincipalsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateAddRolePrincipalsRequest(req); err != nil { return nil, err } - r, err := s.addPrinciplesInRepo(ctx, req.GetRoleId(), req.GetPrincipalIds(), req.GetVersion()) + authResults := s.authResult(ctx, req.GetId(), action.AddPrincipals) + if authResults.Error != nil { + return nil, authResults.Error + } + r, err := s.addPrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion()) if err != nil { return nil, err } @@ -151,14 +153,14 @@ func (s Service) AddRolePrincipals(ctx context.Context, req *pbs.AddRolePrincipa // SetRolePrincipals implements the interface pbs.RoleServiceServer. func (s Service) SetRolePrincipals(ctx context.Context, req *pbs.SetRolePrincipalsRequest) (*pbs.SetRolePrincipalsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateSetRolePrincipalsRequest(req); err != nil { return nil, err } - r, err := s.setPrinciplesInRepo(ctx, req.GetRoleId(), req.GetPrincipalIds(), req.GetVersion()) + authResults := s.authResult(ctx, req.GetId(), action.SetPrincipals) + if authResults.Error != nil { + return nil, authResults.Error + } + r, err := s.setPrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion()) if err != nil { return nil, err } @@ -168,14 +170,14 @@ func (s Service) SetRolePrincipals(ctx context.Context, req *pbs.SetRolePrincipa // RemoveRolePrincipals implements the interface pbs.RoleServiceServer. func (s Service) RemoveRolePrincipals(ctx context.Context, req *pbs.RemoveRolePrincipalsRequest) (*pbs.RemoveRolePrincipalsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateRemoveRolePrincipalsRequest(req); err != nil { return nil, err } - r, err := s.removePrinciplesInRepo(ctx, req.GetRoleId(), req.GetPrincipalIds(), req.GetVersion()) + authResults := s.authResult(ctx, req.GetId(), action.RemovePrincipals) + if authResults.Error != nil { + return nil, authResults.Error + } + r, err := s.removePrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion()) if err != nil { return nil, err } @@ -185,14 +187,14 @@ func (s Service) RemoveRolePrincipals(ctx context.Context, req *pbs.RemoveRolePr // AddRoleGrants implements the interface pbs.RoleServiceServer. func (s Service) AddRoleGrants(ctx context.Context, req *pbs.AddRoleGrantsRequest) (*pbs.AddRoleGrantsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateAddRoleGrantsRequest(req); err != nil { return nil, err } - r, err := s.addGrantsInRepo(ctx, req.GetRoleId(), req.GetGrantStrings(), req.GetVersion()) + authResults := s.authResult(ctx, req.GetId(), action.AddGrants) + if authResults.Error != nil { + return nil, authResults.Error + } + r, err := s.addGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion()) if err != nil { return nil, err } @@ -202,14 +204,14 @@ func (s Service) AddRoleGrants(ctx context.Context, req *pbs.AddRoleGrantsReques // SetRoleGrants implements the interface pbs.RoleServiceServer. func (s Service) SetRoleGrants(ctx context.Context, req *pbs.SetRoleGrantsRequest) (*pbs.SetRoleGrantsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateSetRoleGrantsRequest(req); err != nil { return nil, err } - r, err := s.setGrantsInRepo(ctx, req.GetRoleId(), req.GetGrantStrings(), req.GetVersion()) + authResults := s.authResult(ctx, req.GetId(), action.SetGrants) + if authResults.Error != nil { + return nil, authResults.Error + } + r, err := s.setGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion()) if err != nil { return nil, err } @@ -219,14 +221,14 @@ func (s Service) SetRoleGrants(ctx context.Context, req *pbs.SetRoleGrantsReques // RemoveRoleGrants implements the interface pbs.RoleServiceServer. func (s Service) RemoveRoleGrants(ctx context.Context, req *pbs.RemoveRoleGrantsRequest) (*pbs.RemoveRoleGrantsResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateRemoveRoleGrantsRequest(req); err != nil { return nil, err } - r, err := s.removeGrantsInRepo(ctx, req.GetRoleId(), req.GetGrantStrings(), req.GetVersion()) + authResults := s.authResult(ctx, req.GetId(), action.RemoveGrants) + if authResults.Error != nil { + return nil, authResults.Error + } + r, err := s.removeGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion()) if err != nil { return nil, err } @@ -468,6 +470,45 @@ func (s Service) removeGrantsInRepo(ctx context.Context, roleId string, grants [ return toProto(out, pr, roleGrants), nil } +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} + repo, err := s.repoFn() + if err != nil { + res.Error = err + return res + } + + var parentId string + opts := []auth.Option{auth.WithType(resource.Role), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + scp, err := repo.LookupScope(ctx, parentId) + if err != nil { + res.Error = err + return res + } + if scp == nil { + res.Error = handlers.ForbiddenError() + return res + } + default: + r, _, _, err := repo.LookupRole(ctx, id) + if err != nil { + res.Error = err + return res + } + if r == nil { + res.Error = handlers.ForbiddenError() + return res + } + parentId = r.GetScopeId() + opts = append(opts, auth.WithId(id)) + } + opts = append(opts, auth.WithScopeId(parentId)) + return auth.Verify(ctx, opts...) +} + func toProto(in *iam.Role, principals []iam.PrincipalRole, grants []*iam.RoleGrant) *pb.Role { out := pb.Role{ Id: in.GetPublicId(), @@ -530,7 +571,7 @@ func validateGetRequest(req *pbs.GetRoleRequest) error { return handlers.ValidateGetRequest(iam.RolePrefix, req, handlers.NoopValidatorFn) } -func validateCreateRequest(req *pbs.CreateRoleRequest, s *scopes.ScopeInfo) error { +func validateCreateRequest(req *pbs.CreateRoleRequest) error { return handlers.ValidateCreateRequest(req.GetItem(), func() map[string]string { badFields := map[string]string{} item := req.GetItem() @@ -539,8 +580,8 @@ func validateCreateRequest(req *pbs.CreateRoleRequest, s *scopes.ScopeInfo) erro scope.Global.String() != item.GetScopeId() { badFields["scope_id"] = "This field is missing or improperly formatted." } - if item.GetGrantScopeId() != nil && s.GetType() == scope.Project.String() { - if item.GetGrantScopeId().Value != s.GetId() { + if item.GetGrantScopeId() != nil && handlers.ValidId(scope.Project.Prefix(), item.GetScopeId()) { + if item.GetGrantScopeId().GetValue() != item.GetScopeId() { badFields["grant_scope_id"] = "Must be empty or set to the project_id when the scope type is project." } } @@ -554,7 +595,7 @@ func validateCreateRequest(req *pbs.CreateRoleRequest, s *scopes.ScopeInfo) erro }) } -func validateUpdateRequest(req *pbs.UpdateRoleRequest, s *scopes.ScopeInfo) error { +func validateUpdateRequest(req *pbs.UpdateRoleRequest) error { return handlers.ValidateUpdateRequest(iam.RolePrefix, req, req.GetItem(), func() map[string]string { badFields := map[string]string{} if req.GetItem().GetPrincipalIds() != nil { @@ -569,8 +610,8 @@ func validateUpdateRequest(req *pbs.UpdateRoleRequest, s *scopes.ScopeInfo) erro if req.GetItem().GetGrantStrings() != nil { badFields["grant_strings"] = "This is a read only field and cannot be specified in an update request." } - if req.GetItem().GetGrantScopeId() != nil && s.GetType() == scope.Project.String() { - if req.GetItem().GetGrantScopeId().Value != s.GetId() { + if req.GetItem().GetGrantScopeId() != nil && handlers.ValidId(scope.Project.Prefix(), req.GetItem().GetScopeId()) { + if req.GetItem().GetGrantScopeId().GetValue() != req.GetItem().GetScopeId() { badFields["grant_scope_id"] = "Must be empty or set to the project_id when the scope type is project." } } @@ -595,7 +636,7 @@ func validateListRequest(req *pbs.ListRolesRequest) error { func validateAddRolePrincipalsRequest(req *pbs.AddRolePrincipalsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(iam.RolePrefix, req.GetRoleId()) { + if !handlers.ValidId(iam.RolePrefix, req.GetId()) { badFields["id"] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -617,7 +658,7 @@ func validateAddRolePrincipalsRequest(req *pbs.AddRolePrincipalsRequest) error { func validateSetRolePrincipalsRequest(req *pbs.SetRolePrincipalsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(iam.RolePrefix, req.GetRoleId()) { + if !handlers.ValidId(iam.RolePrefix, req.GetId()) { badFields["id"] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -636,7 +677,7 @@ func validateSetRolePrincipalsRequest(req *pbs.SetRolePrincipalsRequest) error { func validateRemoveRolePrincipalsRequest(req *pbs.RemoveRolePrincipalsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(iam.RolePrefix, req.GetRoleId()) { + if !handlers.ValidId(iam.RolePrefix, req.GetId()) { badFields["id"] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -653,7 +694,7 @@ func validateRemoveRolePrincipalsRequest(req *pbs.RemoveRolePrincipalsRequest) e func validateAddRoleGrantsRequest(req *pbs.AddRoleGrantsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(iam.RolePrefix, req.GetRoleId()) { + if !handlers.ValidId(iam.RolePrefix, req.GetId()) { badFields["id"] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -670,7 +711,7 @@ func validateAddRoleGrantsRequest(req *pbs.AddRoleGrantsRequest) error { func validateSetRoleGrantsRequest(req *pbs.SetRoleGrantsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(iam.RolePrefix, req.GetRoleId()) { + if !handlers.ValidId(iam.RolePrefix, req.GetId()) { badFields["id"] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -684,7 +725,7 @@ func validateSetRoleGrantsRequest(req *pbs.SetRoleGrantsRequest) error { func validateRemoveRoleGrantsRequest(req *pbs.RemoveRoleGrantsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(iam.RolePrefix, req.GetRoleId()) { + if !handlers.ValidId(iam.RolePrefix, req.GetId()) { badFields["id"] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { diff --git a/internal/servers/controller/handlers/roles/role_service_test.go b/internal/servers/controller/handlers/roles/role_service_test.go index c6632e9ef..f751496d5 100644 --- a/internal/servers/controller/handlers/roles/role_service_test.go +++ b/internal/servers/controller/handlers/roles/role_service_test.go @@ -117,7 +117,7 @@ func TestGet(t *testing.T) { name: "Get a non existant Role", req: &pbs.GetRoleRequest{Id: iam.RolePrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Wrong id prefix", @@ -143,7 +143,7 @@ func TestGet(t *testing.T) { scopeId: pr.GetScopeId(), req: &pbs.GetRoleRequest{Id: iam.RolePrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Project Scoped Wrong id prefix", @@ -284,10 +284,7 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteRoleRequest{ Id: iam.RolePrefix + "_doesntexis", }, - res: &pbs.DeleteRoleResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, { name: "Bad Role Id formatting", @@ -315,10 +312,7 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteRoleRequest{ Id: iam.RolePrefix + "_doesntexis", }, - res: &pbs.DeleteRoleResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, } for _, tc := range cases { @@ -346,8 +340,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteRole(ctx, req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") projReq := &pbs.DeleteRoleRequest{ Id: pr.GetPublicId(), @@ -357,8 +351,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteRole(ctx, projReq) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") } @@ -823,8 +817,6 @@ func TestUpdate(t *testing.T) { }, errCode: codes.OK, }, - // TODO: Updating a non existant role should result in a NotFound exception but currently results in - // the repoFn returning an internal error. { name: "Update a Non Existing Role", req: &pbs.UpdateRoleRequest{ @@ -837,7 +829,7 @@ func TestUpdate(t *testing.T) { Description: &wrapperspb.StringValue{Value: "desc"}, }, }, - errCode: codes.Internal, + errCode: codes.PermissionDenied, }, { name: "Cant change Id", @@ -1033,7 +1025,7 @@ func TestAddPrincipal(t *testing.T) { role := iam.TestRole(t, conn, scope.GetPublicId()) tc.setup(role) req := &pbs.AddRolePrincipalsRequest{ - RoleId: role.GetPublicId(), + Id: role.GetPublicId(), Version: role.GetVersion(), PrincipalIds: append(tc.addUsers, tc.addGroups...), } @@ -1062,7 +1054,7 @@ func TestAddPrincipal(t *testing.T) { { name: "Bad Role Id", req: &pbs.AddRolePrincipalsRequest{ - RoleId: "bad id", + Id: "bad id", Version: role.GetVersion(), }, errCode: codes.InvalidArgument, @@ -1159,7 +1151,7 @@ func TestSetPrincipal(t *testing.T) { role := iam.TestRole(t, conn, scope.GetPublicId()) tc.setup(role) req := &pbs.SetRolePrincipalsRequest{ - RoleId: role.GetPublicId(), + Id: role.GetPublicId(), Version: role.GetVersion(), PrincipalIds: append(tc.setUsers, tc.setGroups...), } @@ -1188,7 +1180,7 @@ func TestSetPrincipal(t *testing.T) { { name: "Bad Role Id", req: &pbs.SetRolePrincipalsRequest{ - RoleId: "bad id", + Id: "bad id", Version: role.GetVersion(), }, errCode: codes.InvalidArgument, @@ -1297,7 +1289,7 @@ func TestRemovePrincipal(t *testing.T) { role := iam.TestRole(t, conn, scope.GetPublicId()) tc.setup(role) req := &pbs.RemoveRolePrincipalsRequest{ - RoleId: role.GetPublicId(), + Id: role.GetPublicId(), Version: role.GetVersion(), PrincipalIds: append(tc.removeUsers, tc.removeGroups...), } @@ -1326,7 +1318,7 @@ func TestRemovePrincipal(t *testing.T) { { name: "Bad Role Id", req: &pbs.AddRolePrincipalsRequest{ - RoleId: "bad id", + Id: "bad id", Version: role.GetVersion(), }, errCode: codes.InvalidArgument, @@ -1406,7 +1398,7 @@ func TestAddGrants(t *testing.T) { _ = iam.TestRoleGrant(t, conn, role.GetPublicId(), e) } req := &pbs.AddRoleGrantsRequest{ - RoleId: role.GetPublicId(), + Id: role.GetPublicId(), Version: role.GetVersion(), } scopeId := o.GetPublicId() @@ -1435,7 +1427,7 @@ func TestAddGrants(t *testing.T) { { name: "Bad Role Id", req: &pbs.AddRoleGrantsRequest{ - RoleId: "bad id", + Id: "bad id", GrantStrings: []string{"id=*;actions=create"}, Version: role.GetVersion(), }, @@ -1504,7 +1496,7 @@ func TestSetGrants(t *testing.T) { _ = iam.TestRoleGrant(t, conn, role.GetPublicId(), e) } req := &pbs.SetRoleGrantsRequest{ - RoleId: role.GetPublicId(), + Id: role.GetPublicId(), Version: role.GetVersion(), } scopeId := o.GetPublicId() @@ -1535,7 +1527,7 @@ func TestSetGrants(t *testing.T) { { name: "Bad Role Id", req: &pbs.SetRoleGrantsRequest{ - RoleId: "bad id", + Id: "bad id", GrantStrings: []string{"id=*;actions=create"}, Version: role.GetVersion(), }, @@ -1603,7 +1595,7 @@ func TestRemoveGrants(t *testing.T) { _ = iam.TestRoleGrant(t, conn, role.GetPublicId(), e) } req := &pbs.RemoveRoleGrantsRequest{ - RoleId: role.GetPublicId(), + Id: role.GetPublicId(), Version: role.GetVersion(), } scopeId := o.GetPublicId() @@ -1635,7 +1627,7 @@ func TestRemoveGrants(t *testing.T) { { name: "Bad Role Id", req: &pbs.RemoveRoleGrantsRequest{ - RoleId: "bad id", + Id: "bad id", GrantStrings: []string{"id=*;actions=create"}, Version: role.GetVersion(), }, diff --git a/internal/servers/controller/handlers/scopes/scope_service.go b/internal/servers/controller/handlers/scopes/scope_service.go index ada9dac1e..88eff42a0 100644 --- a/internal/servers/controller/handlers/scopes/scope_service.go +++ b/internal/servers/controller/handlers/scopes/scope_service.go @@ -3,7 +3,6 @@ package scopes import ( "context" "fmt" - "regexp" "sort" "strings" @@ -15,6 +14,8 @@ import ( "github.com/hashicorp/boundary/internal/iam/store" "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -23,7 +24,6 @@ import ( var ( maskManager handlers.MaskManager - reInvalidID = regexp.MustCompile("[^A-Za-z0-9]") ) func init() { @@ -35,7 +35,7 @@ func init() { // Service handles requests as described by the pbs.ScopeServiceServer interface. type Service struct { - repo common.IamRepoFactory + repoFn common.IamRepoFactory } // NewService returns a project service which handles project related requests to boundary. @@ -43,7 +43,7 @@ func NewService(repo common.IamRepoFactory) (Service, error) { if repo == nil { return Service{}, fmt.Errorf("nil iam repository provided") } - return Service{repo: repo}, nil + return Service{repoFn: repo}, nil } var _ pbs.ScopeServiceServer = Service{} @@ -53,14 +53,13 @@ func (s Service) ListScopes(ctx context.Context, req *pbs.ListScopesRequest) (*p if req.GetScopeId() == "" { req.ScopeId = scope.Global.String() } - authResults := auth.Verify(ctx, auth.WithScopeId(req.GetScopeId())) - if authResults.Error != nil { - return nil, authResults.Error - } - if err := validateListRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetScopeId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } pl, err := s.listFromRepo(ctx, authResults.Scope.GetId()) if err != nil { return nil, err @@ -75,13 +74,13 @@ func (s Service) ListScopes(ctx context.Context, req *pbs.ListScopesRequest) (*p // GetScopes implements the interface pbs.ScopeServiceServer. func (s Service) GetScope(ctx context.Context, req *pbs.GetScopeRequest) (*pbs.GetScopeResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateGetRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } p, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -95,7 +94,7 @@ func (s Service) CreateScope(ctx context.Context, req *pbs.CreateScopeRequest) ( if err := validateCreateRequest(req); err != nil { return nil, err } - authResults := auth.Verify(ctx, auth.WithScopeId(req.GetItem().GetScopeId())) + authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create) if authResults.Error != nil { return nil, authResults.Error } @@ -109,13 +108,13 @@ func (s Service) CreateScope(ctx context.Context, req *pbs.CreateScopeRequest) ( // UpdateScope implements the interface pbs.ScopeServiceServer. func (s Service) UpdateScope(ctx context.Context, req *pbs.UpdateScopeRequest) (*pbs.UpdateScopeResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateUpdateRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Update) + if authResults.Error != nil { + return nil, authResults.Error + } p, err := s.updateInRepo(ctx, authResults.Scope, req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) if err != nil { return nil, err @@ -126,13 +125,13 @@ func (s Service) UpdateScope(ctx context.Context, req *pbs.UpdateScopeRequest) ( // DeleteScope implements the interface pbs.ScopeServiceServer. func (s Service) DeleteScope(ctx context.Context, req *pbs.DeleteScopeRequest) (*pbs.DeleteScopeResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateDeleteRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } existed, err := s.deleteFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -141,7 +140,7 @@ func (s Service) DeleteScope(ctx context.Context, req *pbs.DeleteScopeRequest) ( } func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Scope, error) { - repo, err := s.repo() + repo, err := s.repoFn() if err != nil { return nil, err } @@ -178,7 +177,7 @@ func (s Service) createInRepo(ctx context.Context, authResults auth.VerifyResult if err != nil { return nil, status.Errorf(codes.Internal, "Unable to build new scope for creation: %v.", err) } - repo, err := s.repo() + repo, err := s.repoFn() if err != nil { return nil, err } @@ -218,7 +217,7 @@ func (s Service) updateInRepo(ctx context.Context, parentScope *scopes.ScopeInfo if len(dbMask) == 0 { return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."}) } - repo, err := s.repo() + repo, err := s.repoFn() if err != nil { return nil, err } @@ -233,7 +232,7 @@ func (s Service) updateInRepo(ctx context.Context, parentScope *scopes.ScopeInfo } func (s Service) deleteFromRepo(ctx context.Context, scopeId string) (bool, error) { - repo, err := s.repo() + repo, err := s.repoFn() if err != nil { return false, err } @@ -253,7 +252,7 @@ func SortScopes(scps []*pb.Scope) { } func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.Scope, error) { - repo, err := s.repo() + repo, err := s.repoFn() if err != nil { return nil, err } @@ -279,6 +278,45 @@ func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.Scope, return outPl, nil } +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} + repo, err := s.repoFn() + if err != nil { + res.Error = err + return res + } + + var parentId string + opts := []auth.Option{auth.WithType(resource.Scope), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + s, err := repo.LookupScope(ctx, parentId) + if err != nil { + res.Error = err + return res + } + if s == nil { + res.Error = handlers.ForbiddenError() + return res + } + default: + s, err := repo.LookupScope(ctx, id) + if err != nil { + res.Error = err + return res + } + if s == nil { + res.Error = handlers.ForbiddenError() + return res + } + parentId = s.GetParentId() + opts = append(opts, auth.WithId(id)) + } + opts = append(opts, auth.WithScopeId(parentId)) + return auth.Verify(ctx, opts...) +} + func ToProto(in *iam.Scope) *pb.Scope { out := pb.Scope{ Id: in.GetPublicId(), @@ -307,11 +345,11 @@ func validateGetRequest(req *pbs.GetScopeRequest) error { switch { case id == "global": case strings.HasPrefix(id, scope.Org.Prefix()): - if !validId(id, scope.Org.Prefix()+"_") { + if !handlers.ValidId(scope.Org.Prefix(), id) { badFields["id"] = "Invalidly formatted scope id." } case strings.HasPrefix(id, scope.Project.Prefix()): - if !validId(id, scope.Project.Prefix()+"_") { + if !handlers.ValidId(scope.Project.Prefix(), id) { badFields["id"] = "Invalidly formatted scope id." } default: @@ -326,8 +364,8 @@ func validateGetRequest(req *pbs.GetScopeRequest) error { func validateCreateRequest(req *pbs.CreateScopeRequest) error { badFields := map[string]string{} item := req.GetItem() - if req.GetItem().GetScopeId() == "" { - badFields["scope_id"] = "This is a required field." + if item.GetScopeId() == "" { + badFields["scope_id"] = "Missing value for scope_id" } if item.GetId() != "" { badFields["id"] = "This is a read only field." @@ -353,11 +391,11 @@ func validateUpdateRequest(req *pbs.UpdateScopeRequest) error { switch { case id == "global": case strings.HasPrefix(id, scope.Org.Prefix()): - if !validId(id, scope.Org.Prefix()+"_") { + if !handlers.ValidId(scope.Org.Prefix(), id) { badFields["id"] = "Invalidly formatted scope id." } case strings.HasPrefix(id, scope.Project.Prefix()): - if !validId(id, scope.Project.Prefix()+"_") { + if !handlers.ValidId(scope.Project.Prefix(), id) { badFields["id"] = "Invalidly formatted scope id." } default: @@ -402,11 +440,11 @@ func validateDeleteRequest(req *pbs.DeleteScopeRequest) error { case id == "global": badFields["id"] = "Invalid to delete the global scope." case strings.HasPrefix(id, scope.Org.Prefix()): - if !validId(id, scope.Org.Prefix()+"_") { + if !handlers.ValidId(scope.Org.Prefix(), id) { badFields["id"] = "Invalidly formatted scope id." } case strings.HasPrefix(id, scope.Project.Prefix()): - if !validId(id, scope.Project.Prefix()+"_") { + if !handlers.ValidId(scope.Project.Prefix(), id) { badFields["id"] = "Invalidly formatted scope id." } default: @@ -425,11 +463,3 @@ func validateListRequest(req *pbs.ListScopesRequest) error { } return nil } - -func validId(id, prefix string) bool { - if !strings.HasPrefix(id, prefix) { - return false - } - id = strings.TrimPrefix(id, prefix) - return !reInvalidID.Match([]byte(id)) -} diff --git a/internal/servers/controller/handlers/scopes/scope_service_test.go b/internal/servers/controller/handlers/scopes/scope_service_test.go index be0de51e5..74d41edea 100644 --- a/internal/servers/controller/handlers/scopes/scope_service_test.go +++ b/internal/servers/controller/handlers/scopes/scope_service_test.go @@ -101,7 +101,7 @@ func TestGet(t *testing.T) { scopeId: "global", req: &pbs.GetScopeRequest{Id: "o_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Get an existing project", @@ -115,7 +115,7 @@ func TestGet(t *testing.T) { scopeId: org.GetPublicId(), req: &pbs.GetScopeRequest{Id: "p_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Wrong id prefix", @@ -311,10 +311,7 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteScopeRequest{ Id: "p_doesntexis", }, - res: &pbs.DeleteScopeResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, { name: "Bad Project Id formatting", @@ -342,10 +339,7 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteScopeRequest{ Id: "p_doesntexis", }, - res: &pbs.DeleteScopeResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, { name: "Bad Org Id formatting", @@ -382,8 +376,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteScope(ctx, req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") ctx = auth.DisabledAuthTestContext(auth.WithScopeId(scope.Global.String())) req = &pbs.DeleteScopeRequest{ @@ -393,8 +387,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteScope(ctx, req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") } func TestCreate(t *testing.T) { @@ -811,8 +805,6 @@ func TestUpdate(t *testing.T) { }, errCode: codes.OK, }, - // TODO: Updating a non existant project should result in a NotFound exception but currently results in - // the repoFn returning an internal error. { name: "Update a Non Existing Project", scopeId: org.GetPublicId(), @@ -826,7 +818,7 @@ func TestUpdate(t *testing.T) { Description: &wrapperspb.StringValue{Value: "desc"}, }, }, - errCode: codes.Internal, + errCode: codes.PermissionDenied, }, { name: "Cant change Id", diff --git a/internal/servers/controller/handlers/targets/target_service.go b/internal/servers/controller/handlers/targets/target_service.go index bb56a2a63..59a4a6a57 100644 --- a/internal/servers/controller/handlers/targets/target_service.go +++ b/internal/servers/controller/handlers/targets/target_service.go @@ -9,11 +9,12 @@ import ( "github.com/hashicorp/boundary/internal/db" pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/targets" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" - "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/store" "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -33,17 +34,17 @@ func init() { // Service handles request as described by the pbs.TargetServiceServer interface. type Service struct { - repoFn func() (*target.Repository, error) - iamRepoFn func() (*iam.Repository, error) + repoFn common.TargetRepoFactory + iamRepoFn common.IamRepoFactory } // NewService returns a target service which handles target related requests to boundary. -func NewService(repoFn func() (*target.Repository, error), iamRepoFn func() (*iam.Repository, error)) (Service, error) { +func NewService(repoFn common.TargetRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) { if repoFn == nil { return Service{}, fmt.Errorf("nil target repository provided") } if iamRepoFn == nil { - return Service{}, fmt.Errorf("nil target repository provided") + return Service{}, fmt.Errorf("nil iam repository provided") } return Service{repoFn: repoFn, iamRepoFn: iamRepoFn}, nil } @@ -55,7 +56,7 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) ( if err := validateListRequest(req); err != nil { return nil, err } - authResults := s.collectionAuthVerify(ctx, req.GetScopeId(), action.List) + authResults := s.authResult(ctx, req.GetScopeId(), action.List) if authResults.Error != nil { return nil, authResults.Error } @@ -74,7 +75,7 @@ func (s Service) GetTarget(ctx context.Context, req *pbs.GetTargetRequest) (*pbs if err := validateGetRequest(req); err != nil { return nil, err } - authResults := s.authVerify(ctx, req.GetId(), action.Read) + authResults := s.authResult(ctx, req.GetId(), action.Read) if authResults.Error != nil { return nil, authResults.Error } @@ -91,7 +92,7 @@ func (s Service) CreateTarget(ctx context.Context, req *pbs.CreateTargetRequest) if err := validateCreateRequest(req); err != nil { return nil, err } - authResults := s.collectionAuthVerify(ctx, req.GetItem().GetScopeId(), action.Create) + authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create) if authResults.Error != nil { return nil, authResults.Error } @@ -108,7 +109,7 @@ func (s Service) UpdateTarget(ctx context.Context, req *pbs.UpdateTargetRequest) if err := validateUpdateRequest(req); err != nil { return nil, err } - authResults := s.authVerify(ctx, req.GetId(), action.Update) + authResults := s.authResult(ctx, req.GetId(), action.Update) if authResults.Error != nil { return nil, authResults.Error } @@ -125,7 +126,7 @@ func (s Service) DeleteTarget(ctx context.Context, req *pbs.DeleteTargetRequest) if err := validateDeleteRequest(req); err != nil { return nil, err } - authResults := s.authVerify(ctx, req.GetId(), action.Delete) + authResults := s.authResult(ctx, req.GetId(), action.Delete) if authResults.Error != nil { return nil, authResults.Error } @@ -141,7 +142,7 @@ func (s Service) AddTargetHostSets(ctx context.Context, req *pbs.AddTargetHostSe if err := validateAddRequest(req); err != nil { return nil, err } - authResults := s.authVerify(ctx, req.GetId(), action.AddHostSets) + authResults := s.authResult(ctx, req.GetId(), action.AddHostSets) if authResults.Error != nil { return nil, authResults.Error } @@ -158,7 +159,7 @@ func (s Service) SetTargetHostSets(ctx context.Context, req *pbs.SetTargetHostSe if err := validateSetRequest(req); err != nil { return nil, err } - authResults := s.authVerify(ctx, req.GetId(), action.SetHostSets) + authResults := s.authResult(ctx, req.GetId(), action.SetHostSets) if authResults.Error != nil { return nil, authResults.Error } @@ -175,7 +176,7 @@ func (s Service) RemoveTargetHostSets(ctx context.Context, req *pbs.RemoveTarget if err := validateRemoveRequest(req); err != nil { return nil, err } - authResults := s.authVerify(ctx, req.GetId(), action.RemoveHostSets) + authResults := s.authResult(ctx, req.GetId(), action.RemoveHostSets) if authResults.Error != nil { return nil, authResults.Error } @@ -348,48 +349,48 @@ func (s Service) removeInRepo(ctx context.Context, targetId string, hostSetIds [ return toProto(out, m), nil } -func (s Service) collectionAuthVerify(ctx context.Context, parentId string, a action.Type) auth.VerifyResults { - var res auth.VerifyResults - iamRep, err := s.iamRepoFn() - if err != nil { - res.Error = err - return res - } - sc, err := iamRep.LookupScope(ctx, parentId) - if err != nil { - res.Error = err - return res - } - if sc == nil { - res.Error = handlers.ForbiddenError() - return res - } - authResults := auth.Verify(ctx, - auth.WithScopeId(sc.GetPublicId()), - auth.WithAction(a)) - return authResults -} +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} -func (s Service) authVerify(ctx context.Context, id string, a action.Type) auth.VerifyResults { - var res auth.VerifyResults - repo, err := s.repoFn() - if err != nil { - res.Error = err - return res + var parentId string + opts := []auth.Option{auth.WithType(resource.Target), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + iamRepo, err := s.iamRepoFn() + if err != nil { + res.Error = err + return res + } + scp, err := iamRepo.LookupScope(ctx, parentId) + if err != nil { + res.Error = err + return res + } + if scp == nil { + res.Error = handlers.ForbiddenError() + return res + } + default: + repo, err := s.repoFn() + if err != nil { + res.Error = err + return res + } + t, _, err := repo.LookupTarget(ctx, id) + if err != nil { + res.Error = err + return res + } + if t == nil { + res.Error = handlers.ForbiddenError() + return res + } + parentId = t.GetScopeId() + opts = append(opts, auth.WithId(id)) } - tar, _, err := repo.LookupTarget(ctx, id) - if err != nil { - res.Error = err - return res - } - if tar == nil { - res.Error = handlers.ForbiddenError() - return res - } - authResults := auth.Verify(ctx, - auth.WithScopeId(tar.GetScopeId()), - auth.WithAction(a)) - return authResults + opts = append(opts, auth.WithScopeId(parentId)) + return auth.Verify(ctx, opts...) } func toProto(in target.Target, m []*target.TargetSet) *pb.Target { diff --git a/internal/servers/controller/handlers/users/user_service.go b/internal/servers/controller/handlers/users/user_service.go index be0b9dd41..3afb4a17b 100644 --- a/internal/servers/controller/handlers/users/user_service.go +++ b/internal/servers/controller/handlers/users/user_service.go @@ -11,7 +11,10 @@ import ( pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/iam/store" + "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -31,11 +34,11 @@ func init() { // Service handles request as described by the pbs.UserServiceServer interface. type Service struct { - repoFn func() (*iam.Repository, error) + repoFn common.IamRepoFactory } // NewService returns a user service which handles user related requests to boundary. -func NewService(repo func() (*iam.Repository, error)) (Service, error) { +func NewService(repo common.IamRepoFactory) (Service, error) { if repo == nil { return Service{}, fmt.Errorf("nil iam repository provided") } @@ -46,13 +49,13 @@ var _ pbs.UserServiceServer = Service{} // ListUsers implements the interface pbs.UserServiceServer. func (s Service) ListUsers(ctx context.Context, req *pbs.ListUsersRequest) (*pbs.ListUsersResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateListRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetScopeId(), action.List) + if authResults.Error != nil { + return nil, authResults.Error + } ul, err := s.listFromRepo(ctx, req.GetScopeId()) if err != nil { return nil, err @@ -65,13 +68,13 @@ func (s Service) ListUsers(ctx context.Context, req *pbs.ListUsersRequest) (*pbs // GetUsers implements the interface pbs.UserServiceServer. func (s Service) GetUser(ctx context.Context, req *pbs.GetUserRequest) (*pbs.GetUserResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateGetRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Read) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.getFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -82,13 +85,13 @@ func (s Service) GetUser(ctx context.Context, req *pbs.GetUserRequest) (*pbs.Get // CreateUser implements the interface pbs.UserServiceServer. func (s Service) CreateUser(ctx context.Context, req *pbs.CreateUserRequest) (*pbs.CreateUserResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateCreateRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetItem().GetScopeId(), action.Create) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem()) if err != nil { return nil, err @@ -99,13 +102,13 @@ func (s Service) CreateUser(ctx context.Context, req *pbs.CreateUserRequest) (*p // UpdateUser implements the interface pbs.UserServiceServer. func (s Service) UpdateUser(ctx context.Context, req *pbs.UpdateUserRequest) (*pbs.UpdateUserResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateUpdateRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Update) + if authResults.Error != nil { + return nil, authResults.Error + } u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) if err != nil { return nil, err @@ -116,13 +119,13 @@ func (s Service) UpdateUser(ctx context.Context, req *pbs.UpdateUserRequest) (*p // DeleteUser implements the interface pbs.UserServiceServer. func (s Service) DeleteUser(ctx context.Context, req *pbs.DeleteUserRequest) (*pbs.DeleteUserResponse, error) { - authResults := auth.Verify(ctx) - if authResults.Error != nil { - return nil, authResults.Error - } if err := validateDeleteRequest(req); err != nil { return nil, err } + authResults := s.authResult(ctx, req.GetId(), action.Delete) + if authResults.Error != nil { + return nil, authResults.Error + } existed, err := s.deleteFromRepo(ctx, req.GetId()) if err != nil { return nil, err @@ -237,6 +240,45 @@ func (s Service) listFromRepo(ctx context.Context, orgId string) ([]*pb.User, er return outUl, nil } +func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { + res := auth.VerifyResults{} + repo, err := s.repoFn() + if err != nil { + res.Error = err + return res + } + + var parentId string + opts := []auth.Option{auth.WithType(resource.User), auth.WithAction(a)} + switch a { + case action.List, action.Create: + parentId = id + scp, err := repo.LookupScope(ctx, parentId) + if err != nil { + res.Error = err + return res + } + if scp == nil { + res.Error = handlers.ForbiddenError() + return res + } + default: + u, err := repo.LookupUser(ctx, id) + if err != nil { + res.Error = err + return res + } + if u == nil { + res.Error = handlers.ForbiddenError() + return res + } + parentId = u.GetScopeId() + opts = append(opts, auth.WithId(id)) + } + opts = append(opts, auth.WithScopeId(parentId)) + return auth.Verify(ctx, opts...) +} + func toProto(in *iam.User) *pb.User { out := pb.User{ Id: in.GetPublicId(), diff --git a/internal/servers/controller/handlers/users/user_service_test.go b/internal/servers/controller/handlers/users/user_service_test.go index 6d08c656c..0eb1406d1 100644 --- a/internal/servers/controller/handlers/users/user_service_test.go +++ b/internal/servers/controller/handlers/users/user_service_test.go @@ -75,7 +75,7 @@ func TestGet(t *testing.T) { name: "Get a non existant User", req: &pbs.GetUserRequest{Id: iam.UserPrefix + "_DoesntExis"}, res: nil, - errCode: codes.NotFound, + errCode: codes.PermissionDenied, }, { name: "Wrong id prefix", @@ -194,10 +194,7 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteUserRequest{ Id: iam.UserPrefix + "_doesntexis", }, - res: &pbs.DeleteUserResponse{ - Existed: false, - }, - errCode: codes.OK, + errCode: codes.PermissionDenied, }, { name: "Bad User Id formatting", @@ -233,8 +230,8 @@ func TestDelete_twice(t *testing.T) { assert.NoError(gErr, "First attempt") assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") got, gErr = s.DeleteUser(ctx, req) - assert.NoError(gErr, "Second attempt") - assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") + assert.Error(gErr, "Second attempt") + assert.Equal(codes.PermissionDenied, status.Code(gErr), "Expected permission denied for the second delete.") } func TestCreate(t *testing.T) { @@ -517,8 +514,6 @@ func TestUpdate(t *testing.T) { }, errCode: codes.OK, }, - // TODO: Updating a non existant user should result in a NotFound exception but currently results in - // the repoFn returning an internal error. { name: "Update a Non Existing User", req: &pbs.UpdateUserRequest{ @@ -531,7 +526,7 @@ func TestUpdate(t *testing.T) { Description: &wrapperspb.StringValue{Value: "desc"}, }, }, - errCode: codes.Internal, + errCode: codes.PermissionDenied, }, { name: "Cant change Id",