diff --git a/api/internal/genapi/input.go b/api/internal/genapi/input.go index 3a52ea8d7..03556a37d 100644 --- a/api/internal/genapi/input.go +++ b/api/internal/genapi/input.go @@ -137,6 +137,11 @@ var inputStructs = []*structInfo{ createResponseTypes: true, }, // User related resources + { + inProto: &users.Account{}, + outFile: "users/account.gen.go", + outputOnly: true, + }, { inProto: &users.User{}, outFile: "users/user.gen.go", @@ -148,6 +153,9 @@ var inputStructs = []*structInfo{ deleteTemplate, listTemplate, }, + sliceSubTypes: map[string]string{ + "Accounts": "accountIds", + }, pathArgs: []string{"user"}, versionEnabled: true, createResponseTypes: true, diff --git a/api/users/account.gen.go b/api/users/account.gen.go new file mode 100644 index 000000000..5d7c76e97 --- /dev/null +++ b/api/users/account.gen.go @@ -0,0 +1,7 @@ +// Code generated by "make api"; DO NOT EDIT. +package users + +type Account struct { + Id string `json:"id,omitempty"` + ScopeId string `json:"scope_id,omitempty"` +} diff --git a/api/users/user.gen.go b/api/users/user.gen.go index e59bc61c5..0d77fa35b 100644 --- a/api/users/user.gen.go +++ b/api/users/user.gen.go @@ -22,6 +22,8 @@ type User struct { CreatedTime time.Time `json:"created_time,omitempty"` UpdatedTime time.Time `json:"updated_time,omitempty"` Version uint32 `json:"version,omitempty"` + AccountIds []string `json:"account_ids,omitempty"` + Accounts []*Account `json:"accounts,omitempty"` responseBody *bytes.Buffer responseMap map[string]interface{} @@ -340,3 +342,205 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*User target.responseMap = resp.Map return target, nil } + +func (c *Client) AddAccounts(ctx context.Context, userId string, version uint32, accountIds []string, opt ...Option) (*UserUpdateResult, error) { + if userId == "" { + return nil, fmt.Errorf("empty userId value passed into AddAccounts request") + } + if len(accountIds) == 0 { + return nil, errors.New("empty accountIds passed into AddAccounts request") + } + if c.client == nil { + return nil, errors.New("nil client") + } + + opts, apiOpts := getOpts(opt...) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, errors.New("zero version number passed into AddAccounts request") + } + existingTarget, existingErr := c.Read(ctx, userId, opt...) + if existingErr != nil { + if api.AsServerError(existingErr) != nil { + return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) + } + return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingTarget == nil { + return nil, errors.New("nil resource response found when performing initial check-and-set read") + } + if existingTarget.Item == nil { + return nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Item.Version + } + + opts.postMap["version"] = version + opts.postMap["account_ids"] = accountIds + + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("users/%s:add-accounts", userId), opts.postMap, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating AddAccounts request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during AddAccounts call: %w", err) + } + + target := new(UserUpdateResult) + target.Item = new(User) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding AddAccounts response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.responseBody = resp.Body + target.responseMap = resp.Map + return target, nil +} + +func (c *Client) SetAccounts(ctx context.Context, userId string, version uint32, accountIds []string, opt ...Option) (*UserUpdateResult, error) { + if userId == "" { + return nil, fmt.Errorf("empty userId value passed into SetAccounts request") + } + + if c.client == nil { + return nil, errors.New("nil client") + } + + opts, apiOpts := getOpts(opt...) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, errors.New("zero version number passed into SetAccounts request") + } + existingTarget, existingErr := c.Read(ctx, userId, opt...) + if existingErr != nil { + if api.AsServerError(existingErr) != nil { + return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) + } + return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingTarget == nil { + return nil, errors.New("nil resource response found when performing initial check-and-set read") + } + if existingTarget.Item == nil { + return nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Item.Version + } + + opts.postMap["version"] = version + opts.postMap["account_ids"] = accountIds + + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("users/%s:set-accounts", userId), opts.postMap, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating SetAccounts request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during SetAccounts call: %w", err) + } + + target := new(UserUpdateResult) + target.Item = new(User) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding SetAccounts response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.responseBody = resp.Body + target.responseMap = resp.Map + return target, nil +} + +func (c *Client) RemoveAccounts(ctx context.Context, userId string, version uint32, accountIds []string, opt ...Option) (*UserUpdateResult, error) { + if userId == "" { + return nil, fmt.Errorf("empty userId value passed into RemoveAccounts request") + } + if len(accountIds) == 0 { + return nil, errors.New("empty accountIds passed into RemoveAccounts request") + } + if c.client == nil { + return nil, errors.New("nil client") + } + + opts, apiOpts := getOpts(opt...) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, errors.New("zero version number passed into RemoveAccounts request") + } + existingTarget, existingErr := c.Read(ctx, userId, opt...) + if existingErr != nil { + if api.AsServerError(existingErr) != nil { + return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr) + } + return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingTarget == nil { + return nil, errors.New("nil resource response found when performing initial check-and-set read") + } + if existingTarget.Item == nil { + return nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Item.Version + } + + opts.postMap["version"] = version + opts.postMap["account_ids"] = accountIds + + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("users/%s:remove-accounts", userId), opts.postMap, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating RemoveAccounts request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during RemoveAccounts call: %w", err) + } + + target := new(UserUpdateResult) + target.Item = new(User) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding RemoveAccounts response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.responseBody = resp.Body + target.responseMap = resp.Map + return target, nil +} diff --git a/api/users/user_test.go b/api/users/user_test.go index 510705044..70a11e4a0 100644 --- a/api/users/user_test.go +++ b/api/users/user_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/accounts" "github.com/hashicorp/boundary/api/users" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/servers/controller" @@ -13,6 +14,46 @@ import ( "github.com/stretchr/testify/require" ) +func TestCustom(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) + uClient := users.NewClient(client) + aClient := accounts.NewClient(client) + + usr1, err := uClient.Create(tc.Context(), "global") + require.NoError(err) + + acct1, err := aClient.Create(tc.Context(), token.AuthMethodId, accounts.WithPasswordAccountLoginName("accountname")) + require.NoError(err) + acct2, err := aClient.Create(tc.Context(), token.AuthMethodId, accounts.WithPasswordAccountLoginName("accountname2")) + require.NoError(err) + + addResult, err := uClient.AddAccounts(tc.Context(), usr1.Item.Id, 0, []string{acct1.Item.Id, acct2.Item.Id}, users.WithAutomaticVersioning(true)) + assert.NoError(err) + assert.ElementsMatch([]string{acct1.Item.Id, acct2.Item.Id}, addResult.Item.AccountIds) + + // Add the account to the default user + _, err = uClient.AddAccounts(tc.Context(), token.UserId, 0, []string{acct1.Item.Id}, users.WithAutomaticVersioning(true)) + assert.Error(err) + + remResult, err := uClient.RemoveAccounts(tc.Context(), usr1.Item.Id, 0, []string{acct1.Item.Id}, users.WithAutomaticVersioning(true)) + assert.NoError(err) + assert.ElementsMatch([]string{acct2.Item.Id}, remResult.Item.AccountIds) + + // Cannot remove an account that isn't associated with a user. + _, err = uClient.RemoveAccounts(tc.Context(), usr1.Item.Id, 0, []string{acct1.Item.Id}, users.WithAutomaticVersioning(true)) + assert.Error(err) + + setResult, err := uClient.SetAccounts(tc.Context(), usr1.Item.Id, 0, []string{acct1.Item.Id, acct2.Item.Id}, users.WithAutomaticVersioning(true)) + assert.NoError(err) + assert.ElementsMatch([]string{acct1.Item.Id, acct2.Item.Id}, setResult.Item.AccountIds) +} + func TestList(t *testing.T) { assert, require := assert.New(t), require.New(t) tc := controller.NewTestController(t, nil) diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 1b31ccdb0..1b983e69b 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -2342,6 +2342,105 @@ "controller.api.services.v1.UserService" ] } + }, + "/v1/users/{id}:add-accounts": { + "post": { + "summary": "Associates an account to a User.", + "operationId": "UserService_AddUserAccounts", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.users.v1.User" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.services.v1.AddUserAccountsRequest" + } + } + ], + "tags": [ + "controller.api.services.v1.UserService" + ] + } + }, + "/v1/users/{id}:remove-accounts": { + "post": { + "summary": "Removes the specified Accounts from being associated with the provided User.", + "operationId": "UserService_RemoveUserAccounts", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.users.v1.User" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.services.v1.RemoveUserAccountsRequest" + } + } + ], + "tags": [ + "controller.api.services.v1.UserService" + ] + } + }, + "/v1/users/{id}:set-accounts": { + "post": { + "summary": "Set the Accounts associated to the User to exactly the list of provided in the request, removing any Accounts that are not specified.", + "operationId": "UserService_SetUserAccounts", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.users.v1.User" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.services.v1.SetUserAccountsRequest" + } + } + ], + "tags": [ + "controller.api.services.v1.UserService" + ] + } } }, "definitions": { @@ -3258,6 +3357,21 @@ }, "title": "Target contains all fields related to a Target resource" }, + "controller.api.resources.users.v1.Account": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the account.\nOutput only.", + "readOnly": true + }, + "scope_id": { + "type": "string", + "description": "The scope ID of the account.\nOutput only.", + "readOnly": true + } + } + }, "controller.api.resources.users.v1.User": { "type": "object", "properties": { @@ -3299,6 +3413,22 @@ "type": "integer", "format": "int64", "description": "The version can be used in subsequent write requests to ensure this\nresource has not changed and to fail the write if it has." + }, + "account_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Contains the list of account ids for this user.\nOutput only.", + "readOnly": true + }, + "accounts": { + "type": "array", + "items": { + "$ref": "#/definitions/controller.api.resources.users.v1.Account" + }, + "description": "The acounts for this user.\nOutput only.", + "readOnly": true } }, "title": "User contains all fields related to a User resource" @@ -3438,6 +3568,33 @@ } } }, + "controller.api.services.v1.AddUserAccountsRequest": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "version": { + "type": "integer", + "format": "int64", + "description": "The version ensures the user hasn't changed since it was last retrieved and if\nit has the request will fail." + }, + "account_ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "controller.api.services.v1.AddUserAccountsResponse": { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/controller.api.resources.users.v1.User" + } + } + }, "controller.api.services.v1.AuthenticateRequest": { "type": "object", "properties": { @@ -4040,6 +4197,33 @@ } } }, + "controller.api.services.v1.RemoveUserAccountsRequest": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "version": { + "type": "integer", + "format": "int64", + "description": "The version ensures the user hasn't changed since it was last retrieved and if\nit has the request will fail." + }, + "account_ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "controller.api.services.v1.RemoveUserAccountsResponse": { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/controller.api.resources.users.v1.User" + } + } + }, "controller.api.services.v1.SetGroupMembersRequest": { "type": "object", "properties": { @@ -4198,6 +4382,33 @@ } } }, + "controller.api.services.v1.SetUserAccountsRequest": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "version": { + "type": "integer", + "format": "int64", + "description": "The version ensures the user hasn't changed since it was last retrieved and if\nit has the request will fail." + }, + "account_ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "controller.api.services.v1.SetUserAccountsResponse": { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/controller.api.resources.users.v1.User" + } + } + }, "controller.api.services.v1.UpdateAccountResponse": { "type": "object", "properties": { diff --git a/internal/gen/controller/api/resources/users/user.pb.go b/internal/gen/controller/api/resources/users/user.pb.go index 3f7ca9aba..c4b37ce37 100644 --- a/internal/gen/controller/api/resources/users/user.pb.go +++ b/internal/gen/controller/api/resources/users/user.pb.go @@ -29,6 +29,65 @@ const ( // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 +type Account struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The ID of the account. + // Output only. + Id string `protobuf:"bytes,10,opt,name=id,proto3" json:"id,omitempty"` + // The scope ID of the account. + // Output only. + ScopeId string `protobuf:"bytes,20,opt,name=scope_id,proto3" json:"scope_id,omitempty"` +} + +func (x *Account) Reset() { + *x = Account{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_resources_users_v1_user_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Account) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Account) ProtoMessage() {} + +func (x *Account) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_resources_users_v1_user_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Account.ProtoReflect.Descriptor instead. +func (*Account) Descriptor() ([]byte, []int) { + return file_controller_api_resources_users_v1_user_proto_rawDescGZIP(), []int{0} +} + +func (x *Account) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Account) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + // User contains all fields related to a User resource type User struct { state protoimpl.MessageState @@ -56,12 +115,18 @@ type User struct { // The version can be used in subsequent write requests to ensure this // resource has not changed and to fail the write if it has. Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty"` + // Contains the list of account ids for this user. + // Output only. + AccountIds []string `protobuf:"bytes,90,rep,name=account_ids,json=accountIds,proto3" json:"account_ids,omitempty"` + // The acounts for this user. + // Output only. + Accounts []*Account `protobuf:"bytes,100,rep,name=accounts,proto3" json:"accounts,omitempty"` } func (x *User) Reset() { *x = User{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_users_v1_user_proto_msgTypes[0] + mi := &file_controller_api_resources_users_v1_user_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -74,7 +139,7 @@ func (x *User) String() string { func (*User) ProtoMessage() {} func (x *User) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_users_v1_user_proto_msgTypes[0] + mi := &file_controller_api_resources_users_v1_user_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -87,7 +152,7 @@ func (x *User) ProtoReflect() protoreflect.Message { // Deprecated: Use User.ProtoReflect.Descriptor instead. func (*User) Descriptor() ([]byte, []int) { - return file_controller_api_resources_users_v1_user_proto_rawDescGZIP(), []int{0} + return file_controller_api_resources_users_v1_user_proto_rawDescGZIP(), []int{1} } func (x *User) GetId() string { @@ -146,6 +211,20 @@ func (x *User) GetVersion() uint32 { return 0 } +func (x *User) GetAccountIds() []string { + if x != nil { + return x.AccountIds + } + return nil +} + +func (x *User) GetAccounts() []*Account { + if x != nil { + return x.Accounts + } + return nil +} + var File_controller_api_resources_users_v1_user_proto protoreflect.FileDescriptor var file_controller_api_resources_users_v1_user_proto_rawDesc = []byte{ @@ -163,35 +242,45 @@ var file_controller_api_resources_users_v1_user_proto_rawDesc = []byte{ 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, - 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbd, - 0x03, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2d, 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, 0x73, 0x63, - 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x51, + 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x35, + 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, + 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, + 0x70, 0x65, 0x5f, 0x69, 0x64, 0x22, 0xa6, 0x04, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05, 0x73, 0x63, + 0x6f, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 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, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x63, 0x6f, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, + 0x46, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x14, 0xa0, 0xda, 0x29, + 0x01, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 0xa0, 0xda, 0x29, 0x01, + 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x5a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x12, 0x46, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 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, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x42, 0x51, 0x5a, 0x4f, 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, @@ -212,24 +301,26 @@ func file_controller_api_resources_users_v1_user_proto_rawDescGZIP() []byte { return file_controller_api_resources_users_v1_user_proto_rawDescData } -var file_controller_api_resources_users_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_api_resources_users_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_controller_api_resources_users_v1_user_proto_goTypes = []interface{}{ - (*User)(nil), // 0: controller.api.resources.users.v1.User - (*scopes.ScopeInfo)(nil), // 1: controller.api.resources.scopes.v1.ScopeInfo - (*wrappers.StringValue)(nil), // 2: google.protobuf.StringValue - (*timestamp.Timestamp)(nil), // 3: google.protobuf.Timestamp + (*Account)(nil), // 0: controller.api.resources.users.v1.Account + (*User)(nil), // 1: controller.api.resources.users.v1.User + (*scopes.ScopeInfo)(nil), // 2: controller.api.resources.scopes.v1.ScopeInfo + (*wrappers.StringValue)(nil), // 3: google.protobuf.StringValue + (*timestamp.Timestamp)(nil), // 4: google.protobuf.Timestamp } var file_controller_api_resources_users_v1_user_proto_depIdxs = []int32{ - 1, // 0: controller.api.resources.users.v1.User.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo - 2, // 1: controller.api.resources.users.v1.User.name:type_name -> google.protobuf.StringValue - 2, // 2: controller.api.resources.users.v1.User.description:type_name -> google.protobuf.StringValue - 3, // 3: controller.api.resources.users.v1.User.created_time:type_name -> google.protobuf.Timestamp - 3, // 4: controller.api.resources.users.v1.User.updated_time:type_name -> google.protobuf.Timestamp - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 2, // 0: controller.api.resources.users.v1.User.scope:type_name -> controller.api.resources.scopes.v1.ScopeInfo + 3, // 1: controller.api.resources.users.v1.User.name:type_name -> google.protobuf.StringValue + 3, // 2: controller.api.resources.users.v1.User.description:type_name -> google.protobuf.StringValue + 4, // 3: controller.api.resources.users.v1.User.created_time:type_name -> google.protobuf.Timestamp + 4, // 4: controller.api.resources.users.v1.User.updated_time:type_name -> google.protobuf.Timestamp + 0, // 5: controller.api.resources.users.v1.User.accounts:type_name -> controller.api.resources.users.v1.Account + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_controller_api_resources_users_v1_user_proto_init() } @@ -239,6 +330,18 @@ func file_controller_api_resources_users_v1_user_proto_init() { } if !protoimpl.UnsafeEnabled { file_controller_api_resources_users_v1_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_resources_users_v1_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*User); i { case 0: return &v.state @@ -257,7 +360,7 @@ func file_controller_api_resources_users_v1_user_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_api_resources_users_v1_user_proto_rawDesc, NumEnums: 0, - NumMessages: 1, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/gen/controller/api/services/user_service.pb.go b/internal/gen/controller/api/services/user_service.pb.go index a020c5aab..020aa9497 100644 --- a/internal/gen/controller/api/services/user_service.pb.go +++ b/internal/gen/controller/api/services/user_service.pb.go @@ -534,6 +534,342 @@ func (*DeleteUserResponse) Descriptor() ([]byte, []int) { return file_controller_api_services_v1_user_service_proto_rawDescGZIP(), []int{9} } +type AddUserAccountsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // The version ensures the user 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"` + AccountIds []string `protobuf:"bytes,3,rep,name=account_ids,proto3" json:"account_ids,omitempty"` +} + +func (x *AddUserAccountsRequest) Reset() { + *x = AddUserAccountsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddUserAccountsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddUserAccountsRequest) ProtoMessage() {} + +func (x *AddUserAccountsRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddUserAccountsRequest.ProtoReflect.Descriptor instead. +func (*AddUserAccountsRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_user_service_proto_rawDescGZIP(), []int{10} +} + +func (x *AddUserAccountsRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AddUserAccountsRequest) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *AddUserAccountsRequest) GetAccountIds() []string { + if x != nil { + return x.AccountIds + } + return nil +} + +type AddUserAccountsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *users.User `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *AddUserAccountsResponse) Reset() { + *x = AddUserAccountsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddUserAccountsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddUserAccountsResponse) ProtoMessage() {} + +func (x *AddUserAccountsResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddUserAccountsResponse.ProtoReflect.Descriptor instead. +func (*AddUserAccountsResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_user_service_proto_rawDescGZIP(), []int{11} +} + +func (x *AddUserAccountsResponse) GetItem() *users.User { + if x != nil { + return x.Item + } + return nil +} + +type SetUserAccountsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // The version ensures the user 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"` + AccountIds []string `protobuf:"bytes,3,rep,name=account_ids,proto3" json:"account_ids,omitempty"` +} + +func (x *SetUserAccountsRequest) Reset() { + *x = SetUserAccountsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetUserAccountsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetUserAccountsRequest) ProtoMessage() {} + +func (x *SetUserAccountsRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetUserAccountsRequest.ProtoReflect.Descriptor instead. +func (*SetUserAccountsRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_user_service_proto_rawDescGZIP(), []int{12} +} + +func (x *SetUserAccountsRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *SetUserAccountsRequest) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *SetUserAccountsRequest) GetAccountIds() []string { + if x != nil { + return x.AccountIds + } + return nil +} + +type SetUserAccountsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *users.User `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *SetUserAccountsResponse) Reset() { + *x = SetUserAccountsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetUserAccountsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetUserAccountsResponse) ProtoMessage() {} + +func (x *SetUserAccountsResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetUserAccountsResponse.ProtoReflect.Descriptor instead. +func (*SetUserAccountsResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_user_service_proto_rawDescGZIP(), []int{13} +} + +func (x *SetUserAccountsResponse) GetItem() *users.User { + if x != nil { + return x.Item + } + return nil +} + +type RemoveUserAccountsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // The version ensures the user 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"` + AccountIds []string `protobuf:"bytes,3,rep,name=account_ids,proto3" json:"account_ids,omitempty"` +} + +func (x *RemoveUserAccountsRequest) Reset() { + *x = RemoveUserAccountsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveUserAccountsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveUserAccountsRequest) ProtoMessage() {} + +func (x *RemoveUserAccountsRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveUserAccountsRequest.ProtoReflect.Descriptor instead. +func (*RemoveUserAccountsRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_user_service_proto_rawDescGZIP(), []int{14} +} + +func (x *RemoveUserAccountsRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *RemoveUserAccountsRequest) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *RemoveUserAccountsRequest) GetAccountIds() []string { + if x != nil { + return x.AccountIds + } + return nil +} + +type RemoveUserAccountsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *users.User `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *RemoveUserAccountsResponse) Reset() { + *x = RemoveUserAccountsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveUserAccountsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveUserAccountsResponse) ProtoMessage() {} + +func (x *RemoveUserAccountsResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_user_service_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveUserAccountsResponse.ProtoReflect.Descriptor instead. +func (*RemoveUserAccountsResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_user_service_proto_rawDescGZIP(), []int{15} +} + +func (x *RemoveUserAccountsResponse) GetItem() *users.User { + if x != nil { + return x.Item + } + return nil +} + var File_controller_api_services_v1_user_service_proto protoreflect.FileDescriptor var file_controller_api_services_v1_user_service_proto_rawDesc = []byte{ @@ -599,7 +935,43 @@ var file_controller_api_services_v1_user_service_proto_rawDesc = []byte{ 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, - 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x9e, 0x06, 0x0a, 0x0b, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x0a, 0x16, 0x41, + 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 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, + 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x73, 0x22, 0x56, 0x0a, 0x17, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, + 0x75, 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, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x64, 0x0a, 0x16, 0x53, 0x65, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 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, 0x20, 0x0a, + 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x22, + 0x56, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 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, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x67, 0x0a, 0x19, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 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, 0x20, + 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, + 0x22, 0x59, 0x0a, 0x1a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, + 0x63, 0x6f, 0x75, 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, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0xaf, 0x0c, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x97, 0x01, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 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, @@ -649,13 +1021,62 @@ var file_controller_api_services_v1_user_service_proto_rawDesc = []byte{ 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x2a, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x10, 0x12, 0x0e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x55, 0x73, 0x65, 0x72, 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, + 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x55, 0x73, 0x65, 0x72, 0x12, 0xcd, 0x01, 0x0a, + 0x0f, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, + 0x12, 0x32, 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, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 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, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x26, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x3a, 0x01, + 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x22, 0x12, 0x20, 0x41, 0x73, 0x73, 0x6f, + 0x63, 0x69, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x12, 0xb5, 0x02, 0x0a, + 0x0f, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, + 0x12, 0x32, 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, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 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, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb8, 0x01, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x26, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x3a, + 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x88, 0x01, 0x12, 0x85, 0x01, 0x53, + 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x20, + 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, + 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, + 0x61, 0x6e, 0x79, 0x20, 0x41, 0x63, 0x63, 0x6f, 0x75, 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, 0x2e, 0x12, 0x86, 0x02, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, + 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 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, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55, + 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x36, 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, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x80, 0x01, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x4e, 0x12, + 0x4c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, + 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x55, 0x73, 0x65, 0x72, 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 ( @@ -670,44 +1091,59 @@ func file_controller_api_services_v1_user_service_proto_rawDescGZIP() []byte { return file_controller_api_services_v1_user_service_proto_rawDescData } -var file_controller_api_services_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_controller_api_services_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_controller_api_services_v1_user_service_proto_goTypes = []interface{}{ - (*GetUserRequest)(nil), // 0: controller.api.services.v1.GetUserRequest - (*GetUserResponse)(nil), // 1: controller.api.services.v1.GetUserResponse - (*ListUsersRequest)(nil), // 2: controller.api.services.v1.ListUsersRequest - (*ListUsersResponse)(nil), // 3: controller.api.services.v1.ListUsersResponse - (*CreateUserRequest)(nil), // 4: controller.api.services.v1.CreateUserRequest - (*CreateUserResponse)(nil), // 5: controller.api.services.v1.CreateUserResponse - (*UpdateUserRequest)(nil), // 6: controller.api.services.v1.UpdateUserRequest - (*UpdateUserResponse)(nil), // 7: controller.api.services.v1.UpdateUserResponse - (*DeleteUserRequest)(nil), // 8: controller.api.services.v1.DeleteUserRequest - (*DeleteUserResponse)(nil), // 9: controller.api.services.v1.DeleteUserResponse - (*users.User)(nil), // 10: controller.api.resources.users.v1.User - (*field_mask.FieldMask)(nil), // 11: google.protobuf.FieldMask + (*GetUserRequest)(nil), // 0: controller.api.services.v1.GetUserRequest + (*GetUserResponse)(nil), // 1: controller.api.services.v1.GetUserResponse + (*ListUsersRequest)(nil), // 2: controller.api.services.v1.ListUsersRequest + (*ListUsersResponse)(nil), // 3: controller.api.services.v1.ListUsersResponse + (*CreateUserRequest)(nil), // 4: controller.api.services.v1.CreateUserRequest + (*CreateUserResponse)(nil), // 5: controller.api.services.v1.CreateUserResponse + (*UpdateUserRequest)(nil), // 6: controller.api.services.v1.UpdateUserRequest + (*UpdateUserResponse)(nil), // 7: controller.api.services.v1.UpdateUserResponse + (*DeleteUserRequest)(nil), // 8: controller.api.services.v1.DeleteUserRequest + (*DeleteUserResponse)(nil), // 9: controller.api.services.v1.DeleteUserResponse + (*AddUserAccountsRequest)(nil), // 10: controller.api.services.v1.AddUserAccountsRequest + (*AddUserAccountsResponse)(nil), // 11: controller.api.services.v1.AddUserAccountsResponse + (*SetUserAccountsRequest)(nil), // 12: controller.api.services.v1.SetUserAccountsRequest + (*SetUserAccountsResponse)(nil), // 13: controller.api.services.v1.SetUserAccountsResponse + (*RemoveUserAccountsRequest)(nil), // 14: controller.api.services.v1.RemoveUserAccountsRequest + (*RemoveUserAccountsResponse)(nil), // 15: controller.api.services.v1.RemoveUserAccountsResponse + (*users.User)(nil), // 16: controller.api.resources.users.v1.User + (*field_mask.FieldMask)(nil), // 17: google.protobuf.FieldMask } var file_controller_api_services_v1_user_service_proto_depIdxs = []int32{ - 10, // 0: controller.api.services.v1.GetUserResponse.item:type_name -> controller.api.resources.users.v1.User - 10, // 1: controller.api.services.v1.ListUsersResponse.items:type_name -> controller.api.resources.users.v1.User - 10, // 2: controller.api.services.v1.CreateUserRequest.item:type_name -> controller.api.resources.users.v1.User - 10, // 3: controller.api.services.v1.CreateUserResponse.item:type_name -> controller.api.resources.users.v1.User - 10, // 4: controller.api.services.v1.UpdateUserRequest.item:type_name -> controller.api.resources.users.v1.User - 11, // 5: controller.api.services.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask - 10, // 6: controller.api.services.v1.UpdateUserResponse.item:type_name -> controller.api.resources.users.v1.User - 0, // 7: controller.api.services.v1.UserService.GetUser:input_type -> controller.api.services.v1.GetUserRequest - 2, // 8: controller.api.services.v1.UserService.ListUsers:input_type -> controller.api.services.v1.ListUsersRequest - 4, // 9: controller.api.services.v1.UserService.CreateUser:input_type -> controller.api.services.v1.CreateUserRequest - 6, // 10: controller.api.services.v1.UserService.UpdateUser:input_type -> controller.api.services.v1.UpdateUserRequest - 8, // 11: controller.api.services.v1.UserService.DeleteUser:input_type -> controller.api.services.v1.DeleteUserRequest - 1, // 12: controller.api.services.v1.UserService.GetUser:output_type -> controller.api.services.v1.GetUserResponse - 3, // 13: controller.api.services.v1.UserService.ListUsers:output_type -> controller.api.services.v1.ListUsersResponse - 5, // 14: controller.api.services.v1.UserService.CreateUser:output_type -> controller.api.services.v1.CreateUserResponse - 7, // 15: controller.api.services.v1.UserService.UpdateUser:output_type -> controller.api.services.v1.UpdateUserResponse - 9, // 16: controller.api.services.v1.UserService.DeleteUser:output_type -> controller.api.services.v1.DeleteUserResponse - 12, // [12:17] is the sub-list for method output_type - 7, // [7:12] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 16, // 0: controller.api.services.v1.GetUserResponse.item:type_name -> controller.api.resources.users.v1.User + 16, // 1: controller.api.services.v1.ListUsersResponse.items:type_name -> controller.api.resources.users.v1.User + 16, // 2: controller.api.services.v1.CreateUserRequest.item:type_name -> controller.api.resources.users.v1.User + 16, // 3: controller.api.services.v1.CreateUserResponse.item:type_name -> controller.api.resources.users.v1.User + 16, // 4: controller.api.services.v1.UpdateUserRequest.item:type_name -> controller.api.resources.users.v1.User + 17, // 5: controller.api.services.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask + 16, // 6: controller.api.services.v1.UpdateUserResponse.item:type_name -> controller.api.resources.users.v1.User + 16, // 7: controller.api.services.v1.AddUserAccountsResponse.item:type_name -> controller.api.resources.users.v1.User + 16, // 8: controller.api.services.v1.SetUserAccountsResponse.item:type_name -> controller.api.resources.users.v1.User + 16, // 9: controller.api.services.v1.RemoveUserAccountsResponse.item:type_name -> controller.api.resources.users.v1.User + 0, // 10: controller.api.services.v1.UserService.GetUser:input_type -> controller.api.services.v1.GetUserRequest + 2, // 11: controller.api.services.v1.UserService.ListUsers:input_type -> controller.api.services.v1.ListUsersRequest + 4, // 12: controller.api.services.v1.UserService.CreateUser:input_type -> controller.api.services.v1.CreateUserRequest + 6, // 13: controller.api.services.v1.UserService.UpdateUser:input_type -> controller.api.services.v1.UpdateUserRequest + 8, // 14: controller.api.services.v1.UserService.DeleteUser:input_type -> controller.api.services.v1.DeleteUserRequest + 10, // 15: controller.api.services.v1.UserService.AddUserAccounts:input_type -> controller.api.services.v1.AddUserAccountsRequest + 12, // 16: controller.api.services.v1.UserService.SetUserAccounts:input_type -> controller.api.services.v1.SetUserAccountsRequest + 14, // 17: controller.api.services.v1.UserService.RemoveUserAccounts:input_type -> controller.api.services.v1.RemoveUserAccountsRequest + 1, // 18: controller.api.services.v1.UserService.GetUser:output_type -> controller.api.services.v1.GetUserResponse + 3, // 19: controller.api.services.v1.UserService.ListUsers:output_type -> controller.api.services.v1.ListUsersResponse + 5, // 20: controller.api.services.v1.UserService.CreateUser:output_type -> controller.api.services.v1.CreateUserResponse + 7, // 21: controller.api.services.v1.UserService.UpdateUser:output_type -> controller.api.services.v1.UpdateUserResponse + 9, // 22: controller.api.services.v1.UserService.DeleteUser:output_type -> controller.api.services.v1.DeleteUserResponse + 11, // 23: controller.api.services.v1.UserService.AddUserAccounts:output_type -> controller.api.services.v1.AddUserAccountsResponse + 13, // 24: controller.api.services.v1.UserService.SetUserAccounts:output_type -> controller.api.services.v1.SetUserAccountsResponse + 15, // 25: controller.api.services.v1.UserService.RemoveUserAccounts:output_type -> controller.api.services.v1.RemoveUserAccountsResponse + 18, // [18:26] is the sub-list for method output_type + 10, // [10:18] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name } func init() { file_controller_api_services_v1_user_service_proto_init() } @@ -836,6 +1272,78 @@ func file_controller_api_services_v1_user_service_proto_init() { return nil } } + file_controller_api_services_v1_user_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddUserAccountsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_user_service_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddUserAccountsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_user_service_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetUserAccountsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_user_service_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetUserAccountsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_user_service_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveUserAccountsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_user_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveUserAccountsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -843,7 +1351,7 @@ func file_controller_api_services_v1_user_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_api_services_v1_user_service_proto_rawDesc, NumEnums: 0, - NumMessages: 10, + NumMessages: 16, NumExtensions: 0, NumServices: 1, }, @@ -900,6 +1408,24 @@ type UserServiceClient interface { // specifies if the resource existed before the DeleteUser request was // received. DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*DeleteUserResponse, error) + // AddUserAccounts adds users as members to a group. The provided request + // must include the user id which the account will be added to. If the any + // of the ids are missing, malformed or references a non + // existing resource, an error is returned. If any of the accounts are + // associated with another user an error is returned. + AddUserAccounts(ctx context.Context, in *AddUserAccountsRequest, opts ...grpc.CallOption) (*AddUserAccountsResponse, error) + // SetUserAccounts sets the group's members. Any existing accounts are + // removed if they are not included in this request. The provided request + // must include the user ids which the account will be set to. If the user + // id is missing, malformed or references a non existing resource, an error + // is returned. If any of the accounts are associated with another + // user an error is returned. + SetUserAccounts(ctx context.Context, in *SetUserAccountsRequest, opts ...grpc.CallOption) (*SetUserAccountsResponse, error) + // RemoveUserAccounts removes accounts from the specified user. + // The provided request must include the user id which the accounts + // will be removed from. If the provided account id is not associated with the + // provided user, an error is returned. + RemoveUserAccounts(ctx context.Context, in *RemoveUserAccountsRequest, opts ...grpc.CallOption) (*RemoveUserAccountsResponse, error) } type userServiceClient struct { @@ -955,6 +1481,33 @@ func (c *userServiceClient) DeleteUser(ctx context.Context, in *DeleteUserReques return out, nil } +func (c *userServiceClient) AddUserAccounts(ctx context.Context, in *AddUserAccountsRequest, opts ...grpc.CallOption) (*AddUserAccountsResponse, error) { + out := new(AddUserAccountsResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.UserService/AddUserAccounts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) SetUserAccounts(ctx context.Context, in *SetUserAccountsRequest, opts ...grpc.CallOption) (*SetUserAccountsResponse, error) { + out := new(SetUserAccountsResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.UserService/SetUserAccounts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) RemoveUserAccounts(ctx context.Context, in *RemoveUserAccountsRequest, opts ...grpc.CallOption) (*RemoveUserAccountsResponse, error) { + out := new(RemoveUserAccountsResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.UserService/RemoveUserAccounts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // UserServiceServer is the server API for UserService service. type UserServiceServer interface { // GetUser returns a stored User if present. The provided request @@ -988,6 +1541,24 @@ type UserServiceServer interface { // specifies if the resource existed before the DeleteUser request was // received. DeleteUser(context.Context, *DeleteUserRequest) (*DeleteUserResponse, error) + // AddUserAccounts adds users as members to a group. The provided request + // must include the user id which the account will be added to. If the any + // of the ids are missing, malformed or references a non + // existing resource, an error is returned. If any of the accounts are + // associated with another user an error is returned. + AddUserAccounts(context.Context, *AddUserAccountsRequest) (*AddUserAccountsResponse, error) + // SetUserAccounts sets the group's members. Any existing accounts are + // removed if they are not included in this request. The provided request + // must include the user ids which the account will be set to. If the user + // id is missing, malformed or references a non existing resource, an error + // is returned. If any of the accounts are associated with another + // user an error is returned. + SetUserAccounts(context.Context, *SetUserAccountsRequest) (*SetUserAccountsResponse, error) + // RemoveUserAccounts removes accounts from the specified user. + // The provided request must include the user id which the accounts + // will be removed from. If the provided account id is not associated with the + // provided user, an error is returned. + RemoveUserAccounts(context.Context, *RemoveUserAccountsRequest) (*RemoveUserAccountsResponse, error) } // UnimplementedUserServiceServer can be embedded to have forward compatible implementations. @@ -1009,6 +1580,15 @@ func (*UnimplementedUserServiceServer) UpdateUser(context.Context, *UpdateUserRe func (*UnimplementedUserServiceServer) DeleteUser(context.Context, *DeleteUserRequest) (*DeleteUserResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteUser not implemented") } +func (*UnimplementedUserServiceServer) AddUserAccounts(context.Context, *AddUserAccountsRequest) (*AddUserAccountsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddUserAccounts not implemented") +} +func (*UnimplementedUserServiceServer) SetUserAccounts(context.Context, *SetUserAccountsRequest) (*SetUserAccountsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetUserAccounts not implemented") +} +func (*UnimplementedUserServiceServer) RemoveUserAccounts(context.Context, *RemoveUserAccountsRequest) (*RemoveUserAccountsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveUserAccounts not implemented") +} func RegisterUserServiceServer(s *grpc.Server, srv UserServiceServer) { s.RegisterService(&_UserService_serviceDesc, srv) @@ -1104,6 +1684,60 @@ func _UserService_DeleteUser_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } +func _UserService_AddUserAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddUserAccountsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).AddUserAccounts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.UserService/AddUserAccounts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).AddUserAccounts(ctx, req.(*AddUserAccountsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_SetUserAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetUserAccountsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).SetUserAccounts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.UserService/SetUserAccounts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).SetUserAccounts(ctx, req.(*SetUserAccountsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_RemoveUserAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveUserAccountsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).RemoveUserAccounts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.UserService/RemoveUserAccounts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).RemoveUserAccounts(ctx, req.(*RemoveUserAccountsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _UserService_serviceDesc = grpc.ServiceDesc{ ServiceName: "controller.api.services.v1.UserService", HandlerType: (*UserServiceServer)(nil), @@ -1128,6 +1762,18 @@ var _UserService_serviceDesc = grpc.ServiceDesc{ MethodName: "DeleteUser", Handler: _UserService_DeleteUser_Handler, }, + { + MethodName: "AddUserAccounts", + Handler: _UserService_AddUserAccounts_Handler, + }, + { + MethodName: "SetUserAccounts", + Handler: _UserService_SetUserAccounts_Handler, + }, + { + MethodName: "RemoveUserAccounts", + Handler: _UserService_RemoveUserAccounts_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "controller/api/services/v1/user_service.proto", diff --git a/internal/gen/controller/api/services/user_service.pb.gw.go b/internal/gen/controller/api/services/user_service.pb.gw.go index c30eb5728..db5843847 100644 --- a/internal/gen/controller/api/services/user_service.pb.gw.go +++ b/internal/gen/controller/api/services/user_service.pb.gw.go @@ -323,6 +323,210 @@ func local_request_UserService_DeleteUser_0(ctx context.Context, marshaler runti } +func request_UserService_AddUserAccounts_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AddUserAccountsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := client.AddUserAccounts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_UserService_AddUserAccounts_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AddUserAccountsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := server.AddUserAccounts(ctx, &protoReq) + return msg, metadata, err + +} + +func request_UserService_SetUserAccounts_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SetUserAccountsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := client.SetUserAccounts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_UserService_SetUserAccounts_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SetUserAccountsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := server.SetUserAccounts(ctx, &protoReq) + return msg, metadata, err + +} + +func request_UserService_RemoveUserAccounts_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq RemoveUserAccountsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := client.RemoveUserAccounts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_UserService_RemoveUserAccounts_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq RemoveUserAccountsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := server.RemoveUserAccounts(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterUserServiceHandlerServer registers the http handlers for service UserService to "mux". // UnaryRPC :call UserServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -444,6 +648,66 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("POST", pattern_UserService_AddUserAccounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.UserService/AddUserAccounts") + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_UserService_AddUserAccounts_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_UserService_AddUserAccounts_0(ctx, mux, outboundMarshaler, w, req, response_UserService_AddUserAccounts_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_UserService_SetUserAccounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.UserService/SetUserAccounts") + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_UserService_SetUserAccounts_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_UserService_SetUserAccounts_0(ctx, mux, outboundMarshaler, w, req, response_UserService_SetUserAccounts_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_UserService_RemoveUserAccounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.UserService/RemoveUserAccounts") + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_UserService_RemoveUserAccounts_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_UserService_RemoveUserAccounts_0(ctx, mux, outboundMarshaler, w, req, response_UserService_RemoveUserAccounts_0{resp}, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -585,6 +849,66 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("POST", pattern_UserService_AddUserAccounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.UserService/AddUserAccounts") + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_UserService_AddUserAccounts_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_UserService_AddUserAccounts_0(ctx, mux, outboundMarshaler, w, req, response_UserService_AddUserAccounts_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_UserService_SetUserAccounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.UserService/SetUserAccounts") + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_UserService_SetUserAccounts_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_UserService_SetUserAccounts_0(ctx, mux, outboundMarshaler, w, req, response_UserService_SetUserAccounts_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_UserService_RemoveUserAccounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.UserService/RemoveUserAccounts") + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_UserService_RemoveUserAccounts_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_UserService_RemoveUserAccounts_0(ctx, mux, outboundMarshaler, w, req, response_UserService_RemoveUserAccounts_0{resp}, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -615,6 +939,33 @@ func (m response_UserService_UpdateUser_0) XXX_ResponseBody() interface{} { return response.Item } +type response_UserService_AddUserAccounts_0 struct { + proto.Message +} + +func (m response_UserService_AddUserAccounts_0) XXX_ResponseBody() interface{} { + response := m.Message.(*AddUserAccountsResponse) + return response.Item +} + +type response_UserService_SetUserAccounts_0 struct { + proto.Message +} + +func (m response_UserService_SetUserAccounts_0) XXX_ResponseBody() interface{} { + response := m.Message.(*SetUserAccountsResponse) + return response.Item +} + +type response_UserService_RemoveUserAccounts_0 struct { + proto.Message +} + +func (m response_UserService_RemoveUserAccounts_0) XXX_ResponseBody() interface{} { + response := m.Message.(*RemoveUserAccountsResponse) + return response.Item +} + var ( pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "users", "id"}, "")) @@ -625,6 +976,12 @@ var ( pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "users", "id"}, "")) pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "users", "id"}, "")) + + pattern_UserService_AddUserAccounts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "users", "id"}, "add-accounts")) + + pattern_UserService_SetUserAccounts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "users", "id"}, "set-accounts")) + + pattern_UserService_RemoveUserAccounts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "users", "id"}, "remove-accounts")) ) var ( @@ -637,4 +994,10 @@ var ( forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage + + forward_UserService_AddUserAccounts_0 = runtime.ForwardResponseMessage + + forward_UserService_SetUserAccounts_0 = runtime.ForwardResponseMessage + + forward_UserService_RemoveUserAccounts_0 = runtime.ForwardResponseMessage ) diff --git a/internal/iam/repository_user.go b/internal/iam/repository_user.go index b2bdeea58..8dfa2710e 100644 --- a/internal/iam/repository_user.go +++ b/internal/iam/repository_user.go @@ -708,8 +708,8 @@ func dissociateUserFromAccounts(ctx context.Context, repoKms *kms.Kms, reader db if err != nil { return fmt.Errorf("dissociate user from accounts: unable to lookup account %s: %w", accountId, err) } - if acct.IamUserId != "" && acct.IamUserId != userId { - return fmt.Errorf("dissociate user from accounts: %s account is associated with a user %s: %w", accountId, acct.IamUserId, db.ErrInvalidParameter) + if acct.IamUserId != userId { + return fmt.Errorf("dissociate user from accounts: %s account is not associated with user %s: %w", accountId, userId, db.ErrInvalidParameter) } authAccounts = append(authAccounts, &acct) } diff --git a/internal/iam/repository_user_test.go b/internal/iam/repository_user_test.go index 61d208f35..3af64a8a8 100644 --- a/internal/iam/repository_user_test.go +++ b/internal/iam/repository_user_test.go @@ -870,7 +870,7 @@ func TestRepository_dissociateUserWithAccount(t *testing.T) { Ids: func() Ids { u := TestUser(t, repo, org.PublicId) a := testAccount(t, conn, org.PublicId, authMethodId, u.PublicId) - a2 := testAccount(t, conn, org.PublicId, authMethodId, "") + a2 := testAccount(t, conn, org.PublicId, authMethodId, u.PublicId) return Ids{user: u.PublicId, accts: []string{a.PublicId, a2.PublicId}} }(), }, @@ -907,6 +907,8 @@ func TestRepository_dissociateUserWithAccount(t *testing.T) { return Ids{user: u.PublicId, accts: []string{a.PublicId}} }(), }, + wantErr: true, + wantErrIs: db.ErrInvalidParameter, }, { name: "assoc-with-diff-user", @@ -942,7 +944,8 @@ func TestRepository_dissociateUserWithAccount(t *testing.T) { return Ids{user: id, accts: []string{a.PublicId}} }(), }, - wantErr: false, + wantErr: true, + wantErrIs: db.ErrInvalidParameter, }, { name: "bad-user-id", diff --git a/internal/proto/local/controller/api/resources/users/v1/user.proto b/internal/proto/local/controller/api/resources/users/v1/user.proto index 9ce229219..9d102c24e 100644 --- a/internal/proto/local/controller/api/resources/users/v1/user.proto +++ b/internal/proto/local/controller/api/resources/users/v1/user.proto @@ -9,6 +9,16 @@ import "google/protobuf/wrappers.proto"; import "controller/api/resources/scopes/v1/scope.proto"; import "controller/custom_options/v1/options.proto"; +message Account { + // The ID of the account. + // Output only. + string id = 10; + + // The scope ID of the account. + // Output only. + string scope_id = 20 [json_name="scope_id"]; +} + // User contains all fields related to a User resource message User { // The ID of the Project @@ -39,4 +49,12 @@ message User { // The version can be used in subsequent write requests to ensure this // resource has not changed and to fail the write if it has. uint32 version = 80; + + // Contains the list of account ids for this user. + // Output only. + repeated string account_ids = 90; + + // The acounts for this user. + // Output only. + repeated Account accounts = 100; } diff --git a/internal/proto/local/controller/api/services/v1/user_service.proto b/internal/proto/local/controller/api/services/v1/user_service.proto index a483041d2..223e4c225 100644 --- a/internal/proto/local/controller/api/services/v1/user_service.proto +++ b/internal/proto/local/controller/api/services/v1/user_service.proto @@ -86,6 +86,54 @@ service UserService { summary: "Deletes a User" }; } + + // AddUserAccounts adds users as members to a group. The provided request + // must include the user id which the account will be added to. If the any + // of the ids are missing, malformed or references a non + // existing resource, an error is returned. If any of the accounts are + // associated with another user an error is returned. + rpc AddUserAccounts(AddUserAccountsRequest) returns (AddUserAccountsResponse) { + option (google.api.http) = { + post: "/v1/users/{id}:add-accounts" + body: "*" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Associates an Account to a User." + }; + } + + // SetUserAccounts sets the group's members. Any existing accounts are + // removed if they are not included in this request. The provided request + // must include the user ids which the account will be set to. If the user + // id is missing, malformed or references a non existing resource, an error + // is returned. If any of the accounts are associated with another + // user an error is returned. + rpc SetUserAccounts(SetUserAccountsRequest) returns (SetUserAccountsResponse) { + option (google.api.http) = { + post: "/v1/users/{id}:set-accounts" + body: "*" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Set the Accounts associated to the User to exactly the list of provided in the request, removing any Accounts that are not specified." + }; + } + + // RemoveUserAccounts removes accounts from the specified user. + // The provided request must include the user id which the accounts + // will be removed from. If the provided account id is not associated with the + // provided user, an error is returned. + rpc RemoveUserAccounts(RemoveUserAccountsRequest) returns (RemoveUserAccountsResponse) { + option (google.api.http) = { + post: "/v1/users/{id}:remove-accounts" + body: "*" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Removes the specified Accounts from being associated with the provided User." + }; + } } message GetUserRequest { @@ -130,3 +178,39 @@ message DeleteUserRequest { } message DeleteUserResponse {} + +message AddUserAccountsRequest { + string id = 1; + // The version ensures the user hasn't changed since it was last retrieved and if + // it has the request will fail. + uint32 version = 2; + repeated string account_ids = 3 [json_name="account_ids"]; +} + +message AddUserAccountsResponse { + resources.users.v1.User item = 1; +} + +message SetUserAccountsRequest { + string id = 1; + // The version ensures the user hasn't changed since it was last retrieved and if + // it has the request will fail. + uint32 version = 2; + repeated string account_ids = 3 [json_name="account_ids"]; +} + +message SetUserAccountsResponse { + resources.users.v1.User item = 1; +} + +message RemoveUserAccountsRequest { + string id = 1; + // The version ensures the user hasn't changed since it was last retrieved and if + // it has the request will fail. + uint32 version = 2; + repeated string account_ids = 3 [json_name="account_ids"]; +} + +message RemoveUserAccountsResponse { + resources.users.v1.User item = 1; +} diff --git a/internal/servers/controller/handlers/users/user_service.go b/internal/servers/controller/handlers/users/user_service.go index ca83a0ab9..bf2369e30 100644 --- a/internal/servers/controller/handlers/users/user_service.go +++ b/internal/servers/controller/handlers/users/user_service.go @@ -132,12 +132,63 @@ func (s Service) DeleteUser(ctx context.Context, req *pbs.DeleteUserRequest) (*p return nil, nil } +// AddUserAccounts implements the interface pbs.GroupServiceServer. +func (s Service) AddUserAccounts(ctx context.Context, req *pbs.AddUserAccountsRequest) (*pbs.AddUserAccountsResponse, error) { + if err := validateAddUserAccountsRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.AddAccounts) + if authResults.Error != nil { + return nil, authResults.Error + } + u, err := s.addInRepo(ctx, req.GetId(), req.GetAccountIds(), req.GetVersion()) + if err != nil { + return nil, err + } + u.Scope = authResults.Scope + return &pbs.AddUserAccountsResponse{Item: u}, nil +} + +// SetUserAccounts implements the interface pbs.GroupServiceServer. +func (s Service) SetUserAccounts(ctx context.Context, req *pbs.SetUserAccountsRequest) (*pbs.SetUserAccountsResponse, error) { + if err := validateSetUserAccountsRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.SetAccounts) + if authResults.Error != nil { + return nil, authResults.Error + } + u, err := s.setInRepo(ctx, req.GetId(), req.GetAccountIds(), req.GetVersion()) + if err != nil { + return nil, err + } + u.Scope = authResults.Scope + return &pbs.SetUserAccountsResponse{Item: u}, nil +} + +// RemoveUserAccounts implements the interface pbs.GroupServiceServer. +func (s Service) RemoveUserAccounts(ctx context.Context, req *pbs.RemoveUserAccountsRequest) (*pbs.RemoveUserAccountsResponse, error) { + if err := validateRemoveUserAccountsRequest(req); err != nil { + return nil, err + } + authResults := s.authResult(ctx, req.GetId(), action.RemoveAccounts) + if authResults.Error != nil { + return nil, authResults.Error + } + u, err := s.removeInRepo(ctx, req.GetId(), req.GetAccountIds(), req.GetVersion()) + if err != nil { + return nil, err + } + u.Scope = authResults.Scope + return &pbs.RemoveUserAccountsResponse{Item: u}, nil +} + func (s Service) getFromRepo(ctx context.Context, id string) (*pb.User, error) { repo, err := s.repoFn() if err != nil { return nil, err } - u, _, err := repo.LookupUser(ctx, id) + u, accts, err := repo.LookupUser(ctx, id) if err != nil { if errors.Is(err, db.ErrRecordNotFound) { return nil, handlers.NotFoundErrorf("User %q doesn't exist.", id) @@ -147,7 +198,7 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.User, error) { if u == nil { return nil, handlers.NotFoundErrorf("User %q doesn't exist.", id) } - return toProto(u), nil + return toProto(u, accts), nil } func (s Service) createInRepo(ctx context.Context, orgId string, item *pb.User) (*pb.User, error) { @@ -173,7 +224,7 @@ func (s Service) createInRepo(ctx context.Context, orgId string, item *pb.User) if out == nil { return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create user but no error returned from repository.") } - return toProto(out), nil + return toProto(out, nil), nil } func (s Service) updateInRepo(ctx context.Context, orgId, id string, mask []string, item *pb.User) (*pb.User, error) { @@ -198,14 +249,14 @@ func (s Service) updateInRepo(ctx context.Context, orgId, id string, mask []stri if err != nil { return nil, err } - out, _, rowsUpdated, err := repo.UpdateUser(ctx, u, version, dbMask) + out, accts, rowsUpdated, err := repo.UpdateUser(ctx, u, version, dbMask) if err != nil { return nil, fmt.Errorf("unable to update user: %w", err) } if rowsUpdated == 0 { return nil, handlers.NotFoundErrorf("User %q doesn't exist or incorrect version provided.", id) } - return toProto(out), nil + return toProto(out, accts), nil } func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) { @@ -234,11 +285,68 @@ func (s Service) listFromRepo(ctx context.Context, orgId string) ([]*pb.User, er } var outUl []*pb.User for _, u := range ul { - outUl = append(outUl, toProto(u)) + outUl = append(outUl, toProto(u, nil)) } return outUl, nil } +func (s Service) addInRepo(ctx context.Context, userId string, accountIds []string, version uint32) (*pb.User, error) { + repo, err := s.repoFn() + if err != nil { + return nil, err + } + _, err = repo.AssociateAccounts(ctx, userId, version, accountIds) + if err != nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add accounts to user: %v.", err) + } + out, accts, err := repo.LookupUser(ctx, userId) + if err != nil { + return nil, fmt.Errorf("unable to look up user after adding accounts: %w", err) + } + if out == nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup user after adding accounts to it.") + } + return toProto(out, accts), nil +} + +func (s Service) setInRepo(ctx context.Context, userId string, accountIds []string, version uint32) (*pb.User, error) { + repo, err := s.repoFn() + if err != nil { + return nil, err + } + _, err = repo.SetAssociatedAccounts(ctx, userId, version, accountIds) + if err != nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set accounts for the user: %v.", err) + } + out, accts, err := repo.LookupUser(ctx, userId) + if err != nil { + return nil, fmt.Errorf("unable to look up user after setting accounts: %w", err) + } + if out == nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup user after setting accounts for it.") + } + return toProto(out, accts), nil +} + +func (s Service) removeInRepo(ctx context.Context, userId string, accountIds []string, version uint32) (*pb.User, error) { + repo, err := s.repoFn() + if err != nil { + return nil, err + } + _, err = repo.DisassociateAccounts(ctx, userId, version, accountIds) + if err != nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove accounts from user: %v.", err) + } + out, accts, err := repo.LookupUser(ctx, userId) + if err != nil { + return nil, fmt.Errorf("unable to look up user after removing accounts: %w", err) + } + if out == nil { + return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup user after removing accounts from it.") + } + return toProto(out, accts), nil +} + func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults { res := auth.VerifyResults{} repo, err := s.repoFn() @@ -278,13 +386,14 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth. return auth.Verify(ctx, opts...) } -func toProto(in *iam.User) *pb.User { +func toProto(in *iam.User, accts []string) *pb.User { out := pb.User{ Id: in.GetPublicId(), ScopeId: in.GetScopeId(), CreatedTime: in.GetCreateTime().GetTimestamp(), UpdatedTime: in.GetUpdateTime().GetTimestamp(), Version: in.GetVersion(), + AccountIds: accts, } if in.GetDescription() != "" { out.Description = &wrapperspb.StringValue{Value: in.GetDescription()} @@ -292,6 +401,13 @@ func toProto(in *iam.User) *pb.User { if in.GetName() != "" { out.Name = &wrapperspb.StringValue{Value: in.GetName()} } + for _, a := range accts { + out.Accounts = append(out.Accounts, &pb.Account{ + Id: a, + // TODO: Update this when an account can be associated with a user from a different scope. + ScopeId: in.GetScopeId(), + }) + } return &out } @@ -334,3 +450,51 @@ func validateListRequest(req *pbs.ListUsersRequest) error { } return nil } + +func validateAddUserAccountsRequest(req *pbs.AddUserAccountsRequest) error { + badFields := map[string]string{} + if !handlers.ValidId(iam.UserPrefix, req.GetId()) { + badFields["id"] = "Incorrectly formatted identifier." + } + if req.GetVersion() == 0 { + badFields["version"] = "Required field." + } + if len(req.GetAccountIds()) == 0 { + badFields["account_ids"] = "Must be non-empty." + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) + } + return nil +} + +func validateSetUserAccountsRequest(req *pbs.SetUserAccountsRequest) error { + badFields := map[string]string{} + if !handlers.ValidId(iam.UserPrefix, req.GetId()) { + badFields["id"] = "Incorrectly formatted identifier." + } + if req.GetVersion() == 0 { + badFields["version"] = "Required field." + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) + } + return nil +} + +func validateRemoveUserAccountsRequest(req *pbs.RemoveUserAccountsRequest) error { + badFields := map[string]string{} + if !handlers.ValidId(iam.UserPrefix, req.GetId()) { + badFields["id"] = "Incorrectly formatted identifier." + } + if req.GetVersion() == 0 { + badFields["version"] = "Required field." + } + if len(req.GetAccountIds()) == 0 { + badFields["account_ids"] = "Must be non-empty." + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) + } + return nil +} diff --git a/internal/servers/controller/handlers/users/user_service_test.go b/internal/servers/controller/handlers/users/user_service_test.go index 64ab80e9e..3be404ceb 100644 --- a/internal/servers/controller/handlers/users/user_service_test.go +++ b/internal/servers/controller/handlers/users/user_service_test.go @@ -10,6 +10,7 @@ import ( "github.com/golang/protobuf/ptypes" "github.com/google/go-cmp/cmp" "github.com/hashicorp/boundary/internal/auth" + "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes" pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/users" @@ -42,7 +43,6 @@ func createDefaultUserAndRepo(t *testing.T) (*iam.User, func() (*iam.Repository, } func TestGet(t *testing.T) { - assert, require := assert.New(t), require.New(t) u, repoFn := createDefaultUserAndRepo(t) toMerge := &pbs.GetUserRequest{ Id: u.GetPublicId(), @@ -91,6 +91,7 @@ func TestGet(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.GetUserRequest) proto.Merge(req, tc.req) @@ -108,7 +109,6 @@ func TestGet(t *testing.T) { } func TestList(t *testing.T) { - assert, require := assert.New(t), require.New(t) conn, _ := db.TestSetup(t, "postgres") wrap := db.TestWrapper(t) iamRepo := iam.TestRepo(t, conn, wrap) @@ -116,7 +116,7 @@ func TestList(t *testing.T) { return iamRepo, nil } repo, err := repoFn() - require.NoError(err) + require.NoError(t, err) oNoUsers, _ := iam.TestScopes(t, repo) oWithUsers, _ := iam.TestScopes(t, repo) @@ -124,9 +124,9 @@ func TestList(t *testing.T) { var wantUsers []*pb.User for i := 0; i < 10; i++ { newU, err := iam.NewUser(oWithUsers.GetPublicId()) - require.NoError(err) + require.NoError(t, err) u, err := repo.CreateUser(context.Background(), newU) - require.NoError(err) + require.NoError(t, err) wantUsers = append(wantUsers, &pb.User{ Id: u.GetPublicId(), ScopeId: u.GetScopeId(), @@ -156,6 +156,7 @@ func TestList(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) s, err := users.NewService(repoFn) require.NoError(err, "Couldn't create new user service.") @@ -604,3 +605,321 @@ func TestUpdate(t *testing.T) { }) } } + +func TestAddAccount(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrap := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, wrap) + repoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + s, err := users.NewService(repoFn) + require.NoError(t, err, "Error when getting new user service.") + + o, _ := iam.TestScopes(t, iamRepo) + amId := password.TestAuthMethods(t, conn, o.GetPublicId(), 1)[0].GetPublicId() + accts := password.TestAccounts(t, conn, amId, 3) + + addCases := []struct { + name string + setup func(*iam.User) + addAccounts []string + resultAccounts []string + wantErr bool + }{ + { + name: "Add account on empty user", + setup: func(u *iam.User) {}, + addAccounts: []string{accts[1].GetPublicId()}, + resultAccounts: []string{accts[1].GetPublicId()}, + }, + { + name: "Add account on populated user", + setup: func(u *iam.User) { + _, err := iamRepo.SetAssociatedAccounts(context.Background(), u.GetPublicId(), u.GetVersion(), + []string{accts[0].GetPublicId()}) + require.NoError(t, err) + u.Version = u.Version + 1 + }, + addAccounts: []string{accts[1].GetPublicId()}, + resultAccounts: []string{accts[0].GetPublicId(), accts[1].GetPublicId()}, + }, + { + name: "Add empty on populated user", + setup: func(u *iam.User) { + iamRepo.SetAssociatedAccounts(context.Background(), u.GetPublicId(), u.GetVersion(), + []string{accts[0].GetPublicId(), accts[1].GetPublicId()}) + require.NoError(t, err) + u.Version = u.Version + 1 + }, + wantErr: true, + }, + } + + for _, tc := range addCases { + t.Run(tc.name, func(t *testing.T) { + usr := iam.TestUser(t, iamRepo, o.GetPublicId()) + defer func() { + _, err := iamRepo.DeleteUser(context.Background(), usr.GetPublicId()) + require.NoError(t, err) + }() + tc.setup(usr) + req := &pbs.AddUserAccountsRequest{ + Id: usr.GetPublicId(), + Version: usr.GetVersion(), + AccountIds: tc.addAccounts, + } + + got, err := s.AddUserAccounts(auth.DisabledAuthTestContext(auth.WithScopeId(o.GetPublicId())), req) + if tc.wantErr { + assert.Error(t, err) + return + } + require.NoError(t, err, "Got error: %v", err) + assert.ElementsMatch(t, got.GetItem().GetAccountIds(), tc.resultAccounts) + }) + } + + usr := iam.TestUser(t, iamRepo, o.GetPublicId()) + + failCases := []struct { + name string + req *pbs.AddUserAccountsRequest + err error + }{ + { + name: "Bad user Id", + req: &pbs.AddUserAccountsRequest{ + Id: "bad id", + Version: usr.GetVersion(), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range failCases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + _, gErr := s.AddUserAccounts(auth.DisabledAuthTestContext(auth.WithScopeId(usr.GetScopeId())), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "AddUserAccounts(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) + } + }) + } +} + +func TestSetAccount(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrap := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, wrap) + repoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + s, err := users.NewService(repoFn) + require.NoError(t, err, "Error when getting new user service.") + + o, _ := iam.TestScopes(t, iamRepo) + amId := password.TestAuthMethods(t, conn, o.GetPublicId(), 1)[0].GetPublicId() + accts := password.TestAccounts(t, conn, amId, 3) + + setCases := []struct { + name string + setup func(*iam.User) + setAccounts []string + resultAccounts []string + wantErr bool + }{ + { + name: "Set account on empty user", + setup: func(u *iam.User) {}, + setAccounts: []string{accts[1].GetPublicId()}, + resultAccounts: []string{accts[1].GetPublicId()}, + }, + { + name: "Set account on populated user", + setup: func(u *iam.User) { + iamRepo.AssociateAccounts(context.Background(), u.GetPublicId(), u.GetVersion(), + []string{accts[0].GetPublicId()}) + require.NoError(t, err) + u.Version = u.Version + 1 + }, + setAccounts: []string{accts[1].GetPublicId()}, + resultAccounts: []string{accts[1].GetPublicId()}, + }, + { + name: "Set empty on populated user", + setup: func(u *iam.User) { + iamRepo.AssociateAccounts(context.Background(), u.GetPublicId(), u.GetVersion(), + []string{accts[0].GetPublicId(), accts[1].GetPublicId()}) + require.NoError(t, err) + u.Version = u.Version + 1 + }, + setAccounts: []string{}, + resultAccounts: nil, + }, + } + + for _, tc := range setCases { + t.Run(tc.name, func(t *testing.T) { + usr := iam.TestUser(t, iamRepo, o.GetPublicId()) + defer func() { + iamRepo.DeleteUser(context.Background(), usr.GetPublicId()) + }() + + tc.setup(usr) + req := &pbs.SetUserAccountsRequest{ + Id: usr.GetPublicId(), + Version: usr.GetVersion(), + AccountIds: tc.setAccounts, + } + + got, err := s.SetUserAccounts(auth.DisabledAuthTestContext(auth.WithScopeId(o.GetPublicId())), req) + if tc.wantErr { + assert.Error(t, err) + return + } + require.NoError(t, err, "Got error: %v", err) + assert.ElementsMatch(t, got.GetItem().GetAccountIds(), tc.resultAccounts) + }) + } + + usr := iam.TestUser(t, iamRepo, o.GetPublicId()) + + failCases := []struct { + name string + req *pbs.SetUserAccountsRequest + err error + }{ + { + name: "Bad User Id", + req: &pbs.SetUserAccountsRequest{ + Id: "bad id", + Version: usr.GetVersion(), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range failCases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + _, gErr := s.SetUserAccounts(auth.DisabledAuthTestContext(auth.WithScopeId(usr.GetScopeId())), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "SetUserAccounts(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) + } + }) + } +} + +func TestRemoveAccount(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrap := db.TestWrapper(t) + iamRepo := iam.TestRepo(t, conn, wrap) + repoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + s, err := users.NewService(repoFn) + require.NoError(t, err, "Error when getting new user service.") + + o, _ := iam.TestScopes(t, iamRepo) + amId := password.TestAuthMethods(t, conn, o.GetPublicId(), 1)[0].GetPublicId() + accts := password.TestAccounts(t, conn, amId, 3) + + addCases := []struct { + name string + setup func(*iam.User) + removeAccounts []string + resultAccounts []string + wantErr bool + }{ + { + name: "Remove account on empty user", + setup: func(*iam.User) {}, + removeAccounts: []string{accts[1].GetPublicId()}, + wantErr: true, + }, + { + name: "Remove 1 of 2 accounts from user", + setup: func(u *iam.User) { + _, err := iamRepo.SetAssociatedAccounts(context.Background(), u.GetPublicId(), u.GetVersion(), + []string{accts[0].GetPublicId(), accts[1].GetPublicId()}) + require.NoError(t, err) + u.Version = u.Version + 1 + }, + removeAccounts: []string{accts[1].GetPublicId()}, + resultAccounts: []string{accts[0].GetPublicId()}, + }, + { + name: "Remove all accounts from user", + setup: func(u *iam.User) { + _, err := iamRepo.SetAssociatedAccounts(context.Background(), u.GetPublicId(), u.GetVersion(), + []string{accts[0].GetPublicId(), accts[1].GetPublicId()}) + require.NoError(t, err) + u.Version = u.Version + 1 + }, + removeAccounts: []string{accts[0].GetPublicId(), accts[1].GetPublicId()}, + resultAccounts: []string{}, + }, + { + name: "Remove empty on populated user", + setup: func(u *iam.User) { + _, err := iamRepo.SetAssociatedAccounts(context.Background(), u.GetPublicId(), u.GetVersion(), + []string{accts[0].GetPublicId()}) + require.NoError(t, err) + u.Version = u.Version + 1 + }, + wantErr: true, + }, + } + + for _, tc := range addCases { + t.Run(tc.name, func(t *testing.T) { + usr := iam.TestUser(t, iamRepo, o.GetPublicId()) + defer func() { + iamRepo.DeleteUser(context.Background(), usr.GetPublicId()) + }() + tc.setup(usr) + req := &pbs.RemoveUserAccountsRequest{ + Id: usr.GetPublicId(), + Version: usr.GetVersion(), + AccountIds: tc.removeAccounts, + } + + got, err := s.RemoveUserAccounts(auth.DisabledAuthTestContext(auth.WithScopeId(o.GetPublicId())), req) + if tc.wantErr { + assert.Error(t, err) + return + } + require.NoError(t, err, "Got error: %v", err) + + assert.ElementsMatch(t, got.GetItem().GetAccountIds(), tc.resultAccounts) + }) + } + + usr := iam.TestUser(t, iamRepo, o.GetPublicId()) + + failCases := []struct { + name string + req *pbs.AddUserAccountsRequest + err error + }{ + { + name: "Bad User Id", + req: &pbs.AddUserAccountsRequest{ + Id: "bad id", + Version: usr.GetVersion(), + }, + err: handlers.ApiErrorWithCode(codes.InvalidArgument), + }, + } + for _, tc := range failCases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + _, gErr := s.AddUserAccounts(auth.DisabledAuthTestContext(auth.WithScopeId(usr.GetScopeId())), tc.req) + if tc.err != nil { + require.Error(gErr) + assert.True(errors.Is(gErr, tc.err), "AddUserAccounts(%+v) got error %v, wanted %v", tc.req, gErr, tc.err) + } + }) + } +} diff --git a/internal/types/action/action.go b/internal/types/action/action.go index 5531ec31d..421021e50 100644 --- a/internal/types/action/action.go +++ b/internal/types/action/action.go @@ -34,6 +34,9 @@ const ( SetHostSets Type = 25 RemoveHostSets Type = 26 Cancel Type = 27 + AddAccounts Type = 28 + SetAccounts Type = 29 + RemoveAccounts Type = 30 ) var Map = map[string]Type{ @@ -64,6 +67,9 @@ var Map = map[string]Type{ SetHostSets.String(): SetHostSets, RemoveHostSets.String(): RemoveHostSets, Cancel.String(): Cancel, + AddAccounts.String(): AddAccounts, + SetAccounts.String(): SetAccounts, + RemoveAccounts.String(): RemoveAccounts, } func (a Type) String() string { @@ -96,5 +102,8 @@ func (a Type) String() string { "set-host-sets", "remove-host-sets", "cancel", + "add-accounts", + "set-accounts", + "remove-accounts", }[a] }