From c3ecea172db5baab9775a61c2f9ecbb9601575c4 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Tue, 8 Sep 2020 13:27:51 -0700 Subject: [PATCH] Generate new version of SDK resources and Add Tests (#331) --- api/accounts/account.gen.go | 52 +- api/accounts/account_test.go | 83 ++- api/accounts/changepassword.go | 15 +- api/accounts/option.gen.go | 14 +- api/accounts/setpassword.go | 9 +- api/authmethods/authenticate.go | 9 +- api/authmethods/authenticate_test.go | 11 +- api/authmethods/authmethods.gen.go | 32 +- api/authmethods/option.gen.go | 14 +- api/authtokens/authtoken_test.go | 70 ++- api/authtokens/authtokens.gen.go | 22 +- api/authtokens/option.gen.go | 14 +- api/client.go | 77 +-- api/client_test.go | 2 +- api/groups/group.gen.go | 32 +- api/groups/group_test.go | 109 ++-- api/groups/option.gen.go | 14 +- api/hostcatalogs/host_catalog.gen.go | 32 +- api/hostcatalogs/host_catalog_test.go | 65 +-- api/hostcatalogs/option.gen.go | 14 +- api/hosts/host.gen.go | 52 +- api/hosts/host_test.go | 69 +-- api/hosts/option.gen.go | 14 +- api/hostsets/host_set.gen.go | 470 +--------------- api/hostsets/host_set_test.go | 389 ++++++-------- api/hostsets/option.gen.go | 14 +- api/internal/genapi/input.go | 39 +- api/internal/genapi/templates.go | 503 +++--------------- api/options.go | 18 +- api/roles/option.gen.go | 14 +- api/roles/role.gen.go | 32 +- api/roles/role_test.go | 156 +++--- api/scopes/option.gen.go | 14 +- api/scopes/project_test.go | 45 +- api/scopes/scope.gen.go | 23 +- api/targets/option.gen.go | 14 +- api/targets/target.gen.go | 22 +- api/targets/target_test.go | 12 +- api/users/option.gen.go | 14 +- api/users/user.gen.go | 32 +- api/users/user_test.go | 61 +-- internal/auth/auth.go | 250 +-------- internal/auth/auth_test.go | 252 --------- .../auth/password/repository_password_test.go | 8 +- internal/cmd/base/base.go | 38 +- internal/cmd/base/const.go | 3 - internal/cmd/base/format.go | 2 +- internal/cmd/commands.go | 174 +++++- internal/cmd/commands/accounts/account.go | 375 +++++++++++++ internal/cmd/commands/accounts/funcs.go | 74 +++ internal/cmd/commands/accounts/password.go | 242 +++++++++ .../cmd/commands/authenticate/password.go | 10 +- .../cmd/commands/authmethods/authmethod.go | 15 +- internal/cmd/commands/authmethods/funcs.go | 1 + internal/cmd/commands/authmethods/password.go | 10 +- .../cmd/commands/authtokens/authtokens.go | 7 +- internal/cmd/commands/groups/funcs.go | 1 + internal/cmd/commands/groups/group.go | 13 +- internal/cmd/commands/hostcatalogs/funcs.go | 11 +- .../cmd/commands/hostcatalogs/hostcatalog.go | 17 +- internal/cmd/commands/hostcatalogs/static.go | 10 +- internal/cmd/commands/hosts/funcs.go | 75 +++ internal/cmd/commands/hosts/host.go | 278 ++++++++++ internal/cmd/commands/hosts/host_create.go | 131 ----- internal/cmd/commands/hosts/static.go | 216 ++++++++ internal/cmd/commands/hostsets/funcs.go | 13 +- internal/cmd/commands/hostsets/hostset.go | 44 +- internal/cmd/commands/hostsets/static.go | 24 +- internal/cmd/commands/roles/funcs.go | 1 + internal/cmd/commands/roles/role.go | 14 +- internal/cmd/commands/scopes/funcs.go | 1 + internal/cmd/commands/scopes/scope.go | 13 +- internal/cmd/commands/targets/funcs.go | 101 ++++ internal/cmd/commands/targets/target.go | 360 +++++++++++++ internal/cmd/commands/targets/tcp.go | 176 ++++++ internal/cmd/commands/users/funcs.go | 1 + internal/cmd/commands/users/user.go | 13 +- internal/cmd/common/flags.go | 9 + internal/gen/controller.swagger.json | 38 +- .../api/services/account_service.pb.go | 269 +++++----- .../api/services/authenticate_service.pb.go | 38 +- .../services/authenticate_service.pb.gw.go | 123 ++++- .../static/repository_host_set_member_test.go | 10 +- .../host/static/repository_host_set_test.go | 4 +- internal/iam/testing.go | 8 +- .../api/services/v1/account_service.proto | 2 +- .../services/v1/authenticate_service.proto | 7 +- .../handlers/accounts/account_service.go | 10 +- .../handlers/accounts/account_service_test.go | 24 +- .../authenticate/authenticate_service.go | 2 +- internal/servers/controller/multi_test.go | 6 +- internal/servers/controller/testing.go | 6 +- internal/servers/repository_test.go | 13 +- 93 files changed, 3414 insertions(+), 2826 deletions(-) create mode 100644 internal/cmd/commands/accounts/account.go create mode 100644 internal/cmd/commands/accounts/funcs.go create mode 100644 internal/cmd/commands/accounts/password.go create mode 100644 internal/cmd/commands/hosts/funcs.go create mode 100644 internal/cmd/commands/hosts/host.go delete mode 100644 internal/cmd/commands/hosts/host_create.go create mode 100644 internal/cmd/commands/hosts/static.go create mode 100644 internal/cmd/commands/targets/funcs.go create mode 100644 internal/cmd/commands/targets/target.go create mode 100644 internal/cmd/commands/targets/tcp.go diff --git a/api/accounts/account.gen.go b/api/accounts/account.gen.go index c7dda1a12..073fcb2e8 100644 --- a/api/accounts/account.gen.go +++ b/api/accounts/account.gen.go @@ -27,27 +27,42 @@ type Account struct { Attributes map[string]interface{} `json:"attributes,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client } func (c *Client) Create(ctx context.Context, authMethodId string, opt ...Option) (*Account, *api.Error, error) { if authMethodId == "" { return nil, nil, fmt.Errorf("empty authMethodId value passed into Create request") } + opts, apiOpts := getOpts(opt...) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("auth-methods/%s/accounts", authMethodId), opts.postMap, apiOpts...) + opts.postMap["auth_method_id"] = authMethodId + + req, err := c.client.NewRequest(ctx, "POST", "accounts", opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } + if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { @@ -72,22 +87,17 @@ func (c *Client) Create(ctx context.Context, authMethodId string, opt ...Option) return target, apiErr, nil } -func (c *Client) Read(ctx context.Context, authMethodId string, accountId string, opt ...Option) (*Account, *api.Error, error) { - if authMethodId == "" { - return nil, nil, fmt.Errorf("empty authMethodId value passed into Read request") - } - +func (c *Client) Read(ctx context.Context, accountId string, opt ...Option) (*Account, *api.Error, error) { if accountId == "" { - return nil, nil, fmt.Errorf("empty accountId value passed into Read request") + return nil, nil, fmt.Errorf("empty accountId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) - req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("auth-methods/%s/accounts/%s", authMethodId, accountId), nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("accounts/%s", accountId), nil, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Read request: %w", err) } @@ -116,10 +126,7 @@ func (c *Client) Read(ctx context.Context, authMethodId string, accountId string return target, apiErr, nil } -func (c *Client) Update(ctx context.Context, authMethodId string, accountId string, version uint32, opt ...Option) (*Account, *api.Error, error) { - if authMethodId == "" { - return nil, nil, fmt.Errorf("empty authMethodId value passed into Update request") - } +func (c *Client) Update(ctx context.Context, accountId string, version uint32, opt ...Option) (*Account, *api.Error, error) { if accountId == "" { return nil, nil, fmt.Errorf("empty accountId value passed into Update request") } @@ -133,7 +140,7 @@ func (c *Client) Update(ctx context.Context, authMethodId string, accountId stri if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") } - existingTarget, existingApiErr, existingErr := c.Read(ctx, authMethodId, accountId, opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, accountId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -148,7 +155,7 @@ func (c *Client) Update(ctx context.Context, authMethodId string, accountId stri opts.postMap["version"] = version - req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("auth-methods/%s/accounts/%s", authMethodId, accountId), opts.postMap, apiOpts...) + req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("accounts/%s", accountId), opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Update request: %w", err) } @@ -177,22 +184,17 @@ func (c *Client) Update(ctx context.Context, authMethodId string, accountId stri return target, apiErr, nil } -func (c *Client) Delete(ctx context.Context, authMethodId string, accountId string, opt ...Option) (bool, *api.Error, error) { - if authMethodId == "" { - return false, nil, fmt.Errorf("empty authMethodId value passed into Delete request") - } - +func (c *Client) Delete(ctx context.Context, accountId string, opt ...Option) (bool, *api.Error, error) { if accountId == "" { return false, nil, fmt.Errorf("empty accountId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) - req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("auth-methods/%s/accounts/%s", authMethodId, accountId), nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("accounts/%s", accountId), nil, apiOpts...) if err != nil { return false, nil, fmt.Errorf("error creating Delete request: %w", err) } @@ -228,14 +230,14 @@ func (c *Client) List(ctx context.Context, authMethodId string, opt ...Option) ( if authMethodId == "" { return nil, nil, fmt.Errorf("empty authMethodId value passed into List request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) + opts.queryMap["auth_method_id"] = authMethodId - req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("auth-methods/%s/accounts", authMethodId), nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "GET", "accounts", nil, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating List request: %w", err) } diff --git a/api/accounts/account_test.go b/api/accounts/account_test.go index 6e34c8fd4..b070127b1 100644 --- a/api/accounts/account_test.go +++ b/api/accounts/account_test.go @@ -3,6 +3,7 @@ package accounts_test import ( "fmt" "net/http" + "os" "testing" "github.com/hashicorp/boundary/api" @@ -15,18 +16,21 @@ import ( "github.com/stretchr/testify/require" ) -func TestAccounts_List(t *testing.T) { +func TestList(t *testing.T) { + os.Setenv("BOUNDARY_LOG_URLS", "1") + os.Setenv("BOUNDARY_DEV_SKIP_AUTHZ", "1") assert, require := assert.New(t), require.New(t) - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org := iam.TestOrg(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) + require.NotNil(client) + token := tc.Token() + require.NotNil(token) + client.SetToken(token.Token) + org := iam.TestOrg(t, tc.IamRepo(), iam.WithUserId(token.UserId)) amClient := authmethods.NewClient(client) - am, apiErr, err := amClient.Create(tc.Context(), "password") + am, apiErr, err := amClient.Create(tc.Context(), "password", org.GetPublicId()) require.NoError(err) require.Nil(apiErr) require.NotNil(am) @@ -77,21 +81,15 @@ func comparableSlice(in []*accounts.Account) []accounts.Account { return filtered } -func TestAccount_Crud(t *testing.T) { +func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org := iam.TestOrg(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - + token := tc.Token() + client.SetToken(token.Token) + amId := token.AuthMethodId accountClient := accounts.NewClient(client) checkAccount := func(step string, u *accounts.Account, apiErr *api.Error, err error, wantedName string, wantedVersion uint32) { @@ -111,35 +109,30 @@ func TestAccount_Crud(t *testing.T) { u, apiErr, err := accountClient.Create(tc.Context(), amId, accounts.WithName("foo"), accounts.WithPasswordAccountLoginName("loginname")) checkAccount("create", u, apiErr, err, "foo", 1) - u, apiErr, err = accountClient.Read(tc.Context(), amId, u.Id) + u, apiErr, err = accountClient.Read(tc.Context(), u.Id) checkAccount("read", u, apiErr, err, "foo", 1) - u, apiErr, err = accountClient.Update(tc.Context(), amId, u.Id, u.Version, accounts.WithName("bar")) + u, apiErr, err = accountClient.Update(tc.Context(), u.Id, u.Version, accounts.WithName("bar")) checkAccount("update", u, apiErr, err, "bar", 2) - u, apiErr, err = accountClient.Update(tc.Context(), amId, u.Id, u.Version, accounts.DefaultName()) + u, apiErr, err = accountClient.Update(tc.Context(), u.Id, u.Version, accounts.DefaultName()) checkAccount("update", u, apiErr, err, "", 3) - existed, _, err := accountClient.Delete(tc.Context(), amId, u.Id) + existed, _, err := accountClient.Delete(tc.Context(), u.Id) require.NoError(err) assert.Nil(apiErr) assert.True(existed, "Expected existing account when deleted, but it wasn't.") } -func TestAccount_CustomMethods(t *testing.T) { +func TestCustomMethods(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org := iam.TestOrg(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + amId := token.AuthMethodId accountClient := accounts.NewClient(client) @@ -150,34 +143,28 @@ func TestAccount_CustomMethods(t *testing.T) { acct := al[0] - setAcct, apiErr, err := accountClient.SetPassword(tc.Context(), amId, acct.Id, "setpassword", acct.Version) + setAcct, apiErr, err := accountClient.SetPassword(tc.Context(), acct.Id, "setpassword", acct.Version) require.NoError(err) require.Nil(apiErr) require.NotNil(setAcct) assert.Equal(acct.Version+1, setAcct.Version) - changeAcct, apiErr, err := accountClient.ChangePassword(tc.Context(), amId, acct.Id, "setpassword", "changepassword", setAcct.Version) + changeAcct, apiErr, err := accountClient.ChangePassword(tc.Context(), acct.Id, "setpassword", "changepassword", setAcct.Version) require.NoError(err) require.Nil(apiErr) require.NotNil(changeAcct) assert.Equal(setAcct.Version+1, changeAcct.Version) } -func TestAccount_Errors(t *testing.T) { +func TestErrors(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org := iam.TestOrg(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - + token := tc.Token() + client.SetToken(token.Token) + amId := token.AuthMethodId accountClient := accounts.NewClient(client) u, apiErr, err := accountClient.Create(tc.Context(), amId, accounts.WithPasswordAccountLoginName("first")) @@ -190,17 +177,17 @@ func TestAccount_Errors(t *testing.T) { require.NoError(err) assert.NotNil(apiErr) - _, apiErr, err = accountClient.Read(tc.Context(), amId, password.AccountPrefix+"_doesntexis") + _, apiErr, err = accountClient.Read(tc.Context(), password.AccountPrefix+"_doesntexis") require.NoError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusForbidden, apiErr.Status) - _, apiErr, err = accountClient.Read(tc.Context(), amId, "invalid id") + _, apiErr, err = accountClient.Read(tc.Context(), "invalid id") require.NoError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusBadRequest, apiErr.Status) - _, apiErr, err = accountClient.Update(tc.Context(), amId, u.Id, u.Version) + _, apiErr, err = accountClient.Update(tc.Context(), u.Id, u.Version) require.NoError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusBadRequest, apiErr.Status) diff --git a/api/accounts/changepassword.go b/api/accounts/changepassword.go index 691296597..95dba51c2 100644 --- a/api/accounts/changepassword.go +++ b/api/accounts/changepassword.go @@ -9,10 +9,7 @@ import ( "github.com/kr/pretty" ) -func (c *Client) ChangePassword(ctx context.Context, authMethodId, accountId, oldPassword, newPassword string, version uint32, opt ...Option) (*Account, *api.Error, error) { - if authMethodId == "" { - return nil, nil, fmt.Errorf("empty authMethodId value passed into ChangePassword request") - } +func (c *Client) ChangePassword(ctx context.Context, accountId, currentPassword, newPassword string, version uint32, opt ...Option) (*Account, *api.Error, error) { if accountId == "" { return nil, nil, fmt.Errorf("empty accountId value passed into ChangePassword request") } @@ -26,7 +23,7 @@ func (c *Client) ChangePassword(ctx context.Context, authMethodId, accountId, ol if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") } - existingTarget, existingApiErr, existingErr := c.Read(ctx, authMethodId, accountId, opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, accountId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -40,12 +37,12 @@ func (c *Client) ChangePassword(ctx context.Context, authMethodId, accountId, ol } reqBody := map[string]interface{}{ - "version": version, - "old_password": oldPassword, - "new_password": newPassword, + "version": version, + "current_password": currentPassword, + "new_password": newPassword, } - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("auth-methods/%s/accounts/%s:change-password", authMethodId, accountId), reqBody, apiOpts...) + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("accounts/%s:change-password", accountId), reqBody, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating ChangePassword request: %w", err) } diff --git a/api/accounts/option.gen.go b/api/accounts/option.gen.go index 566063ba3..23251470b 100644 --- a/api/accounts/option.gen.go +++ b/api/accounts/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,25 +32,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/accounts/setpassword.go b/api/accounts/setpassword.go index 9618ba44a..d9e0f7b7d 100644 --- a/api/accounts/setpassword.go +++ b/api/accounts/setpassword.go @@ -9,10 +9,7 @@ import ( "github.com/kr/pretty" ) -func (c *Client) SetPassword(ctx context.Context, authMethodId, accountId, password string, version uint32, opt ...Option) (*Account, *api.Error, error) { - if authMethodId == "" { - return nil, nil, fmt.Errorf("empty authMethodId value passed into SetPassword request") - } +func (c *Client) SetPassword(ctx context.Context, accountId, password string, version uint32, opt ...Option) (*Account, *api.Error, error) { if accountId == "" { return nil, nil, fmt.Errorf("empty accountId value passed into SetPassword request") } @@ -26,7 +23,7 @@ func (c *Client) SetPassword(ctx context.Context, authMethodId, accountId, passw if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") } - existingTarget, existingApiErr, existingErr := c.Read(ctx, authMethodId, accountId, opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, accountId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -44,7 +41,7 @@ func (c *Client) SetPassword(ctx context.Context, authMethodId, accountId, passw "password": password, } - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("auth-methods/%s/accounts/%s:set-password", authMethodId, accountId), reqBody, apiOpts...) + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("accounts/%s:set-password", accountId), reqBody, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating SetPassword request: %w", err) } diff --git a/api/authmethods/authenticate.go b/api/authmethods/authenticate.go index b9f2ba554..96a7f778c 100644 --- a/api/authmethods/authenticate.go +++ b/api/authmethods/authenticate.go @@ -8,9 +8,7 @@ import ( "github.com/hashicorp/boundary/api/authtokens" ) -// TODO: This will need to be changed when we add Auth Method API to boundary. We'll also need a better -// way to handle different auth method types. -func (c *Client) Authenticate(ctx context.Context, authMethodId, loginName, password string, opt ...Option) (*authtokens.AuthToken, *api.Error, error) { +func (c *Client) Authenticate(ctx context.Context, authMethodId string, credentials map[string]interface{}, opt ...Option) (*authtokens.AuthToken, *api.Error, error) { if c.client == nil { return nil, nil, fmt.Errorf("nil client in Authenticate request") } @@ -18,10 +16,7 @@ func (c *Client) Authenticate(ctx context.Context, authMethodId, loginName, pass _, apiOpts := getOpts(opt...) reqBody := map[string]interface{}{ - "credentials": map[string]string{ - "login_name": loginName, - "password": password, - }, + "credentials": credentials, } req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("auth-methods/%s:authenticate", authMethodId), reqBody, apiOpts...) diff --git a/api/authmethods/authenticate_test.go b/api/authmethods/authenticate_test.go index 1e1e75172..e5bfced97 100644 --- a/api/authmethods/authenticate_test.go +++ b/api/authmethods/authenticate_test.go @@ -15,22 +15,21 @@ func TestAuthenticate(t *testing.T) { assert, require := assert.New(t), require.New(t) amId := "ampw_1234567890" tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", + DefaultAuthMethodId: amId, + DefaultLoginName: "user", + DefaultPassword: "passpass", }) defer tc.Shutdown() client := tc.Client() methods := authmethods.NewClient(client) - tok, apiErr, err := methods.Authenticate(tc.Context(), amId, "user", "passpass") + tok, apiErr, err := methods.Authenticate(tc.Context(), amId, map[string]interface{}{"login_name": "user", "password": "passpass"}) require.NoError(err) assert.Nil(apiErr, pretty.Sprint(apiErr)) assert.NotNil(tok) - _, apiErr, err = methods.Authenticate(tc.Context(), amId, "user", "wrong") + _, apiErr, err = methods.Authenticate(tc.Context(), amId, map[string]interface{}{"login_name": "user", "password": "wrong"}) require.NoError(err) require.NotNil(t, apiErr) assert.EqualValuesf(http.StatusUnauthorized, apiErr.Status, "Expected unauthenticated, got %q", apiErr.Message) diff --git a/api/authmethods/authmethods.gen.go b/api/authmethods/authmethods.gen.go index f38aa32e0..b8f8ea620 100644 --- a/api/authmethods/authmethods.gen.go +++ b/api/authmethods/authmethods.gen.go @@ -27,16 +27,31 @@ type AuthMethod struct { Attributes map[string]interface{} `json:"attributes,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} } -func (c *Client) Create(ctx context.Context, resourceType string, opt ...Option) (*AuthMethod, *api.Error, error) { +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client +} + +func (c *Client) Create(ctx context.Context, resourceType string, scopeId string, opt ...Option) (*AuthMethod, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into Create request") + } + opts, apiOpts := getOpts(opt...) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -46,10 +61,13 @@ func (c *Client) Create(ctx context.Context, resourceType string, opt ...Option) opts.postMap["type"] = resourceType } + opts.postMap["scope_id"] = scopeId + req, err := c.client.NewRequest(ctx, "POST", "auth-methods", opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } + if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { @@ -76,9 +94,8 @@ func (c *Client) Create(ctx context.Context, resourceType string, opt ...Option) func (c *Client) Read(ctx context.Context, authMethodId string, opt ...Option) (*AuthMethod, *api.Error, error) { if authMethodId == "" { - return nil, nil, fmt.Errorf("empty authMethodId value passed into Read request") + return nil, nil, fmt.Errorf("empty authMethodId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -176,7 +193,6 @@ func (c *Client) Delete(ctx context.Context, authMethodId string, opt ...Option) if authMethodId == "" { return false, nil, fmt.Errorf("empty authMethodId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } @@ -215,12 +231,16 @@ func (c *Client) Delete(ctx context.Context, authMethodId string, opt ...Option) return target.Existed, apiErr, nil } -func (c *Client) List(ctx context.Context, opt ...Option) ([]*AuthMethod, *api.Error, error) { +func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) ([]*AuthMethod, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into List request") + } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) + opts.queryMap["scope_id"] = scopeId req, err := c.client.NewRequest(ctx, "GET", "auth-methods", nil, apiOpts...) if err != nil { diff --git a/api/authmethods/option.gen.go b/api/authmethods/option.gen.go index ae33c206a..d21ff3ade 100644 --- a/api/authmethods/option.gen.go +++ b/api/authmethods/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,25 +32,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/authtokens/authtoken_test.go b/api/authtokens/authtoken_test.go index 60f4cc0fb..7dab5e3e4 100644 --- a/api/authtokens/authtoken_test.go +++ b/api/authtokens/authtoken_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/api/accounts" "github.com/hashicorp/boundary/api/authmethods" "github.com/hashicorp/boundary/api/authtokens" + "github.com/hashicorp/boundary/api/roles" "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/servers/controller" @@ -15,21 +16,41 @@ import ( "github.com/stretchr/testify/require" ) -func TestAuthTokens_List(t *testing.T) { +func TestList(t *testing.T) { assert, require := assert.New(t), require.New(t) + amId := "ampw_1234567890" tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, + DefaultAuthMethodId: amId, + DefaultLoginName: "user", + DefaultPassword: "passpass", }) defer tc.Shutdown() + + token := tc.Token() client := tc.Client() - org := iam.TestOrg(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) + client.SetToken(token.Token) + + org := iam.TestOrg(t, tc.IamRepo(), iam.WithUserId(token.UserId)) amClient := authmethods.NewClient(client) - am, apiErr, err := amClient.Create(tc.Context(), "password") + am, apiErr, err := amClient.Create(tc.Context(), "password", org.GetPublicId()) require.NoError(err) require.Nil(apiErr) require.NotNil(am) - amId := am.Id + amId = am.Id + + rolesClient := roles.NewClient(client) + role, apiErr, err := rolesClient.Create(tc.Context(), org.GetPublicId()) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(role) + role, apiErr, err = rolesClient.AddPrincipals(tc.Context(), role.Id, 0, []string{"u_anon"}, roles.WithAutomaticVersioning(true)) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(role) + role, apiErr, err = rolesClient.AddGrants(tc.Context(), role.Id, 0, []string{"type=auth-method;actions=authenticate"}, roles.WithAutomaticVersioning(true)) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(role) acctClient := accounts.NewClient(client) acct, apiErr, err := acctClient.Create(tc.Context(), amId, accounts.WithPasswordAccountLoginName("user"), accounts.WithPasswordAccountPassword("passpass")) @@ -38,32 +59,32 @@ func TestAuthTokens_List(t *testing.T) { require.NotNil(acct) tokens := authtokens.NewClient(client) - methods := authmethods.NewClient(client) - atl, apiErr, err := tokens.List(tc.Context()) + atl, apiErr, err := tokens.List(tc.Context(), org.GetPublicId()) require.NoError(err) assert.Nil(apiErr) assert.Empty(atl) var expected []*authtokens.AuthToken + methods := authmethods.NewClient(client) - at, apiErr, err := methods.Authenticate(tc.Context(), amId, "user", "passpass") + at, apiErr, err := methods.Authenticate(tc.Context(), amId, map[string]interface{}{"login_name": "user", "password": "passpass"}) require.NoError(err) assert.Nil(apiErr) expected = append(expected, at) - atl, apiErr, err = tokens.List(tc.Context()) + atl, apiErr, err = tokens.List(tc.Context(), org.GetPublicId()) require.NoError(err) assert.Nil(apiErr) assert.ElementsMatch(comparableSlice(expected), comparableSlice(atl)) for i := 1; i < 10; i++ { - at, apiErr, err = methods.Authenticate(tc.Context(), amId, "user", "passpass") + at, apiErr, err = methods.Authenticate(tc.Context(), amId, map[string]interface{}{"login_name": "user", "password": "passpass"}) require.NoError(err) assert.Nil(apiErr) expected = append(expected, at) } - atl, apiErr, err = tokens.List(tc.Context()) + atl, apiErr, err = tokens.List(tc.Context(), org.GetPublicId()) require.NoError(err) assert.Nil(apiErr) assert.ElementsMatch(comparableSlice(expected), comparableSlice(atl)) @@ -92,22 +113,23 @@ func comparableSlice(in []*authtokens.AuthToken) []authtokens.AuthToken { return filtered } -func TestAuthToken_Crud(t *testing.T) { +func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) amId := "ampw_1234567890" tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", + DefaultAuthMethodId: amId, + DefaultLoginName: "user", + DefaultPassword: "passpass", }) defer tc.Shutdown() client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) tokens := authtokens.NewClient(client) methods := authmethods.NewClient(client) - want, apiErr, err := methods.Authenticate(tc.Context(), amId, "user", "passpass") + want, apiErr, err := methods.Authenticate(tc.Context(), amId, map[string]interface{}{"login_name": "user", "password": "passpass"}) require.NoError(err) assert.Empty(apiErr) @@ -122,18 +144,14 @@ func TestAuthToken_Crud(t *testing.T) { assert.True(existed, "Expected existing token when deleted, but it wasn't.") } -func TestAuthToken_Errors(t *testing.T) { +func TestErrors(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) tokens := authtokens.NewClient(client) _, apiErr, err := tokens.Read(tc.Context(), authtoken.AuthTokenPrefix+"_doesntexis") diff --git a/api/authtokens/authtokens.gen.go b/api/authtokens/authtokens.gen.go index 5cbd2b89b..308f7845c 100644 --- a/api/authtokens/authtokens.gen.go +++ b/api/authtokens/authtokens.gen.go @@ -24,19 +24,28 @@ type AuthToken struct { ExpirationTime time.Time `json:"expiration_time,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client } func (c *Client) Read(ctx context.Context, authTokenId string, opt ...Option) (*AuthToken, *api.Error, error) { if authTokenId == "" { - return nil, nil, fmt.Errorf("empty authTokenId value passed into Read request") + return nil, nil, fmt.Errorf("empty authTokenId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -76,7 +85,6 @@ func (c *Client) Delete(ctx context.Context, authTokenId string, opt ...Option) if authTokenId == "" { return false, nil, fmt.Errorf("empty authTokenId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } @@ -115,12 +123,16 @@ func (c *Client) Delete(ctx context.Context, authTokenId string, opt ...Option) return target.Existed, apiErr, nil } -func (c *Client) List(ctx context.Context, opt ...Option) ([]*AuthToken, *api.Error, error) { +func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) ([]*AuthToken, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into List request") + } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) + opts.queryMap["scope_id"] = scopeId req, err := c.client.NewRequest(ctx, "GET", "auth-tokens", nil, apiOpts...) if err != nil { diff --git a/api/authtokens/option.gen.go b/api/authtokens/option.gen.go index f7a619666..c553a16c6 100644 --- a/api/authtokens/option.gen.go +++ b/api/authtokens/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,24 +32,15 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/client.go b/api/client.go index 02e7ce7df..4e2b208b8 100644 --- a/api/client.go +++ b/api/client.go @@ -38,7 +38,6 @@ const EnvBoundaryMaxRetries = "BOUNDARY_MAX_RETRIES" const EnvBoundaryToken = "BOUNDARY_TOKEN" const EnvBoundaryRateLimit = "BOUNDARY_RATE_LIMIT" const EnvBoundarySRVLookup = "BOUNDARY_SRV_LOOKUP" -const EnvBoundaryScopeId = "BOUNDARY_SCOPE_ID" // Config is used to configure the creation of the client type Config struct { @@ -97,9 +96,6 @@ type Config struct { // SRVLookup enables the client to lookup the host through DNS SRV lookup SRVLookup bool - - // ScopeId is the ID of the scope to use if not overridden per-call - ScopeId string } // TLSConfig contains the parameters needed to configure TLS on the HTTP client @@ -140,7 +136,6 @@ func DefaultConfig() (*Config, error) { HttpClient: cleanhttp.DefaultPooledClient(), Timeout: time.Second * 60, TLSConfig: &TLSConfig{}, - ScopeId: "global", } // We read the environment now; after DefaultClient returns we can override @@ -215,10 +210,9 @@ func (c *Config) ConfigureTLS() error { return nil } -// setAddr parses a given string and looks for scope info, setting the -// actual address to the base. Note that if a very malformed URL is passed in, -// this may not return what one expects. For now this is on purpose to avoid -// requiring error handling. +// setAddr parses a given string, setting the actual address to the base. Note +// that if a very malformed URL is passed in, this may not return what one +// expects. For now this is on purpose to avoid requiring error handling. // // This also removes any trailing "/v1"; we'll use that in our commands so we // don't require it from users. @@ -229,11 +223,12 @@ func (c *Config) setAddr(addr string) error { } c.Addr = fmt.Sprintf("%s://%s", u.Scheme, u.Host) - // If there is a scopes segment, elide everything after it. Do this only for - // the last "scopes" segment in case it's part of the base path. - if lastIndex := strings.LastIndex(u.Path, "scopes"); lastIndex != -1 { + // If there is a v1 segment, elide everything after it. Do this only for + // the last v1 segment in case it's part of the base path. + if lastIndex := strings.LastIndex(u.Path, "v1"); lastIndex != -1 { u.Path = u.Path[:lastIndex] } + // Remove trailing or leading slashes path := strings.Trim(u.Path, "/") // Remove v1 in front or back (e.g. they could have @@ -270,10 +265,6 @@ func (c *Config) ReadEnvironment() error { c.Token = v } - if v := os.Getenv(EnvBoundaryScopeId); v != "" { - c.ScopeId = v - } - if v := os.Getenv(EnvBoundaryMaxRetries); v != "" { maxRetries, err := strconv.ParseUint(v, 10, 32) if err != nil { @@ -439,23 +430,6 @@ func (c *Client) SetAddr(addr string) error { return c.config.setAddr(addr) } -// ScopeId fetches the scope ID the client will use by default -func (c *Client) ScopeId() string { - c.modifyLock.RLock() - defer c.modifyLock.RUnlock() - - return c.config.ScopeId -} - -// Sets the scope ID to use when making calls. This will apply to all calls with -// this client unless overridden per request via WithScopeId. -func (c *Client) SetScopeId(scopeId string) { - c.modifyLock.Lock() - defer c.modifyLock.Unlock() - - c.config.ScopeId = scopeId -} - // SetTLSConfig sets the TLS parameters to use and calls ConfigureTLS func (c *Client) SetTLSConfig(conf *TLSConfig) error { c.modifyLock.Lock() @@ -564,7 +538,6 @@ func (c *Client) Clone() *Client { CheckRetry: config.CheckRetry, Limiter: config.Limiter, SRVLookup: config.SRVLookup, - ScopeId: config.ScopeId, } if config.TLSConfig != nil { newConfig.TLSConfig = new(TLSConfig) @@ -632,7 +605,6 @@ func (c *Client) NewRequest(ctx context.Context, method, requestPath string, bod c.modifyLock.RLock() addr := c.config.Addr - scopeId := c.config.ScopeId srvLookup := c.config.SRVLookup token := c.config.Token httpClient := c.config.HttpClient @@ -677,46 +649,13 @@ func (c *Client) NewRequest(ctx context.Context, method, requestPath string, bod } } - opts := getOpts(opt...) - if opts.withScopeId != "" { - scopeId = opts.withScopeId - } - if scopeId == "" { - scopeId = "global" - } - - var finalPath string - switch opts.withNewStyle { - case true: - // Do nothing. Eventually, remove the notion of a scoped client; calls - // to specific resources won't need it, and calls to collections can - // take care of it in the functions themselves as part of the api - // parameters. - finalPath = path.Join(u.Path, "/v1/", requestPath) - default: - scopedPath := strings.TrimPrefix(requestPath, "/") - switch requestPath { - case "scopes": - // This is a special case for creating or listing scopes; don't do - // anything - - default: - // If their path already has 'scopes/' in it, use the given request path - // instead of building it from the client's scope information - if !strings.HasPrefix(scopedPath, "scopes/") { - scopedPath = path.Join("scopes", scopeId, scopedPath) - } - } - finalPath = path.Join(u.Path, "/v1", scopedPath) - } - req := &http.Request{ Method: method, URL: &url.URL{ User: u.User, Scheme: u.Scheme, Host: host, - Path: finalPath, + Path: path.Join(u.Path, "/v1/", requestPath), }, Host: u.Host, } diff --git a/api/client_test.go b/api/client_test.go index 6e47eb033..9688e7aff 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -47,7 +47,7 @@ func TestConfigSetAddress(t *testing.T) { }, { "longer path project", - "http://127.0.0.1:9200/v1/scopes/orgid/auth-methods", + "http://127.0.0.1:9200/v1/auth-methods", "http://127.0.0.1:9200", "", }, diff --git a/api/groups/group.gen.go b/api/groups/group.gen.go index a6be33a1f..2358a1eb5 100644 --- a/api/groups/group.gen.go +++ b/api/groups/group.gen.go @@ -27,24 +27,42 @@ type Group struct { Members []*Member `json:"members,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} } -func (c *Client) Create(ctx context.Context, opt ...Option) (*Group, *api.Error, error) { +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client +} + +func (c *Client) Create(ctx context.Context, scopeId string, opt ...Option) (*Group, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into Create request") + } + opts, apiOpts := getOpts(opt...) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } + opts.postMap["scope_id"] = scopeId + req, err := c.client.NewRequest(ctx, "POST", "groups", opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } + if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { @@ -71,9 +89,8 @@ func (c *Client) Create(ctx context.Context, opt ...Option) (*Group, *api.Error, func (c *Client) Read(ctx context.Context, groupId string, opt ...Option) (*Group, *api.Error, error) { if groupId == "" { - return nil, nil, fmt.Errorf("empty groupId value passed into Read request") + return nil, nil, fmt.Errorf("empty groupId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -171,7 +188,6 @@ func (c *Client) Delete(ctx context.Context, groupId string, opt ...Option) (boo if groupId == "" { return false, nil, fmt.Errorf("empty groupId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } @@ -210,12 +226,16 @@ func (c *Client) Delete(ctx context.Context, groupId string, opt ...Option) (boo return target.Existed, apiErr, nil } -func (c *Client) List(ctx context.Context, opt ...Option) ([]*Group, *api.Error, error) { +func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) ([]*Group, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into List request") + } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) + opts.queryMap["scope_id"] = scopeId req, err := c.client.NewRequest(ctx, "GET", "groups", nil, apiOpts...) if err != nil { diff --git a/api/groups/group_test.go b/api/groups/group_test.go index 6aad74a81..cecc7a3f7 100644 --- a/api/groups/group_test.go +++ b/api/groups/group_test.go @@ -14,52 +14,45 @@ import ( "github.com/stretchr/testify/require" ) -func TestGroup_List(t *testing.T) { +func TestList(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) cases := []struct { - name string - scopeClient *api.Client + name string + scopeId string }{ { - name: "org", - scopeClient: client, + name: "org", + scopeId: org.GetPublicId(), }, { - name: "proj", - scopeClient: projClient, + name: "proj", + scopeId: proj.GetPublicId(), }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - grps := groups.NewClient(tt.scopeClient) - pl, apiErr, err := grps.List(tc.Context()) + grps := groups.NewClient(client) + pl, apiErr, err := grps.List(tc.Context(), tt.scopeId) assert.NoError(err) assert.Nil(apiErr) assert.Empty(pl) expected := make([]*groups.Group, 10) for i := 0; i < 10; i++ { - expected[i], apiErr, err = grps.Create(tc.Context(), groups.WithName(fmt.Sprint(i))) + expected[i], apiErr, err = grps.Create(tc.Context(), tt.scopeId, groups.WithName(fmt.Sprint(i))) require.NoError(err) assert.Nil(apiErr) } - pl, apiErr, err = grps.List(tc.Context()) + pl, apiErr, err = grps.List(tc.Context(), tt.scopeId) require.NoError(err) assert.Nil(apiErr) require.NotNil(pl) @@ -84,22 +77,15 @@ func comparableSlice(in []*groups.Group) []groups.Group { return filtered } -func TestGroup_Crud(t *testing.T) { +func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) checkGroup := func(step string, g *groups.Group, apiErr *api.Error, err error, wantedName string, wantedVersion uint32, expectedUserIds []string) { require.NoError(err, step) @@ -120,31 +106,31 @@ func TestGroup_Crud(t *testing.T) { } cases := []struct { - name string - scopeClient *api.Client + name string + scopeId string }{ { - name: "org", - scopeClient: client, + name: "org", + scopeId: org.GetPublicId(), }, { - name: "proj", - scopeClient: projClient, + name: "proj", + scopeId: proj.GetPublicId(), }, } - user1, apiErr, err := users.NewClient(client).Create(tc.Context()) + user1, apiErr, err := users.NewClient(client).Create(tc.Context(), org.GetPublicId()) require.NoError(err) require.Nil(apiErr) - user2, apiErr, err := users.NewClient(client).Create(tc.Context()) + user2, apiErr, err := users.NewClient(client).Create(tc.Context(), org.GetPublicId()) require.NoError(err) require.Nil(apiErr) for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - groupsClient := groups.NewClient(tt.scopeClient) - g, apiErr, err := groupsClient.Create(tc.Context(), groups.WithName("foo")) + groupsClient := groups.NewClient(client) + g, apiErr, err := groupsClient.Create(tc.Context(), tt.scopeId, groups.WithName("foo")) checkGroup("create", g, apiErr, err, "foo", 1, nil) g, apiErr, err = groupsClient.Read(tc.Context(), g.Id) @@ -177,48 +163,41 @@ func TestGroup_Crud(t *testing.T) { } } -func TestGroup_Errors(t *testing.T) { +func TestErrors(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) cases := []struct { - name string - scopeClient *api.Client + name string + scopeId string }{ { - name: "org", - scopeClient: client, + name: "org", + scopeId: org.GetPublicId(), }, { - name: "proj", - scopeClient: projClient, + name: "proj", + scopeId: proj.GetPublicId(), }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - groupClient := groups.NewClient(tt.scopeClient) + groupClient := groups.NewClient(client) - g, apiErr, err := groupClient.Create(tc.Context(), groups.WithName("first")) + g, apiErr, err := groupClient.Create(tc.Context(), tt.scopeId, groups.WithName("first")) require.NoError(err) assert.Nil(apiErr) assert.NotNil(g) // Create another resource with the same name. - _, apiErr, err = groupClient.Create(tc.Context(), groups.WithName("first")) + _, apiErr, err = groupClient.Create(tc.Context(), tt.scopeId, groups.WithName("first")) require.NoError(err) assert.NotNil(apiErr) diff --git a/api/groups/option.gen.go b/api/groups/option.gen.go index 6c9f07563..96420d58b 100644 --- a/api/groups/option.gen.go +++ b/api/groups/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,25 +32,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/hostcatalogs/host_catalog.gen.go b/api/hostcatalogs/host_catalog.gen.go index e795cb068..520fa109d 100644 --- a/api/hostcatalogs/host_catalog.gen.go +++ b/api/hostcatalogs/host_catalog.gen.go @@ -27,16 +27,31 @@ type HostCatalog struct { Attributes map[string]interface{} `json:"attributes,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} } -func (c *Client) Create(ctx context.Context, resourceType string, opt ...Option) (*HostCatalog, *api.Error, error) { +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client +} + +func (c *Client) Create(ctx context.Context, resourceType string, scopeId string, opt ...Option) (*HostCatalog, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into Create request") + } + opts, apiOpts := getOpts(opt...) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -46,10 +61,13 @@ func (c *Client) Create(ctx context.Context, resourceType string, opt ...Option) opts.postMap["type"] = resourceType } + opts.postMap["scope_id"] = scopeId + req, err := c.client.NewRequest(ctx, "POST", "host-catalogs", opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } + if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { @@ -76,9 +94,8 @@ func (c *Client) Create(ctx context.Context, resourceType string, opt ...Option) func (c *Client) Read(ctx context.Context, hostCatalogId string, opt ...Option) (*HostCatalog, *api.Error, error) { if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Read request") + return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -176,7 +193,6 @@ func (c *Client) Delete(ctx context.Context, hostCatalogId string, opt ...Option if hostCatalogId == "" { return false, nil, fmt.Errorf("empty hostCatalogId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } @@ -215,12 +231,16 @@ func (c *Client) Delete(ctx context.Context, hostCatalogId string, opt ...Option return target.Existed, apiErr, nil } -func (c *Client) List(ctx context.Context, opt ...Option) ([]*HostCatalog, *api.Error, error) { +func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) ([]*HostCatalog, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into List request") + } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) + opts.queryMap["scope_id"] = scopeId req, err := c.client.NewRequest(ctx, "GET", "host-catalogs", nil, apiOpts...) if err != nil { diff --git a/api/hostcatalogs/host_catalog_test.go b/api/hostcatalogs/host_catalog_test.go index 666134c2c..7574f0b45 100644 --- a/api/hostcatalogs/host_catalog_test.go +++ b/api/hostcatalogs/host_catalog_test.go @@ -14,23 +14,18 @@ import ( "github.com/stretchr/testify/require" ) -func TestCatalog_List(t *testing.T) { +func TestList(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - _, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) catalogClient := hostcatalogs.NewClient(client) - ul, apiErr, err := catalogClient.List(tc.Context()) + ul, apiErr, err := catalogClient.List(tc.Context(), proj.GetPublicId()) assert.NoError(err) assert.Nil(apiErr) assert.Empty(ul) @@ -40,21 +35,21 @@ func TestCatalog_List(t *testing.T) { expected = append(expected, &hostcatalogs.HostCatalog{Name: fmt.Sprint(i)}) } - expected[0], apiErr, err = catalogClient.Create(tc.Context(), "static", hostcatalogs.WithName(expected[0].Name)) + expected[0], apiErr, err = catalogClient.Create(tc.Context(), "static", proj.GetPublicId(), hostcatalogs.WithName(expected[0].Name)) assert.NoError(err) assert.Nil(apiErr) - ul, apiErr, err = catalogClient.List(tc.Context()) + ul, apiErr, err = catalogClient.List(tc.Context(), proj.GetPublicId()) assert.NoError(err) assert.Nil(apiErr) assert.ElementsMatch(comparableCatalogSlice(expected[:1]), comparableCatalogSlice(ul)) for i := 1; i < 10; i++ { - expected[i], apiErr, err = catalogClient.Create(tc.Context(), "static", hostcatalogs.WithName(expected[i].Name)) + expected[i], apiErr, err = catalogClient.Create(tc.Context(), "static", proj.GetPublicId(), hostcatalogs.WithName(expected[i].Name)) assert.NoError(err) assert.Nil(apiErr) } - ul, apiErr, err = catalogClient.List(tc.Context()) + ul, apiErr, err = catalogClient.List(tc.Context(), proj.GetPublicId()) require.NoError(err) assert.Nil(apiErr) assert.ElementsMatch(comparableCatalogSlice(expected), comparableCatalogSlice(ul)) @@ -75,22 +70,15 @@ func comparableCatalogSlice(in []*hostcatalogs.HostCatalog) []hostcatalogs.HostC return filtered } -func TestCatalogs_Crud(t *testing.T) { +func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) checkCatalog := func(step string, hc *hostcatalogs.HostCatalog, apiErr *api.Error, err error, wantedName string, wantVersion uint32) { require.NoError(err, step) @@ -106,9 +94,9 @@ func TestCatalogs_Crud(t *testing.T) { assert.Equal(wantVersion, hc.Version) } - hcClient := hostcatalogs.NewClient(projClient) + hcClient := hostcatalogs.NewClient(client) - hc, apiErr, err := hcClient.Create(tc.Context(), "static", hostcatalogs.WithName("foo")) + hc, apiErr, err := hcClient.Create(tc.Context(), "static", proj.GetPublicId(), hostcatalogs.WithName("foo")) checkCatalog("create", hc, apiErr, err, "foo", 1) hc, apiErr, err = hcClient.Read(tc.Context(), hc.Id) @@ -131,28 +119,23 @@ func TestCatalogs_Crud(t *testing.T) { } // TODO: Get better coverage for expected errors and error formats. -func TestCatalogs_Errors(t *testing.T) { +func TestErrors(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - _, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) pc := hostcatalogs.NewClient(client) - hc, apiErr, err := pc.Create(tc.Context(), "static", hostcatalogs.WithName("foo")) + hc, apiErr, err := pc.Create(tc.Context(), "static", proj.GetPublicId(), hostcatalogs.WithName("foo")) require.NoError(err) assert.Nil(apiErr) assert.NotNil(hc) - _, apiErr, err = pc.Create(tc.Context(), "static", hostcatalogs.WithName("foo")) + _, apiErr, err = pc.Create(tc.Context(), "static", proj.GetPublicId(), hostcatalogs.WithName("foo")) require.NoError(err) assert.NotNil(apiErr) diff --git a/api/hostcatalogs/option.gen.go b/api/hostcatalogs/option.gen.go index 28830ac84..883ff83c1 100644 --- a/api/hostcatalogs/option.gen.go +++ b/api/hostcatalogs/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,25 +32,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/hosts/host.gen.go b/api/hosts/host.gen.go index 8bb89d74e..d26c044b4 100644 --- a/api/hosts/host.gen.go +++ b/api/hosts/host.gen.go @@ -28,27 +28,42 @@ type Host struct { Attributes map[string]interface{} `json:"attributes,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client } func (c *Client) Create(ctx context.Context, hostCatalogId string, opt ...Option) (*Host, *api.Error, error) { if hostCatalogId == "" { return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Create request") } + opts, apiOpts := getOpts(opt...) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-catalogs/%s/hosts", hostCatalogId), opts.postMap, apiOpts...) + opts.postMap["host_catalog_id"] = hostCatalogId + + req, err := c.client.NewRequest(ctx, "POST", "hosts", opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } + if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { @@ -73,22 +88,17 @@ func (c *Client) Create(ctx context.Context, hostCatalogId string, opt ...Option return target, apiErr, nil } -func (c *Client) Read(ctx context.Context, hostCatalogId string, hostId string, opt ...Option) (*Host, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Read request") - } - +func (c *Client) Read(ctx context.Context, hostId string, opt ...Option) (*Host, *api.Error, error) { if hostId == "" { - return nil, nil, fmt.Errorf("empty hostId value passed into Read request") + return nil, nil, fmt.Errorf("empty hostId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) - req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("host-catalogs/%s/hosts/%s", hostCatalogId, hostId), nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("hosts/%s", hostId), nil, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Read request: %w", err) } @@ -117,10 +127,7 @@ func (c *Client) Read(ctx context.Context, hostCatalogId string, hostId string, return target, apiErr, nil } -func (c *Client) Update(ctx context.Context, hostCatalogId string, hostId string, version uint32, opt ...Option) (*Host, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Update request") - } +func (c *Client) Update(ctx context.Context, hostId string, version uint32, opt ...Option) (*Host, *api.Error, error) { if hostId == "" { return nil, nil, fmt.Errorf("empty hostId value passed into Update request") } @@ -134,7 +141,7 @@ func (c *Client) Update(ctx context.Context, hostCatalogId string, hostId string if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") } - existingTarget, existingApiErr, existingErr := c.Read(ctx, hostCatalogId, hostId, opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, hostId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -149,7 +156,7 @@ func (c *Client) Update(ctx context.Context, hostCatalogId string, hostId string opts.postMap["version"] = version - req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("host-catalogs/%s/hosts/%s", hostCatalogId, hostId), opts.postMap, apiOpts...) + req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("hosts/%s", hostId), opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Update request: %w", err) } @@ -178,22 +185,17 @@ func (c *Client) Update(ctx context.Context, hostCatalogId string, hostId string return target, apiErr, nil } -func (c *Client) Delete(ctx context.Context, hostCatalogId string, hostId string, opt ...Option) (bool, *api.Error, error) { - if hostCatalogId == "" { - return false, nil, fmt.Errorf("empty hostCatalogId value passed into Delete request") - } - +func (c *Client) Delete(ctx context.Context, hostId string, opt ...Option) (bool, *api.Error, error) { if hostId == "" { return false, nil, fmt.Errorf("empty hostId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) - req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("host-catalogs/%s/hosts/%s", hostCatalogId, hostId), nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("hosts/%s", hostId), nil, apiOpts...) if err != nil { return false, nil, fmt.Errorf("error creating Delete request: %w", err) } @@ -229,14 +231,14 @@ func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option) if hostCatalogId == "" { return nil, nil, fmt.Errorf("empty hostCatalogId value passed into List request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) + opts.queryMap["host_catalog_id"] = hostCatalogId - req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("host-catalogs/%s/hosts", hostCatalogId), nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "GET", "hosts", nil, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating List request: %w", err) } diff --git a/api/hosts/host_test.go b/api/hosts/host_test.go index 616a030c2..42c7c642b 100644 --- a/api/hosts/host_test.go +++ b/api/hosts/host_test.go @@ -15,22 +15,17 @@ import ( "github.com/stretchr/testify/require" ) -func TestHost_List(t *testing.T) { +func TestList(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - _, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static") + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) require.NoError(err) require.Nil(apiErr) require.NotNil(hc) @@ -82,24 +77,17 @@ func comparableHostSlice(in []*hosts.Host) []hosts.Host { return filtered } -func TestHost_Crud(t *testing.T) { +func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - hc, apiErr, err := hostcatalogs.NewClient(projClient).Create(tc.Context(), "static") + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) require.NoError(err) require.Nil(apiErr) require.NotNil(hc) @@ -118,47 +106,42 @@ func TestHost_Crud(t *testing.T) { assert.Equal(wantVersion, h.Version) } - hClient := hosts.NewClient(projClient) + hClient := hosts.NewClient(client) h, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithName("foo"), hosts.WithStaticHostAddress("someaddress")) checkHost("create", h, apiErr, err, "foo", 1) - h, apiErr, err = hClient.Read(tc.Context(), hc.Id, h.Id) + h, apiErr, err = hClient.Read(tc.Context(), h.Id) checkHost("read", h, apiErr, err, "foo", 1) - h, apiErr, err = hClient.Update(tc.Context(), hc.Id, h.Id, h.Version, hosts.WithName("bar")) + h, apiErr, err = hClient.Update(tc.Context(), h.Id, h.Version, hosts.WithName("bar")) checkHost("update", h, apiErr, err, "bar", 2) - h, apiErr, err = hClient.Update(tc.Context(), hc.Id, h.Id, h.Version, hosts.DefaultName()) + h, apiErr, err = hClient.Update(tc.Context(), h.Id, h.Version, hosts.DefaultName()) checkHost("update", h, apiErr, err, "", 3) - existed, apiErr, err := hClient.Delete(tc.Context(), hc.Id, h.Id) + existed, apiErr, err := hClient.Delete(tc.Context(), h.Id) assert.NoError(err) assert.True(existed, "Expected existing catalog when deleted, but it wasn't.") - existed, apiErr, err = hClient.Delete(tc.Context(), hc.Id, h.Id) + existed, apiErr, err = hClient.Delete(tc.Context(), h.Id) require.NoError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusForbidden, apiErr.Status) } // TODO: Get better coverage for expected errors and error formats. -func TestHost_Errors(t *testing.T) { +func TestErrors(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - _, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static") + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) require.NoError(err) require.Nil(apiErr) require.NotNil(hc) @@ -174,12 +157,12 @@ func TestHost_Errors(t *testing.T) { require.NoError(err) assert.NotNil(apiErr) - _, apiErr, err = hClient.Read(tc.Context(), hc.Id, static.HostPrefix+"_doesntexis") + _, apiErr, err = hClient.Read(tc.Context(), static.HostPrefix+"_doesntexis") require.NoError(err) assert.NotNil(apiErr) assert.EqualValues(apiErr.Status, http.StatusForbidden) - _, apiErr, err = hClient.Read(tc.Context(), hc.Id, "invalid id") + _, apiErr, err = hClient.Read(tc.Context(), "invalid id") require.NoError(err) assert.NotNil(apiErr) assert.EqualValues(http.StatusBadRequest, apiErr.Status) diff --git a/api/hosts/option.gen.go b/api/hosts/option.gen.go index 2395586c2..6ea514da3 100644 --- a/api/hosts/option.gen.go +++ b/api/hosts/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,25 +32,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/hostsets/host_set.gen.go b/api/hostsets/host_set.gen.go index b271da8f2..34b4d92ed 100644 --- a/api/hostsets/host_set.gen.go +++ b/api/hostsets/host_set.gen.go @@ -28,252 +28,31 @@ type HostSet struct { Attributes map[string]interface{} `json:"attributes,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client } func (c *Client) Create(ctx context.Context, hostCatalogId string, opt ...Option) (*HostSet, *api.Error, error) { if hostCatalogId == "" { return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Create request") } - opts, apiOpts := getOpts(opt...) - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-catalogs/%s/host-sets", hostCatalogId), opts.postMap, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating Create 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, nil, fmt.Errorf("error performing client request during Create call: %w", err) - } - - target := new(HostSet) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding Create response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} - -func (c *Client) Read(ctx context.Context, hostCatalogId string, hostSetId string, opt ...Option) (*HostSet, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Read request") - } - - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into Read request") - } - - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } opts, apiOpts := getOpts(opt...) - req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("host-catalogs/%s/host-sets/%s", hostCatalogId, hostSetId), nil, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating Read 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, nil, fmt.Errorf("error performing client request during Read call: %w", err) - } - - target := new(HostSet) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding Read response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} - -func (c *Client) Update(ctx context.Context, hostCatalogId string, hostSetId string, version uint32, opt ...Option) (*HostSet, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Update request") - } - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into Update request") - } - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - - if version == 0 { - if !opts.withAutomaticVersioning { - return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") - } - existingTarget, existingApiErr, existingErr := c.Read(ctx, hostCatalogId, hostSetId, opt...) - if existingErr != nil { - return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) - } - if existingApiErr != nil { - return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) - } - if existingTarget == nil { - return nil, nil, errors.New("nil resource found when performing initial check-and-set read") - } - version = existingTarget.Version - } - - opts.postMap["version"] = version - - req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("host-catalogs/%s/host-sets/%s", hostCatalogId, hostSetId), opts.postMap, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating Update 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, nil, fmt.Errorf("error performing client request during Update call: %w", err) - } - - target := new(HostSet) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding Update response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} - -func (c *Client) Delete(ctx context.Context, hostCatalogId string, hostSetId string, opt ...Option) (bool, *api.Error, error) { - if hostCatalogId == "" { - return false, nil, fmt.Errorf("empty hostCatalogId value passed into Delete request") - } - - if hostSetId == "" { - return false, nil, fmt.Errorf("empty hostSetId value passed into Delete request") - } - - if c.client == nil { - return false, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - - req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("host-catalogs/%s/host-sets/%s", hostCatalogId, hostSetId), nil, apiOpts...) - if err != nil { - return false, nil, fmt.Errorf("error creating Delete 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 false, nil, fmt.Errorf("error performing client request during Delete call: %w", err) - } - - type deleteResponse struct { - Existed bool - } - target := &deleteResponse{} - apiErr, err := resp.Decode(target) - if err != nil { - return false, nil, fmt.Errorf("error decoding Delete response: %w", err) - } - if apiErr != nil { - return false, apiErr, nil - } - return target.Existed, apiErr, nil -} - -func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option) ([]*HostSet, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into List request") - } - - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - - req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("host-catalogs/%s/host-sets", hostCatalogId), nil, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating List 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, nil, fmt.Errorf("error performing client request during List call: %w", err) - } - - type listResponse struct { - Items []*HostSet - } - target := &listResponse{} - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding List response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target.Items, apiErr, nil -} - -func (c *Client) Create2(ctx context.Context, hostCatalogId string, opt ...Option) (*HostSet, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Create request") - } - opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -309,7 +88,7 @@ func (c *Client) Create2(ctx context.Context, hostCatalogId string, opt ...Optio return target, apiErr, nil } -func (c *Client) Read2(ctx context.Context, hostSetId string, opt ...Option) (*HostSet, *api.Error, error) { +func (c *Client) Read(ctx context.Context, hostSetId string, opt ...Option) (*HostSet, *api.Error, error) { if hostSetId == "" { return nil, nil, fmt.Errorf("empty hostSetId value passed into Read request") } @@ -318,7 +97,6 @@ func (c *Client) Read2(ctx context.Context, hostSetId string, opt ...Option) (*H } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("host-sets/%s", hostSetId), nil, apiOpts...) if err != nil { @@ -349,7 +127,7 @@ func (c *Client) Read2(ctx context.Context, hostSetId string, opt ...Option) (*H return target, apiErr, nil } -func (c *Client) Update2(ctx context.Context, hostSetId string, version uint32, opt ...Option) (*HostSet, *api.Error, error) { +func (c *Client) Update(ctx context.Context, hostSetId string, version uint32, opt ...Option) (*HostSet, *api.Error, error) { if hostSetId == "" { return nil, nil, fmt.Errorf("empty hostSetId value passed into Update request") } @@ -358,13 +136,12 @@ func (c *Client) Update2(ctx context.Context, hostSetId string, version uint32, } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) if version == 0 { if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") } - existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, hostSetId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -408,7 +185,7 @@ func (c *Client) Update2(ctx context.Context, hostSetId string, version uint32, return target, apiErr, nil } -func (c *Client) Delete2(ctx context.Context, hostSetId string, opt ...Option) (bool, *api.Error, error) { +func (c *Client) Delete(ctx context.Context, hostSetId string, opt ...Option) (bool, *api.Error, error) { if hostSetId == "" { return false, nil, fmt.Errorf("empty hostSetId value passed into Delete request") } @@ -417,7 +194,6 @@ func (c *Client) Delete2(ctx context.Context, hostSetId string, opt ...Option) ( } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("host-sets/%s", hostSetId), nil, apiOpts...) if err != nil { @@ -451,7 +227,7 @@ func (c *Client) Delete2(ctx context.Context, hostSetId string, opt ...Option) ( return target.Existed, apiErr, nil } -func (c *Client) List2(ctx context.Context, hostCatalogId string, opt ...Option) ([]*HostSet, *api.Error, error) { +func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option) ([]*HostSet, *api.Error, error) { if hostCatalogId == "" { return nil, nil, fmt.Errorf("empty hostCatalogId value passed into List request") } @@ -460,7 +236,6 @@ func (c *Client) List2(ctx context.Context, hostCatalogId string, opt ...Option) } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) opts.queryMap["host_catalog_id"] = hostCatalogId req, err := c.client.NewRequest(ctx, "GET", "host-sets", nil, apiOpts...) @@ -495,10 +270,7 @@ func (c *Client) List2(ctx context.Context, hostCatalogId string, opt ...Option) return target.Items, apiErr, nil } -func (c *Client) AddHosts(ctx context.Context, hostCatalogId string, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into AddHosts request") - } +func (c *Client) AddHosts(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { if hostSetId == "" { return nil, nil, fmt.Errorf("empty hostSetId value passed into AddHosts request") } @@ -512,203 +284,7 @@ func (c *Client) AddHosts(ctx context.Context, hostCatalogId string, hostSetId s if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into AddHosts request") } - existingTarget, existingApiErr, existingErr := c.Read(ctx, hostCatalogId, hostSetId, opt...) - if existingErr != nil { - return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) - } - if existingApiErr != nil { - return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) - } - if existingTarget == nil { - return nil, nil, errors.New("nil resource found when performing initial check-and-set read") - } - version = existingTarget.Version - } - - opts.postMap["version"] = version - - if len(hostIds) > 0 { - opts.postMap["host_ids"] = hostIds - } - - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-catalogs/%s/host-sets/%s:add-hosts", hostCatalogId, hostSetId), opts.postMap, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating AddHosts 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, nil, fmt.Errorf("error performing client request during AddHosts call: %w", err) - } - - target := new(HostSet) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding AddHosts response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} - -func (c *Client) SetHosts(ctx context.Context, hostCatalogId string, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into SetHosts request") - } - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into SetHosts request") - } - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - - if version == 0 { - if !opts.withAutomaticVersioning { - return nil, nil, errors.New("zero version number passed into SetHosts request") - } - existingTarget, existingApiErr, existingErr := c.Read(ctx, hostCatalogId, hostSetId, opt...) - if existingErr != nil { - return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) - } - if existingApiErr != nil { - return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) - } - if existingTarget == nil { - return nil, nil, errors.New("nil resource found when performing initial check-and-set read") - } - version = existingTarget.Version - } - - opts.postMap["version"] = version - - if len(hostIds) > 0 { - opts.postMap["host_ids"] = hostIds - } else if hostIds != nil { - // In this function, a non-nil but empty list means clear out - opts.postMap["host_ids"] = nil - } - - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-catalogs/%s/host-sets/%s:set-hosts", hostCatalogId, hostSetId), opts.postMap, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating SetHosts 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, nil, fmt.Errorf("error performing client request during SetHosts call: %w", err) - } - - target := new(HostSet) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding SetHosts response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} - -func (c *Client) RemoveHosts(ctx context.Context, hostCatalogId string, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into RemoveHosts request") - } - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into RemoveHosts request") - } - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - - if version == 0 { - if !opts.withAutomaticVersioning { - return nil, nil, errors.New("zero version number passed into RemoveHosts request") - } - existingTarget, existingApiErr, existingErr := c.Read(ctx, hostCatalogId, hostSetId, opt...) - if existingErr != nil { - return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) - } - if existingApiErr != nil { - return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) - } - if existingTarget == nil { - return nil, nil, errors.New("nil resource found when performing initial check-and-set read") - } - version = existingTarget.Version - } - - opts.postMap["version"] = version - - if len(hostIds) > 0 { - opts.postMap["host_ids"] = hostIds - } - - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-catalogs/%s/host-sets/%s:remove-hosts", hostCatalogId, hostSetId), opts.postMap, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating RemoveHosts 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, nil, fmt.Errorf("error performing client request during RemoveHosts call: %w", err) - } - - target := new(HostSet) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding RemoveHosts response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} - -func (c *Client) AddHosts2(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into AddHosts request") - } - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) - - if version == 0 { - if !opts.withAutomaticVersioning { - return nil, nil, errors.New("zero version number passed into AddHosts request") - } - existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, hostSetId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -756,7 +332,7 @@ func (c *Client) AddHosts2(ctx context.Context, hostSetId string, version uint32 return target, apiErr, nil } -func (c *Client) SetHosts2(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { +func (c *Client) SetHosts(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { if hostSetId == "" { return nil, nil, fmt.Errorf("empty hostSetId value passed into SetHosts request") } @@ -765,13 +341,12 @@ func (c *Client) SetHosts2(ctx context.Context, hostSetId string, version uint32 } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) if version == 0 { if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into SetHosts request") } - existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, hostSetId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -822,7 +397,7 @@ func (c *Client) SetHosts2(ctx context.Context, hostSetId string, version uint32 return target, apiErr, nil } -func (c *Client) RemoveHosts2(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { +func (c *Client) RemoveHosts(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { if hostSetId == "" { return nil, nil, fmt.Errorf("empty hostSetId value passed into RemoveHosts request") } @@ -831,13 +406,12 @@ func (c *Client) RemoveHosts2(ctx context.Context, hostSetId string, version uin } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) if version == 0 { if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into RemoveHosts request") } - existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, hostSetId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } diff --git a/api/hostsets/host_set_test.go b/api/hostsets/host_set_test.go index b61c1259f..d4922d837 100644 --- a/api/hostsets/host_set_test.go +++ b/api/hostsets/host_set_test.go @@ -18,129 +18,95 @@ import ( ) func TestCustom(t *testing.T) { - for _, newStyle := range []bool{false, true} { - t.Run(fmt.Sprintf("custom_%t", newStyle), func(t *testing.T) { - assert, require := assert.New(t), require.New(t) - tc := controller.NewTestController(t, nil) - defer tc.Shutdown() + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() - token := tc.Token() - _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - client := tc.Client().Clone() - client.SetScopeId(proj.GetPublicId()) + client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static") - require.NoError(err) - require.Nil(apiErr) + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) + require.NoError(err) + require.Nil(apiErr) - hClient := hosts.NewClient(client) - h1, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) - require.NoError(err) - require.Nil(apiErr) - h2, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) - require.NoError(err) - require.Nil(apiErr) + hClient := hosts.NewClient(client) + h1, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) + require.NoError(err) + require.Nil(apiErr) + h2, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) + require.NoError(err) + require.Nil(apiErr) - hSetClient := hostsets.NewClient(client) - var hSet *hostsets.HostSet - if newStyle { - hSet, apiErr, err = hSetClient.Create2(tc.Context(), hc.Id) - } else { - hSet, apiErr, err = hSetClient.Create(tc.Context(), hc.Id) - } - require.NoError(err) - require.Nil(apiErr) + hSetClient := hostsets.NewClient(client) + var hSet *hostsets.HostSet + hSet, apiErr, err = hSetClient.Create(tc.Context(), hc.Id) + require.NoError(err) + require.Nil(apiErr) - if newStyle { - hSet, apiErr, err = hSetClient.AddHosts2(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id, h2.Id}) - } else { - hSet, apiErr, err = hSetClient.AddHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id, h2.Id}) - } - require.NoError(err) - require.Nil(apiErr) - assert.Contains(hSet.HostIds, h1.Id, h2.Id) + hSet, apiErr, err = hSetClient.AddHosts(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id, h2.Id}) + require.NoError(err) + require.Nil(apiErr) + assert.Contains(hSet.HostIds, h1.Id, h2.Id) - if newStyle { - hSet, apiErr, err = hSetClient.SetHosts2(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id}) - } else { - hSet, apiErr, err = hSetClient.SetHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id}) - } - require.NoError(err) - require.Nil(apiErr, pretty.Sprint(apiErr)) - assert.ElementsMatch([]string{h1.Id}, hSet.HostIds) + hSet, apiErr, err = hSetClient.SetHosts(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id}) + require.NoError(err) + require.Nil(apiErr, pretty.Sprint(apiErr)) + assert.ElementsMatch([]string{h1.Id}, hSet.HostIds) - if newStyle { - hSet, apiErr, err = hSetClient.RemoveHosts2(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id}) - } else { - hSet, apiErr, err = hSetClient.RemoveHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id}) - } - require.NoError(err) - require.Nil(apiErr) - assert.Empty(hSet.HostIds) - }) - } + hSet, apiErr, err = hSetClient.RemoveHosts(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id}) + require.NoError(err) + require.Nil(apiErr) + assert.Empty(hSet.HostIds) } -func TestSet_List(t *testing.T) { - for _, newStyle := range []bool{false, true} { - assert, require := assert.New(t), require.New(t) - tc := controller.NewTestController(t, nil) - defer tc.Shutdown() +func TestList(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() - _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - client.SetScopeId(proj.GetPublicId()) + client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static") - require.NoError(err) - require.Nil(apiErr) - require.NotNil(hc) + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(hc) - hClient := hostsets.NewClient(client) - var ul []*hostsets.HostSet + hClient := hostsets.NewClient(client) + var ul []*hostsets.HostSet - if newStyle { - ul, apiErr, err = hClient.List2(tc.Context(), hc.Id) - } else { - ul, apiErr, err = hClient.List(tc.Context(), hc.Id) - } - require.NoError(err) - require.Nil(apiErr) - assert.Empty(ul) + ul, apiErr, err = hClient.List(tc.Context(), hc.Id) + require.NoError(err) + require.Nil(apiErr) + assert.Empty(ul) - var expected []*hostsets.HostSet - for i := 0; i < 10; i++ { - expected = append(expected, &hostsets.HostSet{Name: fmt.Sprint(i)}) - } - - expected[0], apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName(expected[0].Name)) - require.NoError(err) - require.Nil(apiErr) - - if newStyle { - ul, apiErr, err = hClient.List2(tc.Context(), hc.Id) - } else { - ul, apiErr, err = hClient.List(tc.Context(), hc.Id) - } - require.NoError(err) - require.Nil(apiErr) - assert.ElementsMatch(comparableSetSlice(expected[:1]), comparableSetSlice(ul)) - - for i := 1; i < 10; i++ { - expected[i], apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName(expected[i].Name)) - require.NoError(err) - require.Nil(apiErr) - } - if newStyle { - ul, apiErr, err = hClient.List2(tc.Context(), hc.Id) - } else { - ul, apiErr, err = hClient.List(tc.Context(), hc.Id) - } - require.NoError(err) - require.Nil(apiErr) - assert.ElementsMatch(comparableSetSlice(expected), comparableSetSlice(ul)) + var expected []*hostsets.HostSet + for i := 0; i < 10; i++ { + expected = append(expected, &hostsets.HostSet{Name: fmt.Sprint(i)}) } + + expected[0], apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName(expected[0].Name)) + require.NoError(err) + require.Nil(apiErr) + + ul, apiErr, err = hClient.List(tc.Context(), hc.Id) + require.NoError(err) + require.Nil(apiErr) + assert.ElementsMatch(comparableSetSlice(expected[:1]), comparableSetSlice(ul)) + + for i := 1; i < 10; i++ { + expected[i], apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName(expected[i].Name)) + require.NoError(err) + require.Nil(apiErr) + } + ul, apiErr, err = hClient.List(tc.Context(), hc.Id) + require.NoError(err) + require.Nil(apiErr) + assert.ElementsMatch(comparableSetSlice(expected), comparableSetSlice(ul)) } func comparableSetSlice(in []*hostsets.HostSet) []hostsets.HostSet { @@ -158,146 +124,97 @@ func comparableSetSlice(in []*hostsets.HostSet) []hostsets.HostSet { return filtered } -func TestSet_Crud(t *testing.T) { - for _, newStyle := range []bool{false, true} { - t.Run(fmt.Sprintf("crud_%t", newStyle), func(t *testing.T) { - assert, require := assert.New(t), require.New(t) - tc := controller.NewTestController(t, nil) - defer tc.Shutdown() +func TestCrud(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() - org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - hc, apiErr, err := hostcatalogs.NewClient(projClient).Create(tc.Context(), "static") - require.NoError(err) - require.Nil(apiErr) - require.NotNil(hc) + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(hc) - checkHost := func(t *testing.T, step string, h *hostsets.HostSet, apiErr *api.Error, err error, wantedName string, wantVersion uint32) { - t.Helper() - require.NoError(err, step) - if !assert.Nil(apiErr, step) && apiErr.Message != "" { - t.Errorf("ApiError message: %q", apiErr.Message) - } - assert.NotNil(h, "returned no resource", step) - gotName := "" - if h.Name != "" { - gotName = h.Name - } - assert.Equal(wantedName, gotName, step) - assert.Equal(wantVersion, h.Version) - } - - hClient := hostsets.NewClient(projClient) - - var h *hostsets.HostSet - if newStyle { - h, apiErr, err = hClient.Create2(tc.Context(), hc.Id, hostsets.WithName("foo")) - } else { - h, apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName("foo")) - } - checkHost(t, "create", h, apiErr, err, "foo", 1) - - if newStyle { - h, apiErr, err = hClient.Read2(tc.Context(), h.Id) - } else { - h, apiErr, err = hClient.Read(tc.Context(), hc.Id, h.Id) - } - checkHost(t, "read", h, apiErr, err, "foo", 1) - - if newStyle { - h, apiErr, err = hClient.Update2(tc.Context(), h.Id, h.Version, hostsets.WithName("bar")) - } else { - h, apiErr, err = hClient.Update(tc.Context(), hc.Id, h.Id, h.Version, hostsets.WithName("bar")) - } - checkHost(t, "update", h, apiErr, err, "bar", 2) - - if newStyle { - h, apiErr, err = hClient.Update2(tc.Context(), h.Id, h.Version, hostsets.DefaultName()) - } else { - h, apiErr, err = hClient.Update(tc.Context(), hc.Id, h.Id, h.Version, hostsets.DefaultName()) - } - checkHost(t, "update", h, apiErr, err, "", 3) - - var existed bool - if newStyle { - existed, apiErr, err = hClient.Delete2(tc.Context(), h.Id) - } else { - existed, apiErr, err = hClient.Delete(tc.Context(), hc.Id, h.Id) - } - assert.NoError(err) - assert.True(existed, "Expected existing catalog when deleted, but it wasn't.") - - if newStyle { - existed, apiErr, err = hClient.Delete2(tc.Context(), h.Id) - } else { - existed, apiErr, err = hClient.Delete(tc.Context(), hc.Id, h.Id) - } - require.NoError(err) - assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) - }) + checkHost := func(t *testing.T, step string, h *hostsets.HostSet, apiErr *api.Error, err error, wantedName string, wantVersion uint32) { + t.Helper() + require.NoError(err, step) + if !assert.Nil(apiErr, step) && apiErr.Message != "" { + t.Errorf("ApiError message: %q", apiErr.Message) + } + assert.NotNil(h, "returned no resource", step) + gotName := "" + if h.Name != "" { + gotName = h.Name + } + assert.Equal(wantedName, gotName, step) + assert.Equal(wantVersion, h.Version) } + + hClient := hostsets.NewClient(client) + + var h *hostsets.HostSet + h, apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName("foo")) + checkHost(t, "create", h, apiErr, err, "foo", 1) + + h, apiErr, err = hClient.Read(tc.Context(), h.Id) + checkHost(t, "read", h, apiErr, err, "foo", 1) + + h, apiErr, err = hClient.Update(tc.Context(), h.Id, h.Version, hostsets.WithName("bar")) + checkHost(t, "update", h, apiErr, err, "bar", 2) + + h, apiErr, err = hClient.Update(tc.Context(), h.Id, h.Version, hostsets.DefaultName()) + checkHost(t, "update", h, apiErr, err, "", 3) + + var existed bool + existed, apiErr, err = hClient.Delete(tc.Context(), h.Id) + assert.NoError(err) + assert.True(existed, "Expected existing catalog when deleted, but it wasn't.") + existed, apiErr, err = hClient.Delete(tc.Context(), h.Id) + require.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusForbidden, apiErr.Status) } // TODO: Get better coverage for expected errors and error formats. -func TestSet_Errors(t *testing.T) { - for _, newStyle := range []bool{false, true} { - assert, require := assert.New(t), require.New(t) - tc := controller.NewTestController(t, nil) - defer tc.Shutdown() +func TestErrors(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() - _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - client.SetScopeId(proj.GetPublicId()) + client := tc.Client() + token := tc.Token() + client.SetToken(token.Token) + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static") - require.NoError(err) - require.Nil(apiErr) - require.NotNil(hc) + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(hc) - hClient := hostsets.NewClient(client) + hClient := hostsets.NewClient(client) - var h *hostsets.HostSet - if newStyle { - h, apiErr, err = hClient.Create2(tc.Context(), hc.Id, hostsets.WithName("foo")) - } else { - h, apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName("foo")) - } - require.NoError(err) - require.Nil(apiErr) - assert.NotNil(h) + var h *hostsets.HostSet + h, apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName("foo")) + require.NoError(err) + require.Nil(apiErr) + assert.NotNil(h) - if newStyle { - h, apiErr, err = hClient.Create2(tc.Context(), hc.Id, hostsets.WithName("foo")) - } else { - h, apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName("foo")) - } - require.NoError(err) - assert.NotNil(apiErr) - assert.Nil(h) + h, apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName("foo")) + require.NoError(err) + assert.NotNil(apiErr) + assert.Nil(h) - if newStyle { - _, apiErr, err = hClient.Read2(tc.Context(), static.HostSetPrefix+"_doesntexis") - } else { - _, apiErr, err = hClient.Read(tc.Context(), hc.Id, static.HostSetPrefix+"_doesntexis") - } - require.NoError(err) - assert.NotNil(apiErr) - assert.EqualValues(http.StatusForbidden, apiErr.Status) + _, apiErr, err = hClient.Read(tc.Context(), static.HostSetPrefix+"_doesntexis") + require.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusForbidden, apiErr.Status) - if newStyle { - _, apiErr, err = hClient.Read2(tc.Context(), "invalid id") - } else { - _, apiErr, err = hClient.Read(tc.Context(), hc.Id, "invalid id") - } - require.NoError(err) - assert.NotNil(apiErr) - assert.EqualValues(http.StatusBadRequest, apiErr.Status) - } + _, apiErr, err = hClient.Read(tc.Context(), "invalid id") + require.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) } diff --git a/api/hostsets/option.gen.go b/api/hostsets/option.gen.go index 40d762ac1..12f651cfb 100644 --- a/api/hostsets/option.gen.go +++ b/api/hostsets/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,25 +32,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/internal/genapi/input.go b/api/internal/genapi/input.go index d0833710c..53d9c37e4 100644 --- a/api/internal/genapi/input.go +++ b/api/internal/genapi/input.go @@ -47,6 +47,10 @@ type structInfo struct { // the attributes map. subtypeName string + // For non-top-level collections, this can be used to indicate the name of + // the argument that should be used + parentTypeName string + // mappings of names of resources and param names for sub slice types, e.g. // role principals and group members sliceSubTypes map[string]string @@ -76,10 +80,6 @@ type structInfo struct { // given type, e.g. arguments only valid for one call or purpose and not // conveyed within the item itself extraOptions []fieldInfo - - useNewStyle bool - - disableOldStyle bool } var inputStructs = []*structInfo{ @@ -231,7 +231,8 @@ var inputStructs = []*structInfo{ deleteTemplate, listTemplate, }, - pathArgs: []string{"auth-method", "account"}, + pathArgs: []string{"account"}, + parentTypeName: "auth-method", versionEnabled: true, }, { @@ -278,7 +279,8 @@ var inputStructs = []*structInfo{ deleteTemplate, listTemplate, }, - pathArgs: []string{"host-catalog", "host"}, + pathArgs: []string{"host"}, + parentTypeName: "host-catalog", versionEnabled: true, }, { @@ -296,18 +298,13 @@ var inputStructs = []*structInfo{ updateTemplate, deleteTemplate, listTemplate, - createTemplate2, - readTemplate2, - updateTemplate2, - deleteTemplate2, - listTemplate2, }, - pathArgs: []string{"host-catalog", "host-set"}, + pathArgs: []string{"host-set"}, + parentTypeName: "host-catalog", sliceSubTypes: map[string]string{ "Hosts": "hostIds", }, versionEnabled: true, - useNewStyle: true, }, { inProto: &targets.HostSet{}, @@ -318,19 +315,17 @@ var inputStructs = []*structInfo{ outFile: "targets/target.gen.go", templates: []*template.Template{ clientTemplate, - createTemplate2, - readTemplate2, - updateTemplate2, - deleteTemplate2, - listTemplate2, + createTemplate, + readTemplate, + updateTemplate, + deleteTemplate, + listTemplate, }, pathArgs: []string{"target"}, sliceSubTypes: map[string]string{ "HostSets": "hostSetIds", }, - versionEnabled: true, - typeOnCreate: true, - useNewStyle: true, - disableOldStyle: true, + versionEnabled: true, + typeOnCreate: true, }, } diff --git a/api/internal/genapi/templates.go b/api/internal/genapi/templates.go index 343344c36..cfafd2022 100644 --- a/api/internal/genapi/templates.go +++ b/api/internal/genapi/templates.go @@ -39,32 +39,17 @@ func toPath(segments []string, action string) string { return fmt.Sprintf("fmt.Sprintf(\"%s%s\", %s)", strings.Join(printfString, "/"), action, strings.Join(printfArg, ", ")) } -func getArgsAndPaths(in []string, action string) (colArgs, resArgs []string, colPath, resPath string) { - var argNames, pathSegment []string - for _, s := range in { - varName := fmt.Sprintf("%sId", strcase.ToLowerCamel(strings.ReplaceAll(s, "-", "_"))) - collectionName := fmt.Sprintf("%ss", s) - - argNames = append(argNames, varName) - pathSegment = append(pathSegment, collectionName, varName) - } - - colArgs, resArgs, colPath, resPath = argNames[:len(argNames)-1], argNames, toPath(pathSegment[:len(pathSegment)-1], action), toPath(pathSegment, action) - - // Scopes create and list operations always need scope ID - if colPath == `"scopes"` { - colArgs = append(colArgs, "scopeId") - } - return -} - -func getArgsAndPathsNewStyle(in []string, action string) (colArg, resArg string, colPath, resPath string) { +func getArgsAndPaths(in []string, parentTypeName, action string) (colArg, resArg string, colPath, resPath string) { resArg = fmt.Sprintf("%sId", strcase.ToLowerCamel(strings.ReplaceAll(in[len(in)-1], "-", "_"))) + strToReplace := "scope" if len(in) == 1 { - colArg = "scopeId" + if parentTypeName != "" { + strToReplace = parentTypeName + } } else { - colArg = fmt.Sprintf("%sId", strcase.ToLowerCamel(strings.ReplaceAll(in[len(in)-2], "-", "_"))) + strToReplace = in[len(in)-2] } + colArg = fmt.Sprintf("%sId", strcase.ToLowerCamel(strings.ReplaceAll(strToReplace, "-", "_"))) colPath = fmt.Sprintf("%ss", in[len(in)-1]) if action != "" { @@ -75,23 +60,19 @@ func getArgsAndPathsNewStyle(in []string, action string) (colArg, resArg string, } type templateInput struct { - Name string - Package string - Fields []fieldInfo - PathArgs []string - CollectionFunctionArgs []string - ResourceFunctionArgs []string - CollectionPath string - ResourcePath string - CollectionFunctionArg2 string - ResourceFunctionArg2 string - CollectionPath2 string - ResourcePath2 string - SliceSubTypes map[string]string - ExtraOptions []fieldInfo - VersionEnabled bool - TypeOnCreate bool - Two string + Name string + Package string + Fields []fieldInfo + PathArgs []string + CollectionFunctionArg string + ResourceFunctionArg string + CollectionPath string + ResourcePath string + ParentTypeName string + SliceSubTypes map[string]string + ExtraOptions []fieldInfo + VersionEnabled bool + TypeOnCreate bool } func fillTemplates() { @@ -103,19 +84,14 @@ func fillTemplates() { Package: in.generatedStructure.pkg, Fields: in.generatedStructure.fields, PathArgs: in.pathArgs, + ParentTypeName: in.parentTypeName, ExtraOptions: in.extraOptions, VersionEnabled: in.versionEnabled, TypeOnCreate: in.typeOnCreate, - Two: "2", - } - - if in.useNewStyle && in.disableOldStyle { - input.Two = "" } if len(in.pathArgs) > 0 { - input.CollectionFunctionArgs, input.ResourceFunctionArgs, input.CollectionPath, input.ResourcePath = getArgsAndPaths(in.pathArgs, "") - input.CollectionFunctionArg2, input.ResourceFunctionArg2, input.CollectionPath2, input.ResourcePath2 = getArgsAndPathsNewStyle(in.pathArgs, "") + input.CollectionFunctionArg, input.ResourceFunctionArg, input.CollectionPath, input.ResourcePath = getArgsAndPaths(in.pathArgs, in.parentTypeName, "") } if err := structTemplate.Execute(outBuf, input); err != nil { @@ -125,12 +101,7 @@ func fillTemplates() { if len(in.sliceSubTypes) > 0 { input.SliceSubTypes = in.sliceSubTypes - if !in.disableOldStyle { - in.templates = append(in.templates, sliceSubTypeTemplate) - } - if in.useNewStyle { - in.templates = append(in.templates, sliceSubTypeTemplate2) - } + in.templates = append(in.templates, sliceSubTypeTemplate) } for _, t := range in.templates { @@ -229,24 +200,23 @@ func fillTemplates() { } } -var listTemplate2 = template.Must(template.New("").Funcs( +var listTemplate = template.Must(template.New("").Funcs( template.FuncMap{ "snakeCase": snakeCase, }, ).Parse(` -func (c *Client) List{{ .Two }}(ctx context.Context, {{ .CollectionFunctionArg2 }} string, opt... Option) ([]*{{ .Name }}, *api.Error, error) { - if {{ .CollectionFunctionArg2 }} == "" { - return nil, nil, fmt.Errorf("empty {{ .CollectionFunctionArg2 }} value passed into List request") +func (c *Client) List(ctx context.Context, {{ .CollectionFunctionArg }} string, opt... Option) ([]*{{ .Name }}, *api.Error, error) { + if {{ .CollectionFunctionArg }} == "" { + return nil, nil, fmt.Errorf("empty {{ .CollectionFunctionArg }} value passed into List request") } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) - opts.queryMap["{{ snakeCase .CollectionFunctionArg2 }}"] = {{ .CollectionFunctionArg2 }} + opts.queryMap["{{ snakeCase .CollectionFunctionArg }}"] = {{ .CollectionFunctionArg }} - req, err := c.client.NewRequest(ctx, "GET", "{{ .CollectionPath2 }}", nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "GET", "{{ .CollectionPath }}", nil, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating List request: %w", err) } @@ -281,101 +251,11 @@ func (c *Client) List{{ .Two }}(ctx context.Context, {{ .CollectionFunctionArg2 } `)) -var listTemplate = template.Must(template.New("").Parse(` -func (c *Client) List(ctx context.Context, {{ range .CollectionFunctionArgs }} {{ . }} string, {{ end }}opt... Option) ([]*{{ .Name }}, *api.Error, error) { {{ range .CollectionFunctionArgs }} - if {{ . }} == "" { - return nil, nil, fmt.Errorf("empty {{ . }} value passed into List request") - } - {{ end }} - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - - req, err := c.client.NewRequest(ctx, "GET", {{ .CollectionPath }}, nil, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating List request: %w", err) - } - {{ if ( eq .CollectionPath "\"scopes\"" ) }} - opts.queryMap["scope_id"] = scopeId - {{ end }} - 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, nil, fmt.Errorf("error performing client request during List call: %w", err) - } - - type listResponse struct { - Items []*{{ .Name }} - } - target := &listResponse{} - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding List response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target.Items, apiErr, nil -} -`)) - -var readTemplate2 = template.Must(template.New("").Parse(` -func (c *Client) Read{{ .Two }}(ctx context.Context, {{ .ResourceFunctionArg2 }} string, opt... Option) (*{{ .Name }}, *api.Error, error) { - if {{ .ResourceFunctionArg2 }} == "" { - return nil, nil, fmt.Errorf("empty {{ .ResourceFunctionArg2 }} value passed into Read request") - } - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) - - req, err := c.client.NewRequest(ctx, "GET", {{ .ResourcePath2 }}, nil, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating Read 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, nil, fmt.Errorf("error performing client request during Read call: %w", err) - } - - target := new({{ .Name }}) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding Read response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} -`)) - var readTemplate = template.Must(template.New("").Parse(` -func (c *Client) Read(ctx context.Context, {{ range .ResourceFunctionArgs }} {{ . }} string, {{ end }} opt... Option) (*{{ .Name }}, *api.Error, error) { {{ range .ResourceFunctionArgs }} - if {{ . }} == "" { - return nil, nil, fmt.Errorf("empty {{ . }} value passed into Read request") +func (c *Client) Read(ctx context.Context, {{ .ResourceFunctionArg }} string, opt... Option) (*{{ .Name }}, *api.Error, error) { + if {{ .ResourceFunctionArg }} == "" { + return nil, nil, fmt.Errorf("empty {{ .ResourceFunctionArg }} value passed into Read request") } - {{ end }} if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -412,57 +292,11 @@ func (c *Client) Read(ctx context.Context, {{ range .ResourceFunctionArgs }} {{ } `)) -var deleteTemplate2 = template.Must(template.New("").Parse(` -func (c *Client) Delete{{ .Two }}(ctx context.Context, {{ .ResourceFunctionArg2 }} string, opt... Option) (bool, *api.Error, error) { - if {{ .ResourceFunctionArg2 }} == "" { - return false, nil, fmt.Errorf("empty {{ .ResourceFunctionArg2 }} value passed into Delete request") - } - if c.client == nil { - return false, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) - - req, err := c.client.NewRequest(ctx, "DELETE", {{ .ResourcePath2 }}, nil, apiOpts...) - if err != nil { - return false, nil, fmt.Errorf("error creating Delete 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 false, nil, fmt.Errorf("error performing client request during Delete call: %w", err) - } - - type deleteResponse struct { - Existed bool - } - target := &deleteResponse{} - apiErr, err := resp.Decode(target) - if err != nil { - return false, nil, fmt.Errorf("error decoding Delete response: %w", err) - } - if apiErr != nil { - return false, apiErr, nil - } - return target.Existed, apiErr, nil -} -`)) - var deleteTemplate = template.Must(template.New("").Parse(` -func (c *Client) Delete(ctx context.Context, {{ range .ResourceFunctionArgs }} {{ . }} string, {{ end }} opt... Option) (bool, *api.Error, error) { {{ range .ResourceFunctionArgs }} - if {{ . }} == "" { - return false, nil, fmt.Errorf("empty {{ . }} value passed into Delete request") +func (c *Client) Delete(ctx context.Context, {{ .ResourceFunctionArg }} string, opt... Option) (bool, *api.Error, error) { + if {{ .ResourceFunctionArg }} == "" { + return false, nil, fmt.Errorf("empty {{ .ResourceFunctionArg }} value passed into Delete request") } - {{ end }} if c.client == nil { return false, nil, fmt.Errorf("nil client") } @@ -502,17 +336,18 @@ func (c *Client) Delete(ctx context.Context, {{ range .ResourceFunctionArgs }} { } `)) -var createTemplate2 = template.Must(template.New("").Funcs( +var createTemplate = template.Must(template.New("").Funcs( template.FuncMap{ "snakeCase": snakeCase, }, ).Parse(` -func (c *Client) Create{{ .Two }} (ctx context.Context, {{ if .TypeOnCreate }} resourceType string, {{ end }} {{ .CollectionFunctionArg2 }} string, opt... Option) (*{{ .Name }}, *api.Error, error) { - if {{ .CollectionFunctionArg2 }} == "" { - return nil, nil, fmt.Errorf("empty {{ .CollectionFunctionArg2 }} value passed into Create request") +func (c *Client) Create (ctx context.Context, {{ if .TypeOnCreate }} resourceType string, {{ end }} {{ .CollectionFunctionArg }} string, opt... Option) (*{{ .Name }}, *api.Error, error) { + if {{ .CollectionFunctionArg }} == "" { + return nil, nil, fmt.Errorf("empty {{ .CollectionFunctionArg }} value passed into Create request") } + opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -522,9 +357,9 @@ func (c *Client) Create{{ .Two }} (ctx context.Context, {{ if .TypeOnCreate }} r opts.postMap["type"] = resourceType }{{ end }} - opts.postMap["{{ snakeCase .CollectionFunctionArg2 }}"] = {{ .CollectionFunctionArg2 }} + opts.postMap["{{ snakeCase .CollectionFunctionArg }}"] = {{ .CollectionFunctionArg }} - req, err := c.client.NewRequest(ctx, "POST", "{{ .CollectionPath2 }}", opts.postMap, apiOpts...) + req, err := c.client.NewRequest(ctx, "POST", "{{ .CollectionPath }}", opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } @@ -556,121 +391,11 @@ func (c *Client) Create{{ .Two }} (ctx context.Context, {{ if .TypeOnCreate }} r } `)) -var createTemplate = template.Must(template.New("").Parse(` -func (c *Client) Create(ctx context.Context, {{ if .TypeOnCreate }} resourceType string, {{ end }} {{ range .CollectionFunctionArgs }} {{ . }} string, {{ end }} opt... Option) (*{{ .Name }}, *api.Error, error) { {{ range .CollectionFunctionArgs }} - if {{ . }} == "" { - return nil, nil, fmt.Errorf("empty {{ . }} value passed into Create request") - } - {{ end }}opts, apiOpts := getOpts(opt...) - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - {{ if .TypeOnCreate }} if resourceType == "" { - return nil, nil, fmt.Errorf("empty resourceType value passed into Create request") - } else { - opts.postMap["type"] = resourceType - }{{ end }} - {{ if ( eq .CollectionPath "\"scopes\"" ) }} - opts.postMap["scope_id"] = scopeId - {{ end }} - - req, err := c.client.NewRequest(ctx, "POST", {{ .CollectionPath }}, opts.postMap, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating Create 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, nil, fmt.Errorf("error performing client request during Create call: %w", err) - } - - target := new({{ .Name }}) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding Create response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} -`)) - -var updateTemplate2 = template.Must(template.New("").Parse(` -func (c *Client) Update{{ .Two }}(ctx context.Context, {{ .ResourceFunctionArg2 }} string, version uint32, opt... Option) (*{{ .Name }}, *api.Error, error) { - if {{ .ResourceFunctionArg2 }} == "" { - return nil, nil, fmt.Errorf("empty {{ .ResourceFunctionArg2 }} value passed into Update request") - } - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) - - {{ if .VersionEnabled }} - if version == 0 { - if !opts.withAutomaticVersioning { - return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") - } - existingTarget, existingApiErr, existingErr := c.Read{{ .Two }}(ctx, {{ .ResourceFunctionArg2 }}, opt...) - if existingErr != nil { - return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) - } - if existingApiErr != nil { - return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) - } - if existingTarget == nil { - return nil, nil, errors.New("nil resource found when performing initial check-and-set read") - } - version = existingTarget.Version - } - {{ end }} - - opts.postMap["version"] = version - - req, err := c.client.NewRequest(ctx, "PATCH", {{ .ResourcePath2 }}, opts.postMap, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating Update 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, nil, fmt.Errorf("error performing client request during Update call: %w", err) - } - - target := new({{ .Name }}) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding Update response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} -`)) - var updateTemplate = template.Must(template.New("").Parse(` -func (c *Client) Update(ctx context.Context, {{ range .ResourceFunctionArgs }} {{ . }} string, {{ end }}version uint32, opt... Option) (*{{ .Name }}, *api.Error, error) { {{ range .ResourceFunctionArgs }} - if {{ . }} == "" { - return nil, nil, fmt.Errorf("empty {{ . }} value passed into Update request") - }{{ end }} +func (c *Client) Update(ctx context.Context, {{ .ResourceFunctionArg }} string, version uint32, opt... Option) (*{{ .Name }}, *api.Error, error) { + if {{ .ResourceFunctionArg }} == "" { + return nil, nil, fmt.Errorf("empty {{ .ResourceFunctionArg }} value passed into Update request") + } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -682,7 +407,7 @@ func (c *Client) Update(ctx context.Context, {{ range .ResourceFunctionArgs }} { if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") } - existingTarget, existingApiErr, existingErr := c.Read(ctx, {{ range .ResourceFunctionArgs }} {{ . }}, {{ end }} opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, {{ .ResourceFunctionArg }}, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -741,11 +466,11 @@ var sliceSubTypeTemplate = template.Must(template.New("").Funcs( {{ range $key, $value := $input.SliceSubTypes }} {{ $fullName := print $op $key }} {{ $actionName := kebabCase $fullName }} -{{ $resPath := getPathWithAction $input.PathArgs $actionName }} -func (c *Client) {{ $fullName }}(ctx context.Context, {{ range $input.ResourceFunctionArgs }} {{ . }} string, {{ end }}version uint32, {{ $value }} []string, opt... Option) (*{{ $input.Name }}, *api.Error, error) { {{ range $input.ResourceFunctionArgs }} - if {{ . }} == "" { - return nil, nil, fmt.Errorf("empty {{ . }} value passed into {{ $fullName }} request") - }{{ end }} +{{ $resPath := getPathWithAction $input.PathArgs $input.ParentTypeName $actionName }} +func (c *Client) {{ $fullName }}(ctx context.Context, {{ $input.ResourceFunctionArg }} string, version uint32, {{ $value }} []string, opt... Option) (*{{ $input.Name }}, *api.Error, error) { + if {{ $input.ResourceFunctionArg }} == "" { + return nil, nil, fmt.Errorf("empty {{ $input.ResourceFunctionArg }} value passed into {{ $fullName }} request") + } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -757,92 +482,7 @@ func (c *Client) {{ $fullName }}(ctx context.Context, {{ range $input.ResourceFu if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into {{ $fullName }} request") } - existingTarget, existingApiErr, existingErr := c.Read(ctx, {{ range $input.ResourceFunctionArgs }} {{ . }}, {{ end }} opt...) - if existingErr != nil { - return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) - } - if existingApiErr != nil { - return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) - } - if existingTarget == nil { - return nil, nil, errors.New("nil resource found when performing initial check-and-set read") - } - version = existingTarget.Version - } - {{ end }} - opts.postMap["version"] = version - - if len({{ $value }}) > 0 { - opts.postMap["{{ snakeCase $value }}"] = {{ $value }} - }{{ if ( eq $op "Set" ) }} else if {{ $value }} != nil { - // In this function, a non-nil but empty list means clear out - opts.postMap["{{ snakeCase $value }}"] = nil - } - {{ end }} - - req, err := c.client.NewRequest(ctx, "POST", {{ $resPath }}, opts.postMap, apiOpts...) - if err != nil { - return nil, nil, fmt.Errorf("error creating {{ $fullName }} 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, nil, fmt.Errorf("error performing client request during {{ $fullName }} call: %w", err) - } - - target := new({{ $input.Name }}) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding {{ $fullName }} response: %w", err) - } - if apiErr != nil { - return nil, apiErr, nil - } - return target, apiErr, nil -} -{{ end }} -{{ end }} -`)) - -var sliceSubTypeTemplate2 = template.Must(template.New("").Funcs( - template.FuncMap{ - "makeSlice": makeSlice, - "snakeCase": snakeCase, - "kebabCase": kebabCase, - "getPathWithActionNewStyle": getPathWithActionNewStyle, - }, -).Parse(` -{{ $input := . }} -{{ range $index, $op := makeSlice "Add" "Set" "Remove" }} -{{ range $key, $value := $input.SliceSubTypes }} -{{ $fullName := print $op $key }} -{{ $actionName := kebabCase $fullName }} -{{ $resPath := getPathWithActionNewStyle $input.PathArgs $actionName }} -func (c *Client) {{ $fullName }}{{ $input.Two }}(ctx context.Context, {{ $input.ResourceFunctionArg2 }} string, version uint32, {{ $value }} []string, opt... Option) (*{{ $input.Name }}, *api.Error, error) { - if {{ $input.ResourceFunctionArg2 }} == "" { - return nil, nil, fmt.Errorf("empty {{ $input.ResourceFunctionArg2 }} value passed into {{ $fullName }} request") - } - if c.client == nil { - return nil, nil, fmt.Errorf("nil client") - } - - opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) - - {{ if $input.VersionEnabled }} - if version == 0 { - if !opts.withAutomaticVersioning { - return nil, nil, errors.New("zero version number passed into {{ $fullName }} request") - } - existingTarget, existingApiErr, existingErr := c.Read{{ $input.Two }}(ctx, {{ $input.ResourceFunctionArg2 }}, opt...) + existingTarget, existingApiErr, existingErr := c.Read(ctx, {{ $input.ResourceFunctionArg }}, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -918,12 +558,22 @@ type {{ .Name }} struct { {{ range .Fields }} `))) var clientTemplate = template.Must(template.New("").Parse(` +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{ client: c } + return &Client{ client: c.Clone() } +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client } `)) @@ -950,7 +600,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -967,25 +616,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } {{ range .Fields }} @@ -1033,12 +673,7 @@ func kebabCase(in string) string { return strcase.ToKebab(in) } -func getPathWithAction(resArgs []string, action string) string { - _, _, _, resPath := getArgsAndPaths(resArgs, action) - return resPath -} - -func getPathWithActionNewStyle(resArgs []string, action string) string { - _, _, _, resPath := getArgsAndPathsNewStyle(resArgs, action) +func getPathWithAction(resArgs []string, parentTypeName, action string) string { + _, _, _, resPath := getArgsAndPaths(resArgs, parentTypeName, action) return resPath } diff --git a/api/options.go b/api/options.go index 943153f3d..e18996c39 100644 --- a/api/options.go +++ b/api/options.go @@ -13,24 +13,8 @@ type Option func(*options) // options = how options are represented type options struct { - withScopeId string - withNewStyle bool } func getDefaultOptions() options { - return options{ - withScopeId: "", - } -} - -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - -func WithNewStyle() Option { - return func(o *options) { - o.withNewStyle = true - } + return options{} } diff --git a/api/roles/option.gen.go b/api/roles/option.gen.go index abbf72041..fd1d72b6f 100644 --- a/api/roles/option.gen.go +++ b/api/roles/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,25 +32,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/roles/role.gen.go b/api/roles/role.gen.go index 036d3ea8e..acdf6c65a 100644 --- a/api/roles/role.gen.go +++ b/api/roles/role.gen.go @@ -30,24 +30,42 @@ type Role struct { Grants []*Grant `json:"grants,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} } -func (c *Client) Create(ctx context.Context, opt ...Option) (*Role, *api.Error, error) { +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client +} + +func (c *Client) Create(ctx context.Context, scopeId string, opt ...Option) (*Role, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into Create request") + } + opts, apiOpts := getOpts(opt...) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } + opts.postMap["scope_id"] = scopeId + req, err := c.client.NewRequest(ctx, "POST", "roles", opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } + if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { @@ -74,9 +92,8 @@ func (c *Client) Create(ctx context.Context, opt ...Option) (*Role, *api.Error, func (c *Client) Read(ctx context.Context, roleId string, opt ...Option) (*Role, *api.Error, error) { if roleId == "" { - return nil, nil, fmt.Errorf("empty roleId value passed into Read request") + return nil, nil, fmt.Errorf("empty roleId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -174,7 +191,6 @@ func (c *Client) Delete(ctx context.Context, roleId string, opt ...Option) (bool if roleId == "" { return false, nil, fmt.Errorf("empty roleId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } @@ -213,12 +229,16 @@ func (c *Client) Delete(ctx context.Context, roleId string, opt ...Option) (bool return target.Existed, apiErr, nil } -func (c *Client) List(ctx context.Context, opt ...Option) ([]*Role, *api.Error, error) { +func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) ([]*Role, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into List request") + } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) + opts.queryMap["scope_id"] = scopeId req, err := c.client.NewRequest(ctx, "GET", "roles", nil, apiOpts...) if err != nil { diff --git a/api/roles/role_test.go b/api/roles/role_test.go index db5966458..9048f4b15 100644 --- a/api/roles/role_test.go +++ b/api/roles/role_test.go @@ -18,36 +18,29 @@ import ( func TestCustom(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) cases := []struct { - name string - scopeClient *api.Client + name string + scopeId string }{ { - name: "org", - scopeClient: client, + name: "org", + scopeId: org.GetPublicId(), }, { - name: "proj", - scopeClient: projClient, + name: "proj", + scopeId: proj.GetPublicId(), }, } - user, apiErr, err := users.NewClient(client).Create(tc.Context()) + user, apiErr, err := users.NewClient(client).Create(tc.Context(), org.GetPublicId()) require.NoError(err) require.Nil(apiErr) @@ -69,15 +62,15 @@ func TestCustom(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - g, apiErr, err := groups.NewClient(tt.scopeClient).Create(tc.Context()) + g, apiErr, err := groups.NewClient(client).Create(tc.Context(), tt.scopeId) require.NoError(err) require.Nil(apiErr) require.NotNil(g) - rc := roles.NewClient(tt.scopeClient) + rc := roles.NewClient(client) var version uint32 = 1 - r, apiErr, err := rc.Create(tc.Context(), roles.WithName("foo")) + r, apiErr, err := rc.Create(tc.Context(), tt.scopeId, roles.WithName("foo")) require.NoError(err) require.Nil(apiErr) require.NotNil(r) @@ -129,65 +122,60 @@ func TestCustom(t *testing.T) { } } -func TestRole_List(t *testing.T) { +func TestList(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) cases := []struct { - name string - scopeClient *api.Client + name string + scopeId string }{ { - name: "org", - scopeClient: client, + name: "org", + scopeId: org.GetPublicId(), }, { - name: "proj", - scopeClient: projClient, + name: "proj", + scopeId: proj.GetPublicId(), }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - roleClient := roles.NewClient(tt.scopeClient) - p1, apiErr, err := roleClient.List(tc.Context()) + var expected []*roles.Role + + roleClient := roles.NewClient(client) + p1, apiErr, err := roleClient.List(tc.Context(), tt.scopeId) require.NoError(err) assert.Nil(apiErr) - require.Len(p1, 0) + require.Len(p1, 1) + expected = append(expected, p1[0]) - var expected []*roles.Role - for i := 0; i < 10; i++ { + for i := 1; i < 11; i++ { expected = append(expected, &roles.Role{Name: fmt.Sprint(i)}) } - expected[0], apiErr, err = roleClient.Create(tc.Context(), roles.WithName(expected[0].Name)) + expected[1], apiErr, err = roleClient.Create(tc.Context(), tt.scopeId, roles.WithName(expected[1].Name)) require.NoError(err) assert.Nil(apiErr) - p2, apiErr, err := roleClient.List(tc.Context()) + p2, apiErr, err := roleClient.List(tc.Context(), tt.scopeId) assert.NoError(err) assert.Nil(apiErr) - assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(p2)) + assert.ElementsMatch(comparableSlice(expected[0:2]), comparableSlice(p2)) - for i := 1; i < 10; i++ { - expected[i], apiErr, err = roleClient.Create(tc.Context(), roles.WithName(expected[i].Name)) + for i := 2; i < 11; i++ { + expected[i], apiErr, err = roleClient.Create(tc.Context(), tt.scopeId, roles.WithName(expected[i].Name)) assert.NoError(err) assert.Nil(apiErr) } - p3, apiErr, err := roleClient.List(tc.Context()) + p3, apiErr, err := roleClient.List(tc.Context(), tt.scopeId) require.NoError(err) assert.Nil(apiErr) assert.ElementsMatch(comparableSlice(expected), comparableSlice(p3)) @@ -210,34 +198,27 @@ func comparableSlice(in []*roles.Role) []roles.Role { return filtered } -func TestRole_Crud(t *testing.T) { +func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) cases := []struct { - name string - scopeClient *api.Client + name string + scopeId string }{ { - name: "org", - scopeClient: client, + name: "org", + scopeId: org.GetPublicId(), }, { - name: "proj", - scopeClient: projClient, + name: "proj", + scopeId: proj.GetPublicId(), }, } @@ -257,8 +238,8 @@ func TestRole_Crud(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - roleClient := roles.NewClient(tt.scopeClient) - g, apiErr, err := roleClient.Create(tc.Context(), roles.WithName("foo")) + roleClient := roles.NewClient(client) + g, apiErr, err := roleClient.Create(tc.Context(), tt.scopeId, roles.WithName("foo")) checkRole("create", g, apiErr, err, "foo", 1) g, apiErr, err = roleClient.Read(tc.Context(), g.Id) @@ -278,47 +259,40 @@ func TestRole_Crud(t *testing.T) { } } -func TestRole_Errors(t *testing.T) { +func TestErrors(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, proj := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) cases := []struct { - name string - scopeClient *api.Client + name string + scopeId string }{ { - name: "org", - scopeClient: client, + name: "org", + scopeId: org.GetPublicId(), }, { - name: "proj", - scopeClient: projClient, + name: "proj", + scopeId: proj.GetPublicId(), }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - roleClient := roles.NewClient(tt.scopeClient) - u, apiErr, err := roleClient.Create(tc.Context(), roles.WithName("first")) + roleClient := roles.NewClient(client) + u, apiErr, err := roleClient.Create(tc.Context(), tt.scopeId, roles.WithName("first")) require.NoError(err) assert.Nil(apiErr) assert.NotNil(u) // Create another resource with the same name. - _, apiErr, err = roleClient.Create(tc.Context(), roles.WithName("first")) + _, apiErr, err = roleClient.Create(tc.Context(), tt.scopeId, roles.WithName("first")) require.NoError(err) assert.NotNil(apiErr) diff --git a/api/scopes/option.gen.go b/api/scopes/option.gen.go index 917a3d15d..acc90cc88 100644 --- a/api/scopes/option.gen.go +++ b/api/scopes/option.gen.go @@ -18,7 +18,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -35,25 +34,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/scopes/project_test.go b/api/scopes/project_test.go index 4100a773d..525f9238b 100644 --- a/api/scopes/project_test.go +++ b/api/scopes/project_test.go @@ -13,20 +13,15 @@ import ( "github.com/stretchr/testify/require" ) -func TestProjects_List(t *testing.T) { +func TestList(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org := iam.TestOrg(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org := iam.TestOrg(t, tc.IamRepo(), iam.WithUserId(token.UserId)) scps := scopes.NewClient(client) @@ -63,20 +58,15 @@ func comparableSlice(in []*scopes.Scope) []scopes.Scope { return filtered } -func TestProjects_Crud(t *testing.T) { +func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, _ := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org, _ := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) scps := scopes.NewClient(client) @@ -116,20 +106,15 @@ func TestProjects_Crud(t *testing.T) { } // TODO: Get better coverage for expected errors and error formats. -func TestProject_Errors(t *testing.T) { +func TestErrors(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org, _ := iam.TestScopes(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org, _ := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) scps := scopes.NewClient(client) diff --git a/api/scopes/scope.gen.go b/api/scopes/scope.gen.go index d8c79f52b..e1eff431d 100644 --- a/api/scopes/scope.gen.go +++ b/api/scopes/scope.gen.go @@ -24,19 +24,31 @@ type Scope struct { Version uint32 `json:"version,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client } func (c *Client) Create(ctx context.Context, scopeId string, opt ...Option) (*Scope, *api.Error, error) { if scopeId == "" { return nil, nil, fmt.Errorf("empty scopeId value passed into Create request") } + opts, apiOpts := getOpts(opt...) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -47,6 +59,7 @@ func (c *Client) Create(ctx context.Context, scopeId string, opt ...Option) (*Sc if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } + if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { @@ -73,9 +86,8 @@ func (c *Client) Create(ctx context.Context, scopeId string, opt ...Option) (*Sc func (c *Client) Read(ctx context.Context, scopeId string, opt ...Option) (*Scope, *api.Error, error) { if scopeId == "" { - return nil, nil, fmt.Errorf("empty scopeId value passed into Read request") + return nil, nil, fmt.Errorf("empty scopeId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -173,7 +185,6 @@ func (c *Client) Delete(ctx context.Context, scopeId string, opt ...Option) (boo if scopeId == "" { return false, nil, fmt.Errorf("empty scopeId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } @@ -216,20 +227,18 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) ([]*Sc if scopeId == "" { return nil, nil, fmt.Errorf("empty scopeId value passed into List request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) + opts.queryMap["scope_id"] = scopeId req, err := c.client.NewRequest(ctx, "GET", "scopes", nil, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating List request: %w", err) } - opts.queryMap["scope_id"] = scopeId - if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { diff --git a/api/targets/option.gen.go b/api/targets/option.gen.go index 1b1dbee6a..460cc54a0 100644 --- a/api/targets/option.gen.go +++ b/api/targets/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,25 +32,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/targets/target.gen.go b/api/targets/target.gen.go index f151ed04b..bb4ceea5f 100644 --- a/api/targets/target.gen.go +++ b/api/targets/target.gen.go @@ -29,20 +29,31 @@ type Target struct { DefaultPort uint32 `json:"default_port,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client } func (c *Client) Create(ctx context.Context, resourceType string, scopeId string, opt ...Option) (*Target, *api.Error, error) { if scopeId == "" { return nil, nil, fmt.Errorf("empty scopeId value passed into Create request") } + opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -92,7 +103,6 @@ func (c *Client) Read(ctx context.Context, targetId string, opt ...Option) (*Tar } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("targets/%s", targetId), nil, apiOpts...) if err != nil { @@ -132,7 +142,6 @@ func (c *Client) Update(ctx context.Context, targetId string, version uint32, op } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) if version == 0 { if !opts.withAutomaticVersioning { @@ -191,7 +200,6 @@ func (c *Client) Delete(ctx context.Context, targetId string, opt ...Option) (bo } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("targets/%s", targetId), nil, apiOpts...) if err != nil { @@ -234,7 +242,6 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) ([]*Ta } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) opts.queryMap["scope_id"] = scopeId req, err := c.client.NewRequest(ctx, "GET", "targets", nil, apiOpts...) @@ -278,7 +285,6 @@ func (c *Client) AddHostSets(ctx context.Context, targetId string, version uint3 } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) if version == 0 { if !opts.withAutomaticVersioning { @@ -341,7 +347,6 @@ func (c *Client) SetHostSets(ctx context.Context, targetId string, version uint3 } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) if version == 0 { if !opts.withAutomaticVersioning { @@ -407,7 +412,6 @@ func (c *Client) RemoveHostSets(ctx context.Context, targetId string, version ui } opts, apiOpts := getOpts(opt...) - apiOpts = append(apiOpts, api.WithNewStyle()) if version == 0 { if !opts.withAutomaticVersioning { diff --git a/api/targets/target_test.go b/api/targets/target_test.go index 014703bbf..0e23aca30 100644 --- a/api/targets/target_test.go +++ b/api/targets/target_test.go @@ -24,18 +24,18 @@ func TestCustom(t *testing.T) { token := tc.Token() _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) client := tc.Client().Clone() - client.SetScopeId(proj.GetPublicId()) + client.SetToken(token.Token) - hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static") + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId()) require.NoError(err) require.Nil(apiErr) hSetClient := hostsets.NewClient(client) - hSet, apiErr, err := hSetClient.Create2(tc.Context(), hc.Id) + hSet, apiErr, err := hSetClient.Create(tc.Context(), hc.Id) require.NoError(err) require.Nil(apiErr) require.NotNil(hSet) - hSet2, apiErr, err := hSetClient.Create2(tc.Context(), hc.Id) + hSet2, apiErr, err := hSetClient.Create(tc.Context(), hc.Id) require.NoError(err) require.Nil(apiErr) require.NotNil(hSet2) @@ -73,8 +73,8 @@ func TestList(t *testing.T) { client := tc.Client() token := tc.Token() + client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - client.SetScopeId(proj.GetPublicId()) tarClient := targets.NewClient(client) ul, apiErr, err := tarClient.List(tc.Context(), proj.GetPublicId()) @@ -129,6 +129,7 @@ func TestCrud(t *testing.T) { client := tc.Client() token := tc.Token() + client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) checkResource := func(t *testing.T, step string, h *targets.Target, apiErr *api.Error, err error, wantedName string, wantVersion uint32) { @@ -175,6 +176,7 @@ func TestSet_Errors(t *testing.T) { client := tc.Client() token := tc.Token() + client.SetToken(token.Token) _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) tarClient := targets.NewClient(client) diff --git a/api/users/option.gen.go b/api/users/option.gen.go index e82410f39..3f5072cd1 100644 --- a/api/users/option.gen.go +++ b/api/users/option.gen.go @@ -16,7 +16,6 @@ type Option func(*options) type options struct { postMap map[string]interface{} queryMap map[string]string - withScopeId string withAutomaticVersioning bool } @@ -33,25 +32,16 @@ func getOpts(opt ...Option) (options, []api.Option) { o(&opts) } var apiOpts []api.Option - if opts.withScopeId != "" { - apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) - } return opts, apiOpts } -func WithScopeId(id string) Option { - return func(o *options) { - o.withScopeId = id - } -} - // If set, and if the version is zero during an update, the API will perform a // fetch to get the current version of the resource and populate it during the // update call. This is convenient but opens up the possibility for subtle // order-of-modification issues, so use carefully. -func WithAutomaticVersioning() Option { +func WithAutomaticVersioning(enable bool) Option { return func(o *options) { - o.withAutomaticVersioning = true + o.withAutomaticVersioning = enable } } diff --git a/api/users/user.gen.go b/api/users/user.gen.go index 8dd98e3e0..092b4795f 100644 --- a/api/users/user.gen.go +++ b/api/users/user.gen.go @@ -25,24 +25,42 @@ type User struct { Version uint32 `json:"version,omitempty"` } +// Client is a client for this collection type Client struct { client *api.Client } +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. func NewClient(c *api.Client) *Client { - return &Client{client: c} + return &Client{client: c.Clone()} } -func (c *Client) Create(ctx context.Context, opt ...Option) (*User, *api.Error, error) { +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client +} + +func (c *Client) Create(ctx context.Context, scopeId string, opt ...Option) (*User, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into Create request") + } + opts, apiOpts := getOpts(opt...) + if c.client == nil { return nil, nil, fmt.Errorf("nil client") } + opts.postMap["scope_id"] = scopeId + req, err := c.client.NewRequest(ctx, "POST", "users", opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } + if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { @@ -69,9 +87,8 @@ func (c *Client) Create(ctx context.Context, opt ...Option) (*User, *api.Error, func (c *Client) Read(ctx context.Context, userId string, opt ...Option) (*User, *api.Error, error) { if userId == "" { - return nil, nil, fmt.Errorf("empty userId value passed into Read request") + return nil, nil, fmt.Errorf("empty userId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -169,7 +186,6 @@ func (c *Client) Delete(ctx context.Context, userId string, opt ...Option) (bool if userId == "" { return false, nil, fmt.Errorf("empty userId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } @@ -208,12 +224,16 @@ func (c *Client) Delete(ctx context.Context, userId string, opt ...Option) (bool return target.Existed, apiErr, nil } -func (c *Client) List(ctx context.Context, opt ...Option) ([]*User, *api.Error, error) { +func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) ([]*User, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into List request") + } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) + opts.queryMap["scope_id"] = scopeId req, err := c.client.NewRequest(ctx, "GET", "users", nil, apiOpts...) if err != nil { diff --git a/api/users/user_test.go b/api/users/user_test.go index c9bff7418..24bbb3537 100644 --- a/api/users/user_test.go +++ b/api/users/user_test.go @@ -13,23 +13,18 @@ import ( "github.com/stretchr/testify/require" ) -func TestUsers_List(t *testing.T) { +func TestList(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org := iam.TestOrg(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org := iam.TestOrg(t, tc.IamRepo(), iam.WithUserId(token.UserId)) userClient := users.NewClient(client) - ul, apiErr, err := userClient.List(tc.Context()) + ul, apiErr, err := userClient.List(tc.Context(), org.GetPublicId()) assert.NoError(err) assert.Nil(apiErr) assert.Empty(ul) @@ -39,21 +34,21 @@ func TestUsers_List(t *testing.T) { expected = append(expected, &users.User{Name: fmt.Sprint(i)}) } - expected[0], apiErr, err = userClient.Create(tc.Context(), users.WithName(expected[0].Name)) + expected[0], apiErr, err = userClient.Create(tc.Context(), org.GetPublicId(), users.WithName(expected[0].Name)) assert.NoError(err) assert.Nil(apiErr) - ul, apiErr, err = userClient.List(tc.Context()) + ul, apiErr, err = userClient.List(tc.Context(), org.GetPublicId()) assert.NoError(err) assert.Nil(apiErr) assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(ul)) for i := 1; i < 10; i++ { - expected[i], apiErr, err = userClient.Create(tc.Context(), users.WithName(expected[i].Name)) + expected[i], apiErr, err = userClient.Create(tc.Context(), org.GetPublicId(), users.WithName(expected[i].Name)) assert.NoError(err) assert.Nil(apiErr) } - ul, apiErr, err = userClient.List(tc.Context()) + ul, apiErr, err = userClient.List(tc.Context(), org.GetPublicId()) require.NoError(err) assert.Nil(apiErr) assert.ElementsMatch(comparableSlice(expected), comparableSlice(ul)) @@ -74,20 +69,15 @@ func comparableSlice(in []*users.User) []users.User { return filtered } -func TestUser_Crud(t *testing.T) { +func TestCrud(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org := iam.TestOrg(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org := iam.TestOrg(t, tc.IamRepo(), iam.WithUserId(token.UserId)) userClient := users.NewClient(client) checkUser := func(step string, u *users.User, apiErr *api.Error, err error, wantedName string, wantedVersion uint32) { @@ -104,7 +94,7 @@ func TestUser_Crud(t *testing.T) { assert.EqualValues(wantedVersion, u.Version) } - u, apiErr, err := userClient.Create(tc.Context(), users.WithName("foo")) + u, apiErr, err := userClient.Create(tc.Context(), org.GetPublicId(), users.WithName("foo")) checkUser("create", u, apiErr, err, "foo", 1) u, apiErr, err = userClient.Read(tc.Context(), u.Id) @@ -127,29 +117,24 @@ func TestUser_Crud(t *testing.T) { assert.EqualValues(http.StatusForbidden, apiErr.Status) } -func TestUser_Errors(t *testing.T) { +func TestErrors(t *testing.T) { assert, require := assert.New(t), require.New(t) - amId := "ampw_1234567890" - tc := controller.NewTestController(t, &controller.TestControllerOpts{ - DisableAuthorizationFailures: true, - DefaultAuthMethodId: amId, - DefaultLoginName: "user", - DefaultPassword: "passpass", - }) + tc := controller.NewTestController(t, nil) defer tc.Shutdown() client := tc.Client() - org := iam.TestOrg(t, tc.IamRepo()) - client.SetScopeId(org.GetPublicId()) + token := tc.Token() + client.SetToken(token.Token) + org := iam.TestOrg(t, tc.IamRepo(), iam.WithUserId(token.UserId)) userClient := users.NewClient(client) - u, apiErr, err := userClient.Create(tc.Context(), users.WithName("first")) + u, apiErr, err := userClient.Create(tc.Context(), org.GetPublicId(), users.WithName("first")) require.NoError(err) assert.Nil(apiErr) assert.NotNil(u) // Create another resource with the same name. - _, apiErr, err = userClient.Create(tc.Context(), users.WithName("first")) + _, apiErr, err = userClient.Create(tc.Context(), org.GetPublicId(), users.WithName("first")) require.NoError(err) assert.NotNil(apiErr) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 01a0bd4a2..f6860525c 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -16,7 +16,6 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/types/action" - "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/recovery" "github.com/hashicorp/go-hclog" @@ -149,26 +148,12 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { return } - // check if it's new style - if opts.withAction != action.Unknown { - v.act = opts.withAction - v.res = &perms.Resource{ - ScopeId: opts.withScopeId, - Id: opts.withId, - Pin: opts.withPin, - Type: opts.withType, - } - } else { - // This remains for legacy - v.requestInfo.scopeId = opts.withScopeId - if err := v.parseAuthParams(); err != nil { - v.logger.Trace("error reading auth parameters from URL", "url", v.requestInfo.Path, "method", v.requestInfo.Method, "error", err) - return - } - } - if v.res == nil { - v.logger.Trace("got nil resource information after decorating auth parameters") - return + v.act = opts.withAction + v.res = &perms.Resource{ + ScopeId: opts.withScopeId, + Id: opts.withId, + Pin: opts.withPin, + Type: opts.withType, } var authResults *perms.ACLResults @@ -192,229 +177,6 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { return } -func (v *verifier) parseAuthParams() error { - // Remove trailing and leading slashes - trimmedPath := strings.Trim(v.requestInfo.Path, "/") - // Remove `v1/` - splitPath := strings.Split(strings.TrimPrefix(trimmedPath, "v1/"), "/") - splitLen := len(splitPath) - - // It must be at least length 1. If so, figure out new vs. legacy parsing - if len(splitPath) == 0 { - return fmt.Errorf("parse auth params: invalid path") - } - - for i := 1; i < splitLen; i++ { - if splitPath[i] == "" { - return fmt.Errorf("parse auth params: empty segment found") - } - } - - // The first segment must be "scopes" - if splitPath[0] != "scopes" { - return fmt.Errorf("parse auth params: invalid first segment %q", splitPath[0]) - } - - v.act = action.Unknown - v.res = &perms.Resource{ - // Start out with scope, and replace when we walk backwards if it's - // actually something else - Type: resource.Scope, - } - - // Handle non-custom types. We'll deal with custom types, including list, - // after parsing the path. - switch v.requestInfo.Method { - case "GET": - v.act = action.Read - case "POST": - v.act = action.Create - case "PATCH": - v.act = action.Update - case "DELETE": - v.act = action.Delete - default: - return fmt.Errorf("parse auth params: unknown method %q", v.requestInfo.Method) - } - - // Look for a custom action - colonSplit := strings.Split(splitPath[splitLen-1], ":") - switch len(colonSplit) { - case 1: - // No custom action specified - - case 2: - // Parse and validate the action, then elide it - actStr := colonSplit[len(colonSplit)-1] - v.act = action.Map[actStr] - if v.act == action.Unknown || v.act == action.All { - return fmt.Errorf("parse auth params: unknown action %q", actStr) - } - // Keep going with the logic without the custom action - splitPath[splitLen-1] = colonSplit[0] - - default: - return fmt.Errorf("parse auth params: unexpected number of colons in last segment %q", colonSplit[len(colonSplit)-1]) - } - - // Get scope information and handle it in a special case; that is, for - // operating on scopes, scope from the request ID, not the path scope - switch splitLen { - case 1: - // We've already validated that this is "scopes" - if v.act == action.Read { - v.act = action.List - } - if v.requestInfo.scopeId == "" { - return errors.New("parse auth params: missing scope ID information for scopes collection operation") - } - v.res.ScopeId = v.requestInfo.scopeId - return nil - - case 2: - id := splitPath[1] - // The next segment should be the scope ID, but it takes place not in - // its own scope but in the parent scope. Rather than require the user - // to provide it, look up the parent. - switch { - case id == "global", strings.HasPrefix(id, scope.Org.Prefix()): - // Org scope parent is always global. Set scope for global - // operations to global as well (it's basically acting as its own - // parent scope). We want this so that users can e.g. modify the - // name or description of the global scope if they have permissions - // in the scope. - v.res.ScopeId = "global" - - default: - // Project case - iamRepo, err := v.iamRepoFn() - if err != nil { - return fmt.Errorf("perform auth check: failed to get iam repo: %w", err) - } - - scp, err := iamRepo.LookupScope(v.ctx, id) - if err != nil { - return fmt.Errorf("perform auth check: failed to lookup scope: %w", err) - } - if scp == nil { - return fmt.Errorf("perform auth check: non-existent scope %q", id) - } - v.res.ScopeId = scp.GetParentId() - } - v.res.Id = id - return nil - - case 3: - // If a custom action was being performed within a scope, it will have - // been elided above. If the last path segment is now empty, address - // this scenario. In this case the action took place _in_ the scope so - // it should be bound accordingly. (This is for actions like - // /scopes/o_abc/:deauthenticate where the action is _in_ the scope, not - // on the scope.) - if splitPath[2] == "" { - v.res.ScopeId = splitPath[1] - v.res.Id = splitPath[1] - return nil - } - - fallthrough - - default: - // In all other cases the scope ID is the next segment - v.res.ScopeId = splitPath[1] - } - - // Walk backwards. As we walk backwards we look for a pin and figure out if - // we're operating on a resource or a collection. The rules for the pin are - // as follows: - // - // * If the last segment is a collection, the pin is the immediately - // preceding ID. This does not include scopes since those are permission - // boundaries. - // - // * If the last segment is an ID, the pin is the immediately preceding ID - // not including the last segment - // - // * If at the end of the logic the pin is the id of a scope then there is - // no pin. The scopes are already enclosing so a pin is redundant. - nextIdIsPin := true - // Use an empty string so we can detect if we found anything in this loop. - var foundId string - var typStr string - // We stop at [2] because we've already dealt with the first two segments - // (scopes/ above. - for i := splitLen - 1; i >= 2; i-- { - segment := splitPath[i] - - if segment == "" { - return fmt.Errorf("parse auth parameters: unexpected empty segment") - } - - // If the segment contains whitespace, it's not valid - if fields := strings.Fields(segment); len(fields) != 1 || fields[0] != segment { - return fmt.Errorf("parse auth params: segment %q contains whitespace", segment) - } - - // Collections don't contain underscores; every resource ID does. - segmentIsCollection := !strings.Contains(segment, "_") - - // If we see an ID, ensure that it's not the right-most ID; if not, it's - // the pin - if !segmentIsCollection && i != splitLen-1 && nextIdIsPin { - v.res.Pin = segment - // By definition this is the last thing we'd be looking for as - // scopes were found above, so we can now break out - break - } - - if typStr == "" { - // The resource check takes place inside the type check because if - // we've identified the type we have either already identified the - // right-most resource ID or we're operating on a collection, so - // this prevents us from finding a different ID earlier in the path. - // We still work backwards to identify a pin. - if foundId == "" && !segmentIsCollection { - foundId = segment - } else { - // Every collection is the plural of the resource type so drop - // the last 's' - if !strings.HasSuffix(segment, "s") { - return fmt.Errorf("parse auth params: invalid collection syntax for %q", segment) - } - typStr = strings.TrimSuffix(segment, "s") - } - } - } - - if foundId != "" { - v.res.Id = foundId - } - - if typStr != "" { - v.res.Type = resource.Map[typStr] - if v.res.Type == resource.Unknown { - return fmt.Errorf("parse auth params: unknown resource type %q", typStr) - } - } else { - // If we found no other type information, we walked backwards all the - // way to the scope boundary, so the type is scope - v.res.Type = resource.Scope - } - - // If we're operating on a collection (that is, the ID is blank) and it's a - // GET, it's actually a list - if v.res.Id == "" && v.act == action.Read { - v.act = action.List - } - - // If the pin ended up being a scope, nil it out - if v.res.Pin != "" && v.res.Pin == v.res.ScopeId { - v.res.Pin = "" - } - - return nil -} - func (v verifier) performAuthCheck() (aclResults *perms.ACLResults, userId string, scopeInfo *scopes.ScopeInfo, retErr error) { // Ensure we return an error by default if we forget to set this somewhere retErr = errors.New("unknown") diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index cb6d94206..1fbcd79ed 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -13,263 +13,11 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/servers/controller/handlers" - "github.com/hashicorp/boundary/internal/types/action" - "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/go-hclog" - "github.com/kr/pretty" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestHandler_AuthDecoration(t *testing.T) { - conn, _ := db.TestSetup(t, "postgres") - wrapper := db.TestWrapper(t) - iamRepo := iam.TestRepo(t, conn, wrapper) - iamRepoFn := func() (*iam.Repository, error) { - return iamRepo, nil - } - org, proj := iam.TestScopes(t, iamRepo) - cases := []struct { - name string - path string - method string - action action.Type - scope string - resource resource.Type - id string - pin string - wantErrContains string - }{ - { - name: "global scope, read, with id", - path: "/v1/scopes/global/users/u_anon", - method: "GET", - action: action.Read, - scope: "global", - resource: resource.User, - id: "u_anon", - }, - { - name: "global scope, update, with id", - path: "/v1/scopes/global/auth-methods/am_1234", - method: "PATCH", - action: action.Update, - scope: "global", - resource: resource.AuthMethod, - id: "am_1234", - }, - { - name: "global scope, delete org", - path: "/v1/scopes/o_1234", - method: "DELETE", - action: action.Delete, - scope: "global", - resource: resource.Scope, - id: "o_1234", - }, - { - name: "org scope, delete project", - path: fmt.Sprintf("/v1/scopes/%s", proj.GetPublicId()), - method: "DELETE", - action: action.Delete, - scope: org.GetPublicId(), - resource: resource.Scope, - id: proj.GetPublicId(), - }, - { - name: "empty segments", - path: "/v1/scopes/o_abc1234/auth-methods//am_1234", - wantErrContains: "empty segment found", - }, - { - name: "global scope, create", - path: "/v1/scopes/global/roles", - method: "POST", - action: action.Create, - scope: "global", - resource: resource.Role, - }, - { - name: "global, invalid collection syntax", - path: "/v1/scope", - wantErrContains: `parse auth params: invalid first segment "scope"`, - }, - { - name: "invalid scopes segment with id", - path: "/v1/scope/o_abc", - wantErrContains: `parse auth params: invalid first segment "scope"`, - }, - { - name: "org, custom action", - path: "/v1/scopes/o_123/auth-methods/am_1234:authenticate", - method: "POST", - action: action.Authenticate, - scope: "o_123", - resource: resource.AuthMethod, - id: "am_1234", - }, - { - name: "unknown action in first segment", - path: "/v1/:authentifake", - wantErrContains: `parse auth params: invalid first segment ":authentifake"`, - }, - { - name: "root, unknown action", - path: "/v1/scopes/o_abc/:authentifake", - wantErrContains: `parse auth params: unknown action "authentifake"`, - }, - { - name: "root, unknown empty action", - path: "/v1/:", - wantErrContains: `parse auth params: invalid first segment ":"`, - }, - { - name: "root, invalid method", - path: "/v1/scopes/o_abc/:authenticate", - method: "FOOBAR", - wantErrContains: `unknown method "FOOBAR"`, - }, - { - name: "root, wrong number of colons", - path: "/v1/scopes/o_abc/:auth:enticate", - wantErrContains: "unexpected number of colons", - }, - { - name: "root, action on global", - path: "/v1/scopes/global", - method: "GET", - action: action.Read, - scope: "global", - resource: resource.Scope, - id: "global", - }, - { - name: "org scope, valid", - path: "/v1/scopes/o_abc123/auth-methods", - method: "POST", - action: action.Create, - scope: "o_abc123", - resource: resource.AuthMethod, - }, - { - name: "project scope, valid", - path: "/v1/scopes/p_1234/host-catalogs", - method: "POST", - action: action.Create, - scope: "p_1234", - resource: resource.HostCatalog, - }, - { - name: "project scope, action on project", - path: "/v1/scopes/p_1234/:deauthenticate", - method: "POST", - action: action.Deauthenticate, - scope: "p_1234", - resource: resource.Scope, - id: "p_1234", - }, - { - name: "global scope, action on org, token scope", - path: "/v1/scopes/o_1234:set-principals", - method: "POST", - action: action.SetPrincipals, - scope: "global", - resource: resource.Scope, - id: "o_1234", - }, - { - name: "global action, valid", - path: "/v1/scopes/global/:read", - action: action.Read, - scope: "global", - resource: resource.Scope, - id: "global", - }, - { - name: "top level, invalid", - path: "/v1/", - wantErrContains: `invalid first segment "v1"`, - }, - { - name: "non-api path", - path: "/", - wantErrContains: `parse auth params: invalid first segment ""`, - }, - { - name: "project scope, pinning collection", - path: "/v1/scopes/p_1234/host-catalogs/hc_1234/host-sets", - action: action.List, - scope: "p_1234", - pin: "hc_1234", - resource: resource.HostSet, - }, - { - name: "project scope, pinning collection, custom action", - path: "/v1/scopes/p_1234/host-catalogs/hc_1234/host-sets:create", - action: action.Create, - scope: "p_1234", - pin: "hc_1234", - resource: resource.HostSet, - }, - { - name: "project scope, pinning id", - path: "/v1/scopes/p_1234/host-catalogs/hc_1234/host-sets/hs_abc", - action: action.Read, - id: "hs_abc", - scope: "p_1234", - pin: "hc_1234", - resource: resource.HostSet, - }, - { - name: "project scope, pinning id, custom action", - path: "/v1/scopes/p_1234/host-catalogs/hc_1234/host-sets/hs_abc:update", - action: action.Update, - id: "hs_abc", - scope: "p_1234", - pin: "hc_1234", - resource: resource.HostSet, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - require, assert := require.New(t), assert.New(t) - if tc.method == "" { - tc.method = "GET" - } - req, err := http.NewRequest(tc.method, fmt.Sprintf("http://127.0.0.1:9200:%s", tc.path), nil) - require.NoError(err) - - v := &verifier{ - requestInfo: RequestInfo{ - Path: req.URL.Path, - Method: tc.method, - }, - iamRepoFn: iamRepoFn, - } - - err = v.parseAuthParams() - if tc.wantErrContains != "" { - require.Error(err) - assert.Contains(err.Error(), tc.wantErrContains, err.Error()) - return - } - require.NoError(err, pretty.Sprint(v)) - - if tc.path == "/" { - return - } - - require.NotNil(v.res) - require.NotEqual(action.Unknown, v.act) - assert.Equal(tc.scope, v.res.ScopeId, "scope "+pretty.Sprint(v)) - assert.Equal(tc.action, v.act, "action "+pretty.Sprint(v)) - assert.Equal(tc.resource, v.res.Type, "type "+pretty.Sprint(v)) - assert.Equal(tc.id, v.res.Id, "id "+pretty.Sprint(v)) - assert.Equal(tc.pin, v.res.Pin, "pin "+pretty.Sprint(v)) - }) - } -} - func TestAuthTokenAuthenticator(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) diff --git a/internal/auth/password/repository_password_test.go b/internal/auth/password/repository_password_test.go index fb28b8594..5c713a1d7 100644 --- a/internal/auth/password/repository_password_test.go +++ b/internal/auth/password/repository_password_test.go @@ -288,7 +288,7 @@ func TestRepository_ChangePassword(t *testing.T) { wantIsErr: db.ErrInvalidParameter, }, { - name: "invalid-no-old-password", + name: "invalid-no-current-password", args: args{ acctId: acct.PublicId, old: "", @@ -325,7 +325,7 @@ func TestRepository_ChangePassword(t *testing.T) { wantIsErr: db.ErrRecordNotFound, }, { - name: "auth-failure-wrong-old-password", + name: "auth-failure-wrong-current-password", args: args{ acctId: acct.PublicId, old: "wrong-password", @@ -455,13 +455,13 @@ func TestRepository_SetPassword(t *testing.T) { createAcct func(string) *Account }{ { - name: "valid-new-password-no-old-password", + name: "valid-new-password-no-current-password", createAcct: createAccount("validnewpwnoold"), oldPw: "", newPw: "abcdefghijk", }, { - name: "valid-new-password-delete-old-password", + name: "valid-new-password-delete-current-password", createAcct: createAccount("validnewpwyesold"), oldPw: origPasswd, newPw: "abcdefghijk", diff --git a/internal/cmd/base/base.go b/internal/cmd/base/base.go index 6bdedfac7..09fc82e04 100644 --- a/internal/cmd/base/base.go +++ b/internal/cmd/base/base.go @@ -53,7 +53,6 @@ type Command struct { flagsOnce sync.Once flagAddr string - flagScope string flagVerbose bool flagTLSCACert string @@ -69,6 +68,7 @@ type Command struct { FlagRecoveryConfig string flagOutputCurlString bool + FlagScopeId string FlagId string FlagName string FlagDescription string @@ -233,39 +233,12 @@ func (c *Command) Client(opt ...Option) (*api.Client, error) { c.UI.Error(fmt.Sprintf("Error unmarshaling stored token information after reading from system credential store: %s", err)) } else { c.client.SetToken(authToken.Token) - if !opts.withNoTokenScope { - c.client.SetScopeId(authToken.Scope.Id) - } } } } } } - // We do this here so we override the stored token info if it's set above - if c.flagScope != NotSetValue { - c.client.SetScopeId(c.flagScope) - if c.flagFormat == "table" && c.flagVerbose { - if c.flagScope == os.Getenv("BOUNDARY_TOKEN") { - c.UI.Info(fmt.Sprintf("Scope of %q set from BOUNDARY_TOKEN env var", c.flagScope)) - } else { - c.UI.Info(fmt.Sprintf("Scope of %q set from -scope command flag", c.flagScope)) - } - } - } else if c.client.ScopeId() != "" && !opts.withNoTokenScope { - // If it didn't come from env or a flag but isn't empty, it must have come from the token - if c.flagFormat == "table" && c.flagVerbose { - c.UI.Info(fmt.Sprintf("Scope of %q set from saved token with name %q", c.client.ScopeId(), tokenName)) - } - } - // At this point if we haven't figured out the scope, default to "global" - if c.client.ScopeId() == "" { - c.client.SetScopeId("global") - if c.flagFormat == "table" && c.flagVerbose { - c.UI.Info(`Scope of "global" set by default`) - } - } - if opts.withNoTokenValue { c.client.SetToken("") } @@ -300,15 +273,6 @@ func (c *Command) FlagSet(bit FlagSetBit) *FlagSets { Usage: "Addr of the Boundary controller, as a complete URL (e.g. https://boundary.example.com:9200).", }) - f.StringVar(&StringVar{ - Name: FlagNameScope, - Target: &c.flagScope, - Default: NotSetValue, - EnvVar: api.EnvBoundaryScopeId, - Completion: complete.PredictAnything, - Usage: `Scope in which to make the request. If not specified, will default to the scope of a saved token (if found), otherwise will default to "global".`, - }) - f.StringVar(&StringVar{ Name: FlagNameCACert, Target: &c.flagTLSCACert, diff --git a/internal/cmd/base/const.go b/internal/cmd/base/const.go index 16a8248d8..9c2a0a731 100644 --- a/internal/cmd/base/const.go +++ b/internal/cmd/base/const.go @@ -4,9 +4,6 @@ const ( // FlagNameAddr is the flag used in the base command to read in the // address of the Boundary server. FlagNameAddr = "addr" - // FlagNameScope is the flag used in the base command to read in the scope - // in which to make a request. - FlagNameScope = "scope" // FlagnameCACert is the flag used in the base command to read in the CA // cert. FlagNameCACert = "ca-cert" diff --git a/internal/cmd/base/format.go b/internal/cmd/base/format.go index ef92761bb..20b2f690b 100644 --- a/internal/cmd/base/format.go +++ b/internal/cmd/base/format.go @@ -51,7 +51,7 @@ func WrapSlice(prefixSpaces int, input []string) string { for _, v := range input { ret = append(ret, fmt.Sprintf("%s%s", strings.Repeat(" ", prefixSpaces), - fmt.Sprintf("%s: ", v), + v, )) } diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index b36724f12..752e8bef2 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -6,6 +6,7 @@ import ( "syscall" "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/commands/accounts" "github.com/hashicorp/boundary/internal/cmd/commands/authenticate" "github.com/hashicorp/boundary/internal/cmd/commands/authmethods" "github.com/hashicorp/boundary/internal/cmd/commands/authtokens" @@ -19,6 +20,7 @@ import ( "github.com/hashicorp/boundary/internal/cmd/commands/proxy" "github.com/hashicorp/boundary/internal/cmd/commands/roles" "github.com/hashicorp/boundary/internal/cmd/commands/scopes" + "github.com/hashicorp/boundary/internal/cmd/commands/targets" "github.com/hashicorp/boundary/internal/cmd/commands/users" "github.com/hashicorp/boundary/internal/cmd/commands/worker" @@ -77,6 +79,66 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { }, nil }, + "accounts": func() (cli.Command, error) { + return &accounts.Command{ + Command: base.NewCommand(ui), + }, nil + }, + "accounts read": func() (cli.Command, error) { + return &accounts.Command{ + Command: base.NewCommand(ui), + Func: "read", + }, nil + }, + "accounts delete": func() (cli.Command, error) { + return &accounts.Command{ + Command: base.NewCommand(ui), + Func: "delete", + }, nil + }, + "accounts list": func() (cli.Command, error) { + return &accounts.Command{ + Command: base.NewCommand(ui), + Func: "list", + }, nil + }, + "accounts set-password": func() (cli.Command, error) { + return &accounts.Command{ + Command: base.NewCommand(ui), + Func: "set-password", + }, nil + }, + "accounts change-password": func() (cli.Command, error) { + return &accounts.Command{ + Command: base.NewCommand(ui), + Func: "change-password", + }, nil + }, + "accounts create": func() (cli.Command, error) { + return &accounts.Command{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "accounts create password": func() (cli.Command, error) { + return &accounts.PasswordCommand{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "accounts update": func() (cli.Command, error) { + return &accounts.Command{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "accounts update password": func() (cli.Command, error) { + return &accounts.PasswordCommand{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "auth-methods": func() (cli.Command, error) { return &authmethods.Command{ Command: base.NewCommand(ui), @@ -335,11 +397,53 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { }, nil }, - "hosts create": func() (cli.Command, error) { - return &hosts.CreateCommand{ + "hosts": func() (cli.Command, error) { + return &hosts.Command{ Command: base.NewCommand(ui), }, nil }, + "hosts read": func() (cli.Command, error) { + return &hosts.Command{ + Command: base.NewCommand(ui), + Func: "read", + }, nil + }, + "hosts delete": func() (cli.Command, error) { + return &hosts.Command{ + Command: base.NewCommand(ui), + Func: "delete", + }, nil + }, + "hosts list": func() (cli.Command, error) { + return &hosts.Command{ + Command: base.NewCommand(ui), + Func: "list", + }, nil + }, + "hosts create": func() (cli.Command, error) { + return &hosts.Command{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "hosts create static": func() (cli.Command, error) { + return &hosts.StaticCommand{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "hosts update": func() (cli.Command, error) { + return &hosts.Command{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "hosts update static": func() (cli.Command, error) { + return &hosts.StaticCommand{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, "roles": func() (cli.Command, error) { return &roles.Command{ @@ -449,6 +553,72 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { }, nil }, + "targets": func() (cli.Command, error) { + return &targets.Command{ + Command: base.NewCommand(ui), + }, nil + }, + "targets read": func() (cli.Command, error) { + return &targets.Command{ + Command: base.NewCommand(ui), + Func: "read", + }, nil + }, + "targets delete": func() (cli.Command, error) { + return &targets.Command{ + Command: base.NewCommand(ui), + Func: "delete", + }, nil + }, + "targets list": func() (cli.Command, error) { + return &targets.Command{ + Command: base.NewCommand(ui), + Func: "list", + }, nil + }, + "targets create": func() (cli.Command, error) { + return &targets.Command{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "targets create tcp": func() (cli.Command, error) { + return &targets.TcpCommand{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "targets update": func() (cli.Command, error) { + return &targets.Command{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "targets update tcp": func() (cli.Command, error) { + return &targets.TcpCommand{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "targets add-host-sets": func() (cli.Command, error) { + return &targets.Command{ + Command: base.NewCommand(ui), + Func: "add-host-sets", + }, nil + }, + "targets remove-host-sets": func() (cli.Command, error) { + return &targets.Command{ + Command: base.NewCommand(ui), + Func: "remove-host-sets", + }, nil + }, + "targets set-host-sets": func() (cli.Command, error) { + return &targets.Command{ + Command: base.NewCommand(ui), + Func: "set-host-sets", + }, nil + }, + "users": func() (cli.Command, error) { return &users.Command{ Command: base.NewCommand(ui), diff --git a/internal/cmd/commands/accounts/account.go b/internal/cmd/commands/accounts/account.go new file mode 100644 index 000000000..650d9548f --- /dev/null +++ b/internal/cmd/commands/accounts/account.go @@ -0,0 +1,375 @@ +package accounts + +import ( + "fmt" + "os" + "strings" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/accounts" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/boundary/internal/types/resource" + "github.com/hashicorp/vault/sdk/helper/password" + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*Command)(nil) +var _ cli.CommandAutocomplete = (*Command)(nil) + +type Command struct { + *base.Command + + Func string + + flagAuthMethodId string + flagPassword string + flagCurrentPassword string + flagNewPassword string +} + +func (c *Command) Synopsis() string { + switch c.Func { + case "create": + return "Create account resources within Boundary" + case "update": + return "Update account resources within Boundary" + default: + return common.SynopsisFunc(c.Func, "account") + } +} + +var flagsMap = map[string][]string{ + "read": {"id"}, + "delete": {"id"}, + "list": {"auth-method-id"}, + "set-password": {"id", "password", "version"}, + "change-password": {"id", "current-password", "new-password", "version"}, +} + +func (c *Command) Help() string { + helpMap := common.HelpMap("account") + var helpStr string + switch c.Func { + case "": + return base.WrapForHelpText([]string{ + "Usage: boundary accounts [sub command] [options] [args]", + "", + " This command allows operations on Boundary account resources. Example:", + "", + " Read a account:", + "", + ` $ boundary accounts read -id apw_1234567890`, + "", + " Please see the accounts subcommand help for detailed usage information.", + }) + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary accounts create [type] [sub command] [options] [args]", + "", + " This command allows create operations on Boundary account resources. Example:", + "", + " Create a password-type account:", + "", + ` $ boundary accounts create password -name prodops -description "For ProdOps usage"`, + "", + " Please see the typed subcommand help for detailed usage information.", + }) + case "update": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary accounts update [type] [sub command] [options] [args]", + "", + " This command allows update operations on Boundary account resources. Example:", + "", + " Update a password-type account:", + "", + ` $ boundary accounts update password -id apw_1234567890 -name devops -description "For DevOps usage"`, + "", + " Please see the typed subcommand help for detailed usage information.", + }) + case "set-password": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary accounts set-password [sub command] [options] [args]", + "", + " This command allows setting the password on account-type resources, if the types match and the operation is allowed by the given account type. Example:", + "", + " Set the password on a password-type account:", + "", + ` $ boundary accounts set-password -id apw_1234567890 -password `, + }) + case "change-password": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary accounts change-password [sub command] [options] [args]", + "", + " This command allows changing the password (with verification of the current password) on account-type resources, if the types match and the operation is allowed by the given account type. Example:", + "", + " Change the password on a password-type account:", + "", + ` $ boundary accounts change-password -id apw_1234567890 -current-password -new-password `, + }) + default: + helpStr = helpMap[c.Func]() + } + return helpStr + c.Flags().Help() +} + +func (c *Command) Flags() *base.FlagSets { + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + + f := set.NewFlagSet("Command Options") + + if len(flagsMap[c.Func]) > 0 { + common.PopulateCommonFlags(c.Command, f, resource.Account.String(), flagsMap[c.Func]) + } + + for _, name := range flagsMap[c.Func] { + switch name { + case "auth-method-id": + f.StringVar(&base.StringVar{ + Name: "auth-method-id", + Target: &c.flagAuthMethodId, + Usage: "The auth-method resource in which to create or update the account resource", + }) + case "password": + f.StringVar(&base.StringVar{ + Name: "password", + Target: &c.flagPassword, + Usage: "The password for the account. If not specified, the command will prompt for the password to be entered in a non-echoing way.", + }) + case "current-password": + f.StringVar(&base.StringVar{ + Name: "current-password", + Target: &c.flagCurrentPassword, + Usage: "The current password for the account. If not specified, the command will prompt for the password to be entered in a non-echoing way.", + }) + case "new-password": + f.StringVar(&base.StringVar{ + Name: "new-password", + Target: &c.flagNewPassword, + Usage: "The new password for the account. If not specified, the command will prompt for the password to be entered in a non-echoing way.", + }) + } + } + + return set +} + +func (c *Command) AutocompleteArgs() complete.Predictor { + return complete.PredictAnything +} + +func (c *Command) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *Command) Run(args []string) int { + switch c.Func { + case "", "create", "update": + return cli.RunResultHelp + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" { + c.UI.Error("ID is required but not passed in via -id") + return 1 + } + if strutil.StrListContains(flagsMap[c.Func], "auth-method-id") && c.flagAuthMethodId == "" { + c.UI.Error("Auth Method ID must be passed in via -auth-method-id") + return 1 + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error())) + return 2 + } + + var opts []accounts.Option + + switch c.FlagName { + case "": + case "null": + opts = append(opts, accounts.DefaultName()) + default: + opts = append(opts, accounts.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, accounts.DefaultDescription()) + default: + opts = append(opts, accounts.WithDescription(c.FlagDescription)) + } + + if strutil.StrListContains(flagsMap[c.Func], "password") && c.flagPassword == "" { + fmt.Print("Password is not set as flag, please enter it now (will be hidden): ") + value, err := password.Read(os.Stdin) + fmt.Print("\n") + if err != nil { + c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error())) + return 2 + } + c.flagPassword = strings.TrimSpace(value) + } + + if strutil.StrListContains(flagsMap[c.Func], "current-password") && c.flagCurrentPassword == "" { + fmt.Print("Current password is not set as flag, please enter it now (will be hidden): ") + value, err := password.Read(os.Stdin) + fmt.Print("\n") + if err != nil { + c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error())) + return 2 + } + c.flagCurrentPassword = strings.TrimSpace(value) + } + + if strutil.StrListContains(flagsMap[c.Func], "new-password") && c.flagNewPassword == "" { + fmt.Print("New password is not set as flag, please enter it now (will be hidden): ") + value, err := password.Read(os.Stdin) + fmt.Print("\n") + if err != nil { + c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error())) + return 2 + } + c.flagNewPassword = strings.TrimSpace(value) + } + + // Perform check-and-set when needed + var version uint32 + switch c.Func { + case "create": + // These don't update so don't need the existing version + default: + switch c.FlagVersion { + case 0: + opts = append(opts, accounts.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + } + + accountClient := accounts.NewClient(client) + + var existed bool + var account *accounts.Account + var listedAccounts []*accounts.Account + var apiErr *api.Error + + switch c.Func { + case "read": + account, apiErr, err = accountClient.Read(c.Context, c.FlagId, opts...) + case "delete": + existed, apiErr, err = accountClient.Delete(c.Context, c.FlagId, opts...) + case "list": + listedAccounts, apiErr, err = accountClient.List(c.Context, c.flagAuthMethodId, opts...) + case "set-password": + account, apiErr, err = accountClient.SetPassword(c.Context, c.FlagId, c.flagPassword, version, opts...) + case "change-password": + account, apiErr, err = accountClient.ChangePassword(c.Context, c.FlagId, c.flagCurrentPassword, c.flagNewPassword, version, opts...) + } + + plural := "account" + if c.Func == "list" { + plural = "accounts" + } + if err != nil { + c.UI.Error(fmt.Sprintf("Error trying to %s %s: %s", c.Func, plural, err.Error())) + return 2 + } + if apiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing %s on %s: %s", c.Func, plural, pretty.Sprint(apiErr))) + return 1 + } + + switch c.Func { + case "delete": + switch base.Format(c.UI) { + case "json": + c.UI.Output("null") + case "table": + output := "The delete operation completed successfully" + switch existed { + case true: + output += "." + default: + output += ", however the resource did not exist at the time." + } + c.UI.Output(output) + } + return 0 + + case "list": + switch base.Format(c.UI) { + case "json": + if len(listedAccounts) == 0 { + c.UI.Output("null") + return 0 + } + b, err := base.JsonFormatter{}.Format(listedAccounts) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting as JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + + case "table": + if len(listedAccounts) == 0 { + c.UI.Output("No accounts found") + return 0 + } + var output []string + output = []string{ + "", + "Account information:", + } + for i, m := range listedAccounts { + if i > 0 { + output = append(output, "") + } + if true { + output = append(output, + fmt.Sprintf(" ID: %s", m.Id), + fmt.Sprintf(" Version: %d", m.Version), + fmt.Sprintf(" Type: %s", m.Type), + ) + } + if m.Name != "" { + output = append(output, + fmt.Sprintf(" Name: %s", m.Name), + ) + } + if m.Description != "" { + output = append(output, + fmt.Sprintf(" Description: %s", m.Description), + ) + } + } + c.UI.Output(base.WrapForHelpText(output)) + } + return 0 + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateAccountTableOutput(account)) + case "json": + b, err := base.JsonFormatter{}.Format(account) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting as JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + } + + return 0 +} diff --git a/internal/cmd/commands/accounts/funcs.go b/internal/cmd/commands/accounts/funcs.go new file mode 100644 index 000000000..bc48a7d13 --- /dev/null +++ b/internal/cmd/commands/accounts/funcs.go @@ -0,0 +1,74 @@ +package accounts + +import ( + "fmt" + "time" + + "github.com/hashicorp/boundary/api/accounts" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func generateAccountTableOutput(in *accounts.Account) string { + var ret []string + + nonAttributeMap := map[string]interface{}{ + "ID": in.Id, + "Scope ID": in.Scope.Id, + "Version": in.Version, + "Type": in.Type, + "Created Time": in.CreatedTime.Local().Format(time.RFC3339), + "Updated Time": in.UpdatedTime.Local().Format(time.RFC3339), + "Auth Method ID": in.AuthMethodId, + } + + if in.Name != "" { + nonAttributeMap["Name"] = in.Name + } + if in.Description != "" { + nonAttributeMap["Description"] = in.Description + } + + maxLength := 0 + for k := range nonAttributeMap { + if len(k) > maxLength { + maxLength = len(k) + } + } + if len(in.Attributes) > 0 { + for k, v := range in.Attributes { + if attributeMap[k] != "" { + in.Attributes[attributeMap[k]] = v + delete(in.Attributes, k) + } + } + for k := range in.Attributes { + if len(k) > maxLength { + maxLength = len(k) + } + } + } + + ret = append(ret, "", "Account information:") + + ret = append(ret, + // We do +2 because there is another +2 offset for attributes below + base.WrapMap(2, maxLength+2, nonAttributeMap), + ) + + if len(in.Attributes) > 0 { + if true { + ret = append(ret, + fmt.Sprintf(" Attributes: %s", ""), + ) + } + ret = append(ret, + base.WrapMap(4, maxLength, in.Attributes), + ) + } + + return base.WrapForHelpText(ret) +} + +var attributeMap = map[string]string{ + "login_name": "Login Name", +} diff --git a/internal/cmd/commands/accounts/password.go b/internal/cmd/commands/accounts/password.go new file mode 100644 index 000000000..9d39c086e --- /dev/null +++ b/internal/cmd/commands/accounts/password.go @@ -0,0 +1,242 @@ +package accounts + +import ( + "fmt" + "net/textproto" + "os" + "strings" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/accounts" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/vault/sdk/helper/password" + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*PasswordCommand)(nil) +var _ cli.CommandAutocomplete = (*PasswordCommand)(nil) + +type PasswordCommand struct { + *base.Command + + Func string + + flagAuthMethodId string + flagLoginName string + flagPassword string +} + +func (c *PasswordCommand) Synopsis() string { + return fmt.Sprintf("%s a password-type account within Boundary", textproto.CanonicalMIMEHeaderKey(c.Func)) +} + +var passwordFlagsMap = map[string][]string{ + "create": {"auth-method-id", "name", "description", "login-name", "password"}, + "update": {"id", "name", "description", "version"}, +} + +func (c *PasswordCommand) Help() string { + var info string + switch c.Func { + case "create": + info = base.WrapForHelpText([]string{ + "Usage: boundary accounts password create [options] [args]", + "", + " Create a password-type account. Example:", + "", + ` $ boundary accounts password create -name prodops -description "Password account for ProdOps" -address "127.0.0.1"`, + "", + "", + }) + + case "update": + info = base.WrapForHelpText([]string{ + "Usage: boundary accounts password update [options] [args]", + "", + " Update a password-type account given its ID. Example:", + "", + ` $ boundary accounts password update -id hst_1234567890 -name "devops" -description "Password account for DevOps" -address "10.20.30.40"`, + "", + "", + }) + } + return info + c.Flags().Help() +} + +func (c *PasswordCommand) Flags() *base.FlagSets { + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + + f := set.NewFlagSet("Command Options") + + if len(passwordFlagsMap[c.Func]) > 0 { + common.PopulateCommonFlags(c.Command, f, "password-type account", passwordFlagsMap[c.Func]) + } + + for _, name := range passwordFlagsMap[c.Func] { + switch name { + case "auth-method-id": + f.StringVar(&base.StringVar{ + Name: "auth-method-id", + Target: &c.flagAuthMethodId, + Usage: "The auth-method resource in which to create or update the account resource", + }) + } + } + + f = set.NewFlagSet("Password Account Options") + + for _, name := range passwordFlagsMap[c.Func] { + switch name { + case "login-name": + f.StringVar(&base.StringVar{ + Name: "login-name", + Target: &c.flagLoginName, + Usage: "The login name for the account", + }) + case "password": + f.StringVar(&base.StringVar{ + Name: "password", + Target: &c.flagPassword, + Usage: "The password for the account. If not specified, the command will prompt for the password to be entered in a non-echoing way.", + }) + } + } + + return set +} + +func (c *PasswordCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictAnything +} + +func (c *PasswordCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *PasswordCommand) Run(args []string) int { + if c.Func == "" { + return cli.RunResultHelp + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if strutil.StrListContains(passwordFlagsMap[c.Func], "id") && c.FlagId == "" { + c.UI.Error("ID is required but not passed in via -id") + return 1 + } + if strutil.StrListContains(passwordFlagsMap[c.Func], "auth-method-id") && c.flagAuthMethodId == "" { + c.UI.Error("Auth Method ID must be passed in via -auth-method-id") + return 1 + } + if c.Func == "create" && c.flagLoginName == "" { + c.UI.Error("Login Name must be passed in via -login-name") + return 1 + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error())) + return 2 + } + + var opts []accounts.Option + + switch c.FlagName { + case "": + case "null": + opts = append(opts, accounts.DefaultName()) + default: + opts = append(opts, accounts.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, accounts.DefaultDescription()) + default: + opts = append(opts, accounts.WithDescription(c.FlagDescription)) + } + + switch c.flagLoginName { + case "": + case "null": + opts = append(opts, accounts.DefaultPasswordAccountLoginName()) + default: + opts = append(opts, accounts.WithPasswordAccountLoginName(c.flagLoginName)) + } + + if strutil.StrListContains(passwordFlagsMap[c.Func], "password") { + switch c.flagPassword { + case "": + fmt.Print("Password is not set as flag, please enter it now (will be hidden): ") + value, err := password.Read(os.Stdin) + fmt.Print("\n") + if err != nil { + c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error())) + return 2 + } + opts = append(opts, accounts.WithPasswordAccountPassword(strings.TrimSpace(value))) + default: + opts = append(opts, accounts.WithPasswordAccountPassword(c.flagPassword)) + } + } + + accountClient := accounts.NewClient(client) + + // Perform check-and-set when needed + var version uint32 + switch c.Func { + case "create": + // These don't update so don't need the existing version + default: + switch c.FlagVersion { + case 0: + opts = append(opts, accounts.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + } + + var account *accounts.Account + var apiErr *api.Error + + switch c.Func { + case "create": + account, apiErr, err = accountClient.Create(c.Context, c.flagAuthMethodId, opts...) + case "update": + account, apiErr, err = accountClient.Update(c.Context, c.FlagId, version, opts...) + } + + plural := "password-type account" + if err != nil { + c.UI.Error(fmt.Sprintf("Error trying to %s %s: %s", c.Func, plural, err.Error())) + return 2 + } + if apiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing %s on %s: %s", c.Func, plural, pretty.Sprint(apiErr))) + return 1 + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateAccountTableOutput(account)) + case "json": + b, err := base.JsonFormatter{}.Format(account) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting as JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + } + + return 0 +} diff --git a/internal/cmd/commands/authenticate/password.go b/internal/cmd/commands/authenticate/password.go index f2154d7b5..355ec3d86 100644 --- a/internal/cmd/commands/authenticate/password.go +++ b/internal/cmd/commands/authenticate/password.go @@ -56,14 +56,14 @@ func (c *PasswordCommand) Flags() *base.FlagSets { Name: "login-name", Target: &c.flagLoginName, EnvVar: envLoginName, - Usage: "Login name", + Usage: "The login name corresponding to an account within the given auth method", }) f.StringVar(&base.StringVar{ Name: "password", Target: &c.flagPassword, EnvVar: envPassword, - Usage: "Password", + Usage: "The password associated with the login name", }) f.StringVar(&base.StringVar{ @@ -121,7 +121,11 @@ func (c *PasswordCommand) Run(args []string) int { // note: Authenticate() calls SetToken() under the hood to set the // auth bearer on the client so we do not need to do anything with the // returned token after this call, so we ignore it - result, apiErr, err := authmethods.NewClient(client).Authenticate(c.Context, c.flagAuthMethodId, c.flagLoginName, c.flagPassword) + result, apiErr, err := authmethods.NewClient(client).Authenticate(c.Context, c.flagAuthMethodId, + map[string]interface{}{ + "login_name": c.flagLoginName, + "password": c.flagPassword, + }) if err != nil { c.UI.Error(fmt.Sprintf("Error trying to perform authentication: %s", err.Error())) return 2 diff --git a/internal/cmd/commands/authmethods/authmethod.go b/internal/cmd/commands/authmethods/authmethod.go index 147dd8a16..f83593131 100644 --- a/internal/cmd/commands/authmethods/authmethod.go +++ b/internal/cmd/commands/authmethods/authmethod.go @@ -37,10 +37,12 @@ func (c *Command) Synopsis() string { var flagsMap = map[string][]string{ "read": {"id"}, "delete": {"id"}, + "list": {"scope-id"}, } func (c *Command) Help() string { helpMap := common.HelpMap("auth-method") + var helpStr string switch c.Func { case "": return base.WrapForHelpText([]string{ @@ -55,7 +57,7 @@ func (c *Command) Help() string { " Please see the auth-methods subcommand help for detailed usage information.", }) case "create": - return base.WrapForHelpText([]string{ + helpStr = base.WrapForHelpText([]string{ "Usage: boundary auth-methods create [type] [sub command] [options] [args]", "", " This command allows create operations on Boundary auth-method resources. Example:", @@ -67,7 +69,7 @@ func (c *Command) Help() string { " Please see the typed subcommand help for detailed usage information.", }) case "update": - return base.WrapForHelpText([]string{ + helpStr = base.WrapForHelpText([]string{ "Usage: boundary auth-methods update [type] [sub command] [options] [args]", "", " This command allows update operations on Boundary auth-method resources. Example:", @@ -79,8 +81,9 @@ func (c *Command) Help() string { " Please see the typed subcommand help for detailed usage information.", }) default: - return helpMap[c.Func]() + c.Flags().Help() + helpStr = helpMap[c.Func]() } + return helpStr + c.Flags().Help() } func (c *Command) Flags() *base.FlagSets { @@ -119,6 +122,10 @@ func (c *Command) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(flagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } client, err := c.Client() if err != nil { @@ -157,7 +164,7 @@ func (c *Command) Run(args []string) int { case "delete": existed, apiErr, err = authmethodClient.Delete(c.Context, c.FlagId, opts...) case "list": - listedMethods, apiErr, err = authmethodClient.List(c.Context, opts...) + listedMethods, apiErr, err = authmethodClient.List(c.Context, c.FlagScopeId, opts...) } plural := "auth method" diff --git a/internal/cmd/commands/authmethods/funcs.go b/internal/cmd/commands/authmethods/funcs.go index 6375a2227..daf866c38 100644 --- a/internal/cmd/commands/authmethods/funcs.go +++ b/internal/cmd/commands/authmethods/funcs.go @@ -26,6 +26,7 @@ func generateAuthMethodTableOutput(in *authmethods.AuthMethod) string { nonAttributeMap := map[string]interface{}{ "ID": in.Id, + "Scope ID": in.Scope.Id, "Version": in.Version, "Type": in.Type, "Created Time": in.CreatedTime.Local().Format(time.RFC3339), diff --git a/internal/cmd/commands/authmethods/password.go b/internal/cmd/commands/authmethods/password.go index da56b257c..087edda61 100644 --- a/internal/cmd/commands/authmethods/password.go +++ b/internal/cmd/commands/authmethods/password.go @@ -32,7 +32,7 @@ func (c *PasswordCommand) Synopsis() string { } var passwordFlagsMap = map[string][]string{ - "create": {"name", "description"}, + "create": {"scope-id", "name", "description"}, "update": {"id", "name", "description", "version"}, } @@ -102,6 +102,10 @@ func (c *PasswordCommand) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(passwordFlagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } client, err := c.Client() if err != nil { @@ -174,7 +178,7 @@ func (c *PasswordCommand) Run(args []string) int { default: switch c.FlagVersion { case 0: - opts = append(opts, authmethods.WithAutomaticVersioning()) + opts = append(opts, authmethods.WithAutomaticVersioning(true)) default: version = uint32(c.FlagVersion) } @@ -185,7 +189,7 @@ func (c *PasswordCommand) Run(args []string) int { switch c.Func { case "create": - method, apiErr, err = authmethodClient.Create(c.Context, "password", opts...) + method, apiErr, err = authmethodClient.Create(c.Context, "password", c.FlagScopeId, opts...) case "update": method, apiErr, err = authmethodClient.Update(c.Context, c.FlagId, version, opts...) } diff --git a/internal/cmd/commands/authtokens/authtokens.go b/internal/cmd/commands/authtokens/authtokens.go index 561b7d800..81995acc4 100644 --- a/internal/cmd/commands/authtokens/authtokens.go +++ b/internal/cmd/commands/authtokens/authtokens.go @@ -31,6 +31,7 @@ func (c *Command) Synopsis() string { var flagsMap = map[string][]string{ "read": {"id"}, "delete": {"id"}, + "list": {"scope-id"}, } func (c *Command) Help() string { @@ -76,6 +77,10 @@ func (c *Command) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(flagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } client, err := c.Client() if err != nil { @@ -96,7 +101,7 @@ func (c *Command) Run(args []string) int { case "delete": existed, apiErr, err = authtokenClient.Delete(c.Context, c.FlagId) case "list": - listedTokens, apiErr, err = authtokenClient.List(c.Context) + listedTokens, apiErr, err = authtokenClient.List(c.Context, c.FlagScopeId) } plural := "auth token" diff --git a/internal/cmd/commands/groups/funcs.go b/internal/cmd/commands/groups/funcs.go index adde48ffd..58fee35c0 100644 --- a/internal/cmd/commands/groups/funcs.go +++ b/internal/cmd/commands/groups/funcs.go @@ -75,6 +75,7 @@ func generateGroupTableOutput(in *groups.Group) string { nonAttributeMap := map[string]interface{}{ "ID": in.Id, + "Scope ID": in.Scope.Id, "Version": in.Version, "Created Time": in.CreatedTime.Local().Format(time.RFC3339), "Updated Time": in.UpdatedTime.Local().Format(time.RFC3339), diff --git a/internal/cmd/commands/groups/group.go b/internal/cmd/commands/groups/group.go index 4f01710f3..a6a66367f 100644 --- a/internal/cmd/commands/groups/group.go +++ b/internal/cmd/commands/groups/group.go @@ -43,10 +43,11 @@ var helpMap = func() map[string]func() string { } var flagsMap = map[string][]string{ - "create": {"name", "description"}, + "create": {"scope-id", "name", "description"}, "update": {"id", "name", "description", "version"}, "read": {"id"}, "delete": {"id"}, + "list": {"scope-id"}, "add-members": {"id", "member", "version"}, "set-members": {"id", "member", "version"}, "remove-members": {"id", "member", "version"}, @@ -95,6 +96,10 @@ func (c *Command) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(flagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } client, err := c.Client() if err != nil { @@ -151,7 +156,7 @@ func (c *Command) Run(args []string) int { default: switch c.FlagVersion { case 0: - opts = append(opts, groups.WithAutomaticVersioning()) + opts = append(opts, groups.WithAutomaticVersioning(true)) default: version = uint32(c.FlagVersion) } @@ -164,7 +169,7 @@ func (c *Command) Run(args []string) int { switch c.Func { case "create": - group, apiErr, err = groupClient.Create(c.Context, opts...) + group, apiErr, err = groupClient.Create(c.Context, c.FlagScopeId, opts...) case "update": group, apiErr, err = groupClient.Update(c.Context, c.FlagId, version, opts...) case "read": @@ -172,7 +177,7 @@ func (c *Command) Run(args []string) int { case "delete": existed, apiErr, err = groupClient.Delete(c.Context, c.FlagId, opts...) case "list": - listedGroups, apiErr, err = groupClient.List(c.Context, opts...) + listedGroups, apiErr, err = groupClient.List(c.Context, c.FlagScopeId, opts...) case "add-members": group, apiErr, err = groupClient.AddMembers(c.Context, c.FlagId, version, members, opts...) case "set-members": diff --git a/internal/cmd/commands/hostcatalogs/funcs.go b/internal/cmd/commands/hostcatalogs/funcs.go index c59a622fa..f76754e63 100644 --- a/internal/cmd/commands/hostcatalogs/funcs.go +++ b/internal/cmd/commands/hostcatalogs/funcs.go @@ -13,6 +13,7 @@ func generateHostCatalogTableOutput(in *hostcatalogs.HostCatalog) string { nonAttributeMap := map[string]interface{}{ "ID": in.Id, + "Scope ID": in.Scope.Id, "Version": in.Version, "Type": in.Type, "Created Time": in.CreatedTime.Local().Format(time.RFC3339), @@ -54,12 +55,8 @@ func generateHostCatalogTableOutput(in *hostcatalogs.HostCatalog) string { ) if len(in.Attributes) > 0 { - if true { - ret = append(ret, - fmt.Sprintf(" Attributes: %s", ""), - ) - } ret = append(ret, + fmt.Sprintf(" Attributes: %s", ""), base.WrapMap(4, maxLength, in.Attributes), ) } @@ -67,4 +64,6 @@ func generateHostCatalogTableOutput(in *hostcatalogs.HostCatalog) string { return base.WrapForHelpText(ret) } -var attributeMap = map[string]string{} +var attributeMap = map[string]string{ + "address": "Address", +} diff --git a/internal/cmd/commands/hostcatalogs/hostcatalog.go b/internal/cmd/commands/hostcatalogs/hostcatalog.go index 9920a1133..b60cd864f 100644 --- a/internal/cmd/commands/hostcatalogs/hostcatalog.go +++ b/internal/cmd/commands/hostcatalogs/hostcatalog.go @@ -26,7 +26,7 @@ type Command struct { func (c *Command) Synopsis() string { switch c.Func { case "create": - return "Create host-catalolg resources within Boundary" + return "Create host-catalog resources within Boundary" case "update": return "Update host-catalog resources within Boundary" default: @@ -37,10 +37,12 @@ func (c *Command) Synopsis() string { var flagsMap = map[string][]string{ "read": {"id"}, "delete": {"id"}, + "list": {"scope-id"}, } func (c *Command) Help() string { helpMap := common.HelpMap("host-catalog") + var helpStr string switch c.Func { case "": return base.WrapForHelpText([]string{ @@ -55,7 +57,7 @@ func (c *Command) Help() string { " Please see the host-catalogs subcommand help for detailed usage information.", }) case "create": - return base.WrapForHelpText([]string{ + helpStr = base.WrapForHelpText([]string{ "Usage: boundary host-catalogs create [type] [sub command] [options] [args]", "", " This command allows create operations on Boundary host-catalog resources. Example:", @@ -67,7 +69,7 @@ func (c *Command) Help() string { " Please see the typed subcommand help for detailed usage information.", }) case "update": - return base.WrapForHelpText([]string{ + helpStr = base.WrapForHelpText([]string{ "Usage: boundary host-catalogs update [type] [sub command] [options] [args]", "", " This command allows update operations on Boundary host-catalog resources. Example:", @@ -79,8 +81,9 @@ func (c *Command) Help() string { " Please see the typed subcommand help for detailed usage information.", }) default: - return helpMap[c.Func]() + c.Flags().Help() + helpStr = helpMap[c.Func]() } + return helpStr + c.Flags().Help() } func (c *Command) Flags() *base.FlagSets { @@ -119,6 +122,10 @@ func (c *Command) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(flagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } client, err := c.Client() if err != nil { @@ -157,7 +164,7 @@ func (c *Command) Run(args []string) int { case "delete": existed, apiErr, err = hostcatalogClient.Delete(c.Context, c.FlagId, opts...) case "list": - listedCatalogs, apiErr, err = hostcatalogClient.List(c.Context, opts...) + listedCatalogs, apiErr, err = hostcatalogClient.List(c.Context, c.FlagScopeId, opts...) } plural := "host catalog" diff --git a/internal/cmd/commands/hostcatalogs/static.go b/internal/cmd/commands/hostcatalogs/static.go index 72b05f5cc..77d9c815c 100644 --- a/internal/cmd/commands/hostcatalogs/static.go +++ b/internal/cmd/commands/hostcatalogs/static.go @@ -28,7 +28,7 @@ func (c *StaticCommand) Synopsis() string { } var staticFlagsMap = map[string][]string{ - "create": {"name", "description"}, + "create": {"scope-id", "name", "description"}, "update": {"id", "name", "description", "version"}, } @@ -95,6 +95,10 @@ func (c *StaticCommand) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(staticFlagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } client, err := c.Client() if err != nil { @@ -130,7 +134,7 @@ func (c *StaticCommand) Run(args []string) int { default: switch c.FlagVersion { case 0: - opts = append(opts, hostcatalogs.WithAutomaticVersioning()) + opts = append(opts, hostcatalogs.WithAutomaticVersioning(true)) default: version = uint32(c.FlagVersion) } @@ -141,7 +145,7 @@ func (c *StaticCommand) Run(args []string) int { switch c.Func { case "create": - catalog, apiErr, err = hostcatalogClient.Create(c.Context, "static", opts...) + catalog, apiErr, err = hostcatalogClient.Create(c.Context, "static", c.FlagScopeId, opts...) case "update": catalog, apiErr, err = hostcatalogClient.Update(c.Context, c.FlagId, version, opts...) } diff --git a/internal/cmd/commands/hosts/funcs.go b/internal/cmd/commands/hosts/funcs.go new file mode 100644 index 000000000..3f4f197a3 --- /dev/null +++ b/internal/cmd/commands/hosts/funcs.go @@ -0,0 +1,75 @@ +package hosts + +import ( + "fmt" + "time" + + "github.com/hashicorp/boundary/api/hosts" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func generateHostTableOutput(in *hosts.Host) string { + var ret []string + + nonAttributeMap := map[string]interface{}{ + "ID": in.Id, + "Scope ID": in.Scope.Id, + "Version": in.Version, + "Type": in.Type, + "Created Time": in.CreatedTime.Local().Format(time.RFC3339), + "Updated Time": in.UpdatedTime.Local().Format(time.RFC3339), + "Host Catalog ID": in.HostCatalogId, + } + + if in.Name != "" { + nonAttributeMap["Name"] = in.Name + } + if in.Description != "" { + nonAttributeMap["Description"] = in.Description + } + + maxLength := 0 + for k := range nonAttributeMap { + if len(k) > maxLength { + maxLength = len(k) + } + } + if len(in.Attributes) > 0 { + for k, v := range in.Attributes { + if attributeMap[k] != "" { + in.Attributes[attributeMap[k]] = v + delete(in.Attributes, k) + } + } + for k := range in.Attributes { + if len(k) > maxLength { + maxLength = len(k) + } + } + } + + ret = append(ret, "", "Host information:") + + ret = append(ret, + // We do +2 because there is another +2 offset for attributes below + base.WrapMap(2, maxLength+2, nonAttributeMap), + ) + + if len(in.HostSetIds) > 0 { + ret = append(ret, + " Host Set IDs:", + base.WrapSlice(4, in.HostSetIds), + ) + } + + if len(in.Attributes) > 0 { + ret = append(ret, + fmt.Sprintf(" Attributes: %s", ""), + base.WrapMap(4, maxLength, in.Attributes), + ) + } + + return base.WrapForHelpText(ret) +} + +var attributeMap = map[string]string{} diff --git a/internal/cmd/commands/hosts/host.go b/internal/cmd/commands/hosts/host.go new file mode 100644 index 000000000..f616f4f4a --- /dev/null +++ b/internal/cmd/commands/hosts/host.go @@ -0,0 +1,278 @@ +package hosts + +import ( + "fmt" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/hosts" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/boundary/internal/types/resource" + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*Command)(nil) +var _ cli.CommandAutocomplete = (*Command)(nil) + +type Command struct { + *base.Command + + Func string + + flagHostCatalogId string +} + +func (c *Command) Synopsis() string { + switch c.Func { + case "create": + return "Create host resources within Boundary" + case "update": + return "Update host resources within Boundary" + default: + return common.SynopsisFunc(c.Func, "host") + } +} + +var flagsMap = map[string][]string{ + "read": {"id"}, + "delete": {"id"}, + "list": {"host-catalog-id"}, +} + +func (c *Command) Help() string { + helpMap := common.HelpMap("host") + var helpStr string + switch c.Func { + case "": + return base.WrapForHelpText([]string{ + "Usage: boundary hosts [sub command] [options] [args]", + "", + " This command allows operations on Boundary host resources. Example:", + "", + " Read a host:", + "", + ` $ boundary hosts read -id hst_1234567890`, + "", + " Please see the hosts subcommand help for detailed usage information.", + }) + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary hosts create [type] [sub command] [options] [args]", + "", + " This command allows create operations on Boundary host resources. Example:", + "", + " Create a static-type host:", + "", + ` $ boundary hosts create static -name prodops -description "For ProdOps usage"`, + "", + " Please see the typed subcommand help for detailed usage information.", + }) + case "update": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary hosts update [type] [sub command] [options] [args]", + "", + " This command allows update operations on Boundary host resources. Example:", + "", + " Update a static-type host:", + "", + ` $ boundary hosts update static -id hst_1234567890 -name devops -description "For DevOps usage"`, + "", + " Please see the typed subcommand help for detailed usage information.", + }) + default: + helpStr = helpMap[c.Func]() + } + return helpStr + c.Flags().Help() +} + +func (c *Command) Flags() *base.FlagSets { + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + + f := set.NewFlagSet("Command Options") + + if len(flagsMap[c.Func]) > 0 { + common.PopulateCommonFlags(c.Command, f, resource.Host.String(), flagsMap[c.Func]) + } + + for _, name := range flagsMap[c.Func] { + switch name { + case "host-catalog-id": + f.StringVar(&base.StringVar{ + Name: "host-catalog-id", + Target: &c.flagHostCatalogId, + Usage: "The host-catalog resource in which to create or update the host resource", + }) + } + } + + return set +} + +func (c *Command) AutocompleteArgs() complete.Predictor { + return complete.PredictAnything +} + +func (c *Command) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *Command) Run(args []string) int { + switch c.Func { + case "", "create", "update": + return cli.RunResultHelp + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" { + c.UI.Error("ID is required but not passed in via -id") + return 1 + } + if strutil.StrListContains(flagsMap[c.Func], "host-catalog-id") && c.flagHostCatalogId == "" { + c.UI.Error("Host Catalog ID must be passed in via -host-catalog-id") + return 1 + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error())) + return 2 + } + + var opts []hosts.Option + + switch c.FlagName { + case "": + case "null": + opts = append(opts, hosts.DefaultName()) + default: + opts = append(opts, hosts.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, hosts.DefaultDescription()) + default: + opts = append(opts, hosts.WithDescription(c.FlagDescription)) + } + + hostClient := hosts.NewClient(client) + + var existed bool + var host *hosts.Host + var listedHosts []*hosts.Host + var apiErr *api.Error + + switch c.Func { + case "read": + host, apiErr, err = hostClient.Read(c.Context, c.FlagId, opts...) + case "delete": + existed, apiErr, err = hostClient.Delete(c.Context, c.FlagId, opts...) + case "list": + listedHosts, apiErr, err = hostClient.List(c.Context, c.flagHostCatalogId, opts...) + } + + plural := "host" + if c.Func == "list" { + plural = "hosts" + } + if err != nil { + c.UI.Error(fmt.Sprintf("Error trying to %s %s: %s", c.Func, plural, err.Error())) + return 2 + } + if apiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing %s on %s: %s", c.Func, plural, pretty.Sprint(apiErr))) + return 1 + } + + switch c.Func { + case "delete": + switch base.Format(c.UI) { + case "json": + c.UI.Output("null") + case "table": + output := "The delete operation completed successfully" + switch existed { + case true: + output += "." + default: + output += ", however the resource did not exist at the time." + } + c.UI.Output(output) + } + return 0 + + case "list": + switch base.Format(c.UI) { + case "json": + if len(listedHosts) == 0 { + c.UI.Output("null") + return 0 + } + b, err := base.JsonFormatter{}.Format(listedHosts) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting as JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + + case "table": + if len(listedHosts) == 0 { + c.UI.Output("No hosts found") + return 0 + } + var output []string + output = []string{ + "", + "Host information:", + } + for i, m := range listedHosts { + if i > 0 { + output = append(output, "") + } + if true { + output = append(output, + fmt.Sprintf(" ID: %s", m.Id), + fmt.Sprintf(" Version: %d", m.Version), + fmt.Sprintf(" Type: %s", m.Type), + ) + } + if m.Name != "" { + output = append(output, + fmt.Sprintf(" Name: %s", m.Name), + ) + } + if m.Description != "" { + output = append(output, + fmt.Sprintf(" Description: %s", m.Description), + ) + } + } + c.UI.Output(base.WrapForHelpText(output)) + } + return 0 + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateHostTableOutput(host)) + case "json": + b, err := base.JsonFormatter{}.Format(host) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting as JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + } + + return 0 +} diff --git a/internal/cmd/commands/hosts/host_create.go b/internal/cmd/commands/hosts/host_create.go deleted file mode 100644 index a750f5ee7..000000000 --- a/internal/cmd/commands/hosts/host_create.go +++ /dev/null @@ -1,131 +0,0 @@ -package hosts - -import ( - "fmt" - "strings" - - "github.com/hashicorp/boundary/api/hosts" - "github.com/hashicorp/boundary/internal/cmd/base" - "github.com/kr/pretty" - "github.com/mitchellh/cli" - "github.com/posener/complete" -) - -var _ cli.Command = (*CreateCommand)(nil) -var _ cli.CommandAutocomplete = (*CreateCommand)(nil) - -type CreateCommand struct { - *base.Command - - flagAddress string - flagName string - flagCatalogId string - flagDescription string -} - -func (c *CreateCommand) Synopsis() string { - return "Creates a host in the given host catalog" -} - -func (c *CreateCommand) Help() string { - helpText := ` -Usage: boundary hosts create - - Creates a host in the given host catalog. This command will result in an - error for any catalog that does not support manual host creation. - - Example: - - $ boundary hosts create -catalog-id= -address= -name= - -` + c.Flags().Help() - - return strings.TrimSpace(helpText) -} - -func (c *CreateCommand) Flags() *base.FlagSets { - set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) - - f := set.NewFlagSet("Command Options") - - f.StringVar(&base.StringVar{ - Name: "address", - Target: &c.flagAddress, - Completion: complete.PredictAnything, - Usage: "The host's address; can be an IP address or DNS name", - }) - - f.StringVar(&base.StringVar{ - Name: "name", - Target: &c.flagName, - Completion: complete.PredictAnything, - Usage: "An optional name assigned to the host for display purposes", - }) - - f.StringVar(&base.StringVar{ - Name: "catalog-id", - Target: &c.flagCatalogId, - Completion: complete.PredictAnything, - Usage: "The ID of the host catalog in which the host should be created", - }) - - f.StringVar(&base.StringVar{ - Name: "description", - Target: &c.flagDescription, - Completion: complete.PredictNothing, - Usage: "An optional description assigned to the host for display purposes", - }) - - return set -} - -func (c *CreateCommand) AutocompleteArgs() complete.Predictor { - return complete.PredictAnything -} - -func (c *CreateCommand) AutocompleteFlags() complete.Flags { - return c.Flags().Completions() -} - -func (c *CreateCommand) Run(args []string) int { - f := c.Flags() - - if err := f.Parse(args); err != nil { - c.UI.Error(err.Error()) - return 1 - } - - switch { - case c.flagCatalogId == "": - c.UI.Error("Catalog ID must be provided via -catalog-id") - return 1 - case c.flagAddress == "": - c.UI.Error("Host address must be provided via -address") - return 1 - } - - client, err := c.Client() - if err != nil { - c.UI.Error(err.Error()) - return 2 - } - - host, apiErr, err := hosts.NewClient(client).Create(c.Context, - c.flagCatalogId, - hosts.WithStaticHostAddress(c.flagAddress), - hosts.WithName(c.flagName), - hosts.WithDescription(c.flagDescription)) - - switch { - case err != nil: - c.UI.Error(fmt.Errorf("error creating host: %w", err).Error()) - return 2 - case apiErr != nil: - c.UI.Error(pretty.Sprint(apiErr)) - return 2 - default: - c.UI.Info(pretty.Sprint(host)) - } - - return 0 -} diff --git a/internal/cmd/commands/hosts/static.go b/internal/cmd/commands/hosts/static.go new file mode 100644 index 000000000..67dab8ed8 --- /dev/null +++ b/internal/cmd/commands/hosts/static.go @@ -0,0 +1,216 @@ +package hosts + +import ( + "fmt" + "net/textproto" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/hosts" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*StaticCommand)(nil) +var _ cli.CommandAutocomplete = (*StaticCommand)(nil) + +type StaticCommand struct { + *base.Command + + Func string + + flagHostCatalogId string + flagAddress string +} + +func (c *StaticCommand) Synopsis() string { + return fmt.Sprintf("%s a static-type host within Boundary", textproto.CanonicalMIMEHeaderKey(c.Func)) +} + +var staticFlagsMap = map[string][]string{ + "create": {"host-catalog-id", "name", "description", "address"}, + "update": {"id", "name", "description", "version", "address"}, +} + +func (c *StaticCommand) Help() string { + var info string + switch c.Func { + case "create": + info = base.WrapForHelpText([]string{ + "Usage: boundary hosts static create [options] [args]", + "", + " Create a static-type host. Example:", + "", + ` $ boundary hosts static create -name prodops -description "Static host for ProdOps" -address "127.0.0.1"`, + "", + "", + }) + + case "update": + info = base.WrapForHelpText([]string{ + "Usage: boundary hosts static update [options] [args]", + "", + " Update a static-type host given its ID. Example:", + "", + ` $ boundary hosts static update -id hst_1234567890 -name "devops" -description "Static host for DevOps" -address "10.20.30.40"`, + "", + "", + }) + } + return info + c.Flags().Help() +} + +func (c *StaticCommand) Flags() *base.FlagSets { + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + + f := set.NewFlagSet("Command Options") + + if len(staticFlagsMap[c.Func]) > 0 { + common.PopulateCommonFlags(c.Command, f, "static-type host", staticFlagsMap[c.Func]) + } + + for _, name := range staticFlagsMap[c.Func] { + switch name { + case "host-catalog-id": + f.StringVar(&base.StringVar{ + Name: "host-catalog-id", + Target: &c.flagHostCatalogId, + Usage: "The host-catalog resource in which to create or update the host resource", + }) + } + } + + f = set.NewFlagSet("Static Host Options") + + for _, name := range staticFlagsMap[c.Func] { + switch name { + case "address": + f.StringVar(&base.StringVar{ + Name: "address", + Target: &c.flagAddress, + Usage: "The address of the host", + }) + } + } + + return set +} + +func (c *StaticCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictAnything +} + +func (c *StaticCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *StaticCommand) Run(args []string) int { + if c.Func == "" { + return cli.RunResultHelp + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if strutil.StrListContains(staticFlagsMap[c.Func], "id") && c.FlagId == "" { + c.UI.Error("ID is required but not passed in via -id") + return 1 + } + if strutil.StrListContains(staticFlagsMap[c.Func], "host-catalog-id") && c.flagHostCatalogId == "" { + c.UI.Error("Host Catalog ID must be passed in via -host-catalog-id") + return 1 + } + if c.Func == "create" && c.flagAddress == "" { + c.UI.Error("Address must be passed in via -address") + return 1 + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error())) + return 2 + } + + var opts []hosts.Option + + switch c.FlagName { + case "": + case "null": + opts = append(opts, hosts.DefaultName()) + default: + opts = append(opts, hosts.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, hosts.DefaultDescription()) + default: + opts = append(opts, hosts.WithDescription(c.FlagDescription)) + } + + switch c.flagAddress { + case "": + case "null": + opts = append(opts, hosts.DefaultStaticHostAddress()) + default: + opts = append(opts, hosts.WithStaticHostAddress(c.flagAddress)) + } + + hostClient := hosts.NewClient(client) + + // Perform check-and-set when needed + var version uint32 + switch c.Func { + case "create": + // These don't update so don't need the existing version + default: + switch c.FlagVersion { + case 0: + opts = append(opts, hosts.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + } + + var host *hosts.Host + var apiErr *api.Error + + switch c.Func { + case "create": + host, apiErr, err = hostClient.Create(c.Context, c.flagHostCatalogId, opts...) + case "update": + host, apiErr, err = hostClient.Update(c.Context, c.FlagId, version, opts...) + } + + plural := "static-type host" + if err != nil { + c.UI.Error(fmt.Sprintf("Error trying to %s %s: %s", c.Func, plural, err.Error())) + return 2 + } + if apiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing %s on %s: %s", c.Func, plural, pretty.Sprint(apiErr))) + return 1 + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateHostTableOutput(host)) + case "json": + b, err := base.JsonFormatter{}.Format(host) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting as JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + } + + return 0 +} diff --git a/internal/cmd/commands/hostsets/funcs.go b/internal/cmd/commands/hostsets/funcs.go index a58ea3ab7..a1ebf19a8 100644 --- a/internal/cmd/commands/hostsets/funcs.go +++ b/internal/cmd/commands/hostsets/funcs.go @@ -13,6 +13,7 @@ func generateHostSetTableOutput(in *hostsets.HostSet) string { nonAttributeMap := map[string]interface{}{ "ID": in.Id, + "Scope ID": in.Scope.Id, "Version": in.Version, "Type": in.Type, "Created Time": in.CreatedTime.Local().Format(time.RFC3339), @@ -55,23 +56,15 @@ func generateHostSetTableOutput(in *hostsets.HostSet) string { ) if len(in.HostIds) > 0 { - if true { - ret = append(ret, - fmt.Sprintf(" Host IDs:"), - ) - } ret = append(ret, + " Host IDs:", base.WrapSlice(4, in.HostIds), ) } if len(in.Attributes) > 0 { - if true { - ret = append(ret, - fmt.Sprintf(" Attributes: %s", ""), - ) - } ret = append(ret, + fmt.Sprintf(" Attributes: %s", ""), base.WrapMap(4, maxLength, in.Attributes), ) } diff --git a/internal/cmd/commands/hostsets/hostset.go b/internal/cmd/commands/hostsets/hostset.go index d2f37839e..1a476b7b6 100644 --- a/internal/cmd/commands/hostsets/hostset.go +++ b/internal/cmd/commands/hostsets/hostset.go @@ -46,6 +46,7 @@ func (c *Command) Synopsis() string { var flagsMap = map[string][]string{ "read": {"id"}, "delete": {"id"}, + "list": {"host-catalog-id"}, "add-hosts": {"id", "host", "version"}, "set-hosts": {"id", "host", "version"}, "remove-hosts": {"id", "host", "version"}, @@ -53,6 +54,7 @@ var flagsMap = map[string][]string{ func (c *Command) Help() string { helpMap := common.HelpMap("host-set") + var helpStr string switch c.Func { case "": return base.WrapForHelpText([]string{ @@ -67,7 +69,7 @@ func (c *Command) Help() string { " Please see the host-sets subcommand help for detailed usage information.", }) case "create": - return base.WrapForHelpText([]string{ + helpStr = base.WrapForHelpText([]string{ "Usage: boundary host-sets create [type] [sub command] [options] [args]", "", " This command allows create operations on Boundary host-set resources. Example:", @@ -79,7 +81,7 @@ func (c *Command) Help() string { " Please see the typed subcommand help for detailed usage information.", }) case "update": - return base.WrapForHelpText([]string{ + helpStr = base.WrapForHelpText([]string{ "Usage: boundary host-sets update [type] [sub command] [options] [args]", "", " This command allows update operations on Boundary host-set resources. Example:", @@ -91,7 +93,7 @@ func (c *Command) Help() string { " Please see the typed subcommand help for detailed usage information.", }) case "add-hosts": - return base.WrapForHelpText([]string{ + helpStr = base.WrapForHelpText([]string{ "Usage: boundary host-sets add-hosts [sub command] [options] [args]", "", " This command allows adding hosts to host-set resources, if the types match and the operation is allowed by the given host-set type. Example:", @@ -101,7 +103,7 @@ func (c *Command) Help() string { ` $ boundary host-sets add-hosts -id hsst_1234567890 -host hst_1234567890 -host hst_0987654321`, }) case "remove-hosts": - return base.WrapForHelpText([]string{ + helpStr = base.WrapForHelpText([]string{ "Usage: boundary host-sets remove-hosts [sub command] [options] [args]", "", " This command allows removing hosts from host-set resources, if the types match and the operation is allowed by the given host-set type. Example:", @@ -111,7 +113,7 @@ func (c *Command) Help() string { ` $ boundary host-sets remove-hosts -id hsst_1234567890 -host hst_0987654321`, }) case "set-hosts": - return base.WrapForHelpText([]string{ + helpStr = base.WrapForHelpText([]string{ "Usage: boundary host-sets set-hosts [sub command] [options] [args]", "", " This command allows setting the complete set of hosts on host-set resources, if the types match and the operation is allowed by the given host-set type. Example:", @@ -121,8 +123,9 @@ func (c *Command) Help() string { ` $ boundary host-sets remove-hosts -id hsst_1234567890 -host hst_1234567890`, }) default: - return helpMap[c.Func]() + c.Flags().Help() + helpStr = helpMap[c.Func]() } + return helpStr + c.Flags().Help() } func (c *Command) Flags() *base.FlagSets { @@ -134,14 +137,14 @@ func (c *Command) Flags() *base.FlagSets { common.PopulateCommonFlags(c.Command, f, resource.HostSet.String(), flagsMap[c.Func]) } - f.StringVar(&base.StringVar{ - Name: "host-catalog-id", - Target: &c.flagHostCatalogId, - Usage: "The host-catalog resource in which to create or update the host-set resource", - }) - for _, name := range flagsMap[c.Func] { switch name { + case "host-catalog-id": + f.StringVar(&base.StringVar{ + Name: "host-catalog-id", + Target: &c.flagHostCatalogId, + Usage: "The host-catalog resource in which to create or update the host-set resource", + }) case "host": f.StringSliceVar(&base.StringSliceVar{ Name: "host", @@ -174,11 +177,14 @@ func (c *Command) Run(args []string) int { c.UI.Error(err.Error()) return 1 } - if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(flagsMap[c.Func], "host-catalog-id") && c.flagHostCatalogId == "" { + c.UI.Error("Host Catalog ID must be passed in via -host-catalog-id") + return 1 + } client, err := c.Client() if err != nil { @@ -232,7 +238,7 @@ func (c *Command) Run(args []string) int { case "add-hosts", "remove-hosts", "set-hosts": switch c.FlagVersion { case 0: - opts = append(opts, hostsets.WithAutomaticVersioning()) + opts = append(opts, hostsets.WithAutomaticVersioning(true)) default: version = uint32(c.FlagVersion) } @@ -250,17 +256,17 @@ func (c *Command) Run(args []string) int { switch c.Func { case "read": - set, apiErr, err = hostsetClient.Read(c.Context, c.flagHostCatalogId, c.FlagId, opts...) + set, apiErr, err = hostsetClient.Read(c.Context, c.FlagId, opts...) case "delete": - existed, apiErr, err = hostsetClient.Delete(c.Context, c.flagHostCatalogId, c.FlagId, opts...) + existed, apiErr, err = hostsetClient.Delete(c.Context, c.FlagId, opts...) case "list": listedSets, apiErr, err = hostsetClient.List(c.Context, c.flagHostCatalogId, opts...) case "add-hosts": - set, apiErr, err = hostsetClient.AddHosts(c.Context, c.flagHostCatalogId, c.FlagId, version, c.flagHosts, opts...) + set, apiErr, err = hostsetClient.AddHosts(c.Context, c.FlagId, version, c.flagHosts, opts...) case "remove-hosts": - set, apiErr, err = hostsetClient.RemoveHosts(c.Context, c.flagHostCatalogId, c.FlagId, version, c.flagHosts, opts...) + set, apiErr, err = hostsetClient.RemoveHosts(c.Context, c.FlagId, version, c.flagHosts, opts...) case "set-hosts": - set, apiErr, err = hostsetClient.SetHosts(c.Context, c.flagHostCatalogId, c.FlagId, version, c.flagHosts, opts...) + set, apiErr, err = hostsetClient.SetHosts(c.Context, c.FlagId, version, c.flagHosts, opts...) } plural := "host set" diff --git a/internal/cmd/commands/hostsets/static.go b/internal/cmd/commands/hostsets/static.go index 06b1155b7..77e6bcfd5 100644 --- a/internal/cmd/commands/hostsets/static.go +++ b/internal/cmd/commands/hostsets/static.go @@ -30,7 +30,7 @@ func (c *StaticCommand) Synopsis() string { } var staticFlagsMap = map[string][]string{ - "create": {"name", "description"}, + "create": {"host-catalog-id", "name", "description"}, "update": {"id", "name", "description", "version"}, } @@ -71,11 +71,16 @@ func (c *StaticCommand) Flags() *base.FlagSets { common.PopulateCommonFlags(c.Command, f, "static-type host-set", staticFlagsMap[c.Func]) } - f.StringVar(&base.StringVar{ - Name: "host-catalog-id", - Target: &c.flagHostCatalogId, - Usage: "The host-catalog resource in which to create or update the host-set resource", - }) + for _, name := range staticFlagsMap[c.Func] { + switch name { + case "host-catalog-id": + f.StringVar(&base.StringVar{ + Name: "host-catalog-id", + Target: &c.flagHostCatalogId, + Usage: "The host-catalog resource in which to create or update the host-set resource", + }) + } + } return set } @@ -104,8 +109,7 @@ func (c *StaticCommand) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } - - if c.flagHostCatalogId == "" { + if strutil.StrListContains(staticFlagsMap[c.Func], "host-catalog-id") && c.flagHostCatalogId == "" { c.UI.Error("Host Catalog ID must be passed in via -host-catalog-id") return 1 } @@ -144,7 +148,7 @@ func (c *StaticCommand) Run(args []string) int { default: switch c.FlagVersion { case 0: - opts = append(opts, hostsets.WithAutomaticVersioning()) + opts = append(opts, hostsets.WithAutomaticVersioning(true)) default: version = uint32(c.FlagVersion) } @@ -157,7 +161,7 @@ func (c *StaticCommand) Run(args []string) int { case "create": set, apiErr, err = hostsetClient.Create(c.Context, c.flagHostCatalogId, opts...) case "update": - set, apiErr, err = hostsetClient.Update(c.Context, c.flagHostCatalogId, c.FlagId, version, opts...) + set, apiErr, err = hostsetClient.Update(c.Context, c.FlagId, version, opts...) } plural := "static-type host-set" diff --git a/internal/cmd/commands/roles/funcs.go b/internal/cmd/commands/roles/funcs.go index befdae0d5..e2cef233b 100644 --- a/internal/cmd/commands/roles/funcs.go +++ b/internal/cmd/commands/roles/funcs.go @@ -91,6 +91,7 @@ func generateRoleTableOutput(in *roles.Role) string { nonAttributeMap := map[string]interface{}{ "ID": in.Id, + "Scope ID": in.Scope.Id, "Version": in.Version, "Created Time": in.CreatedTime.Local().Format(time.RFC3339), "Updated Time": in.UpdatedTime.Local().Format(time.RFC3339), diff --git a/internal/cmd/commands/roles/role.go b/internal/cmd/commands/roles/role.go index 161a7f5ee..e9f502d49 100644 --- a/internal/cmd/commands/roles/role.go +++ b/internal/cmd/commands/roles/role.go @@ -22,6 +22,7 @@ type Command struct { Func string + flagScope string flagGrantScopeId string flagPrincipals []string flagGrants []string @@ -51,10 +52,11 @@ var helpMap = func() map[string]func() string { } var flagsMap = map[string][]string{ - "create": {"name", "description", "grantscopeid"}, + "create": {"scope-id", "name", "description", "grantscopeid"}, "update": {"id", "name", "description", "grantscopeid", "version"}, "read": {"id"}, "delete": {"id"}, + "list": {"scope-id"}, "add-principals": {"id", "principal", "version"}, "set-principals": {"id", "principal", "version"}, "remove-principals": {"id", "principal", "version"}, @@ -105,6 +107,10 @@ func (c *Command) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(flagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } client, err := c.Client() if err != nil { @@ -198,7 +204,7 @@ func (c *Command) Run(args []string) int { default: switch c.FlagVersion { case 0: - opts = append(opts, roles.WithAutomaticVersioning()) + opts = append(opts, roles.WithAutomaticVersioning(true)) default: version = uint32(c.FlagVersion) } @@ -211,7 +217,7 @@ func (c *Command) Run(args []string) int { switch c.Func { case "create": - role, apiErr, err = roleClient.Create(c.Context, opts...) + role, apiErr, err = roleClient.Create(c.Context, c.FlagScopeId, opts...) case "update": role, apiErr, err = roleClient.Update(c.Context, c.FlagId, version, opts...) case "read": @@ -219,7 +225,7 @@ func (c *Command) Run(args []string) int { case "delete": existed, apiErr, err = roleClient.Delete(c.Context, c.FlagId, opts...) case "list": - listedRoles, apiErr, err = roleClient.List(c.Context, opts...) + listedRoles, apiErr, err = roleClient.List(c.Context, c.FlagScopeId, opts...) case "add-principals": role, apiErr, err = roleClient.AddPrincipals(c.Context, c.FlagId, version, principals, opts...) case "set-principals": diff --git a/internal/cmd/commands/scopes/funcs.go b/internal/cmd/commands/scopes/funcs.go index a4007821b..94c2039b3 100644 --- a/internal/cmd/commands/scopes/funcs.go +++ b/internal/cmd/commands/scopes/funcs.go @@ -12,6 +12,7 @@ func generateScopeTableOutput(in *scopes.Scope) string { nonAttributeMap := map[string]interface{}{ "ID": in.Id, + "Scope ID": in.Scope.Id, "Version": in.Version, "Created Time": in.CreatedTime.Local().Format(time.RFC3339), "Updated Time": in.UpdatedTime.Local().Format(time.RFC3339), diff --git a/internal/cmd/commands/scopes/scope.go b/internal/cmd/commands/scopes/scope.go index 91e94703b..ec06e80e5 100644 --- a/internal/cmd/commands/scopes/scope.go +++ b/internal/cmd/commands/scopes/scope.go @@ -30,10 +30,11 @@ func (c *Command) Synopsis() string { } var flagsMap = map[string][]string{ - "create": {"name", "description", "skip-role-creation"}, + "create": {"scope-id", "name", "description", "skip-role-creation"}, "update": {"id", "name", "description", "version"}, "read": {"id"}, "delete": {"id"}, + "list": {"scope-id"}, } func (c *Command) Help() string { @@ -86,6 +87,10 @@ func (c *Command) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(flagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } client, err := c.Client() if err != nil { @@ -123,7 +128,7 @@ func (c *Command) Run(args []string) int { default: switch c.FlagVersion { case 0: - opts = append(opts, scopes.WithAutomaticVersioning()) + opts = append(opts, scopes.WithAutomaticVersioning(true)) default: version = uint32(c.FlagVersion) } @@ -136,7 +141,7 @@ func (c *Command) Run(args []string) int { switch c.Func { case "create": - scope, apiErr, err = scopeClient.Create(c.Context, client.ScopeId(), opts...) + scope, apiErr, err = scopeClient.Create(c.Context, c.FlagScopeId, opts...) case "update": scope, apiErr, err = scopeClient.Update(c.Context, c.FlagId, version, opts...) case "read": @@ -144,7 +149,7 @@ func (c *Command) Run(args []string) int { case "delete": existed, apiErr, err = scopeClient.Delete(c.Context, c.FlagId, opts...) case "list": - listedScopes, apiErr, err = scopeClient.List(c.Context, client.ScopeId(), opts...) + listedScopes, apiErr, err = scopeClient.List(c.Context, c.FlagScopeId, opts...) } plural := "scope" diff --git a/internal/cmd/commands/targets/funcs.go b/internal/cmd/commands/targets/funcs.go new file mode 100644 index 000000000..7c8e31070 --- /dev/null +++ b/internal/cmd/commands/targets/funcs.go @@ -0,0 +1,101 @@ +package targets + +import ( + "fmt" + "time" + + "github.com/hashicorp/boundary/api/scopes" + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/internal/cmd/base" +) + +func generateTargetTableOutput(in *targets.Target) string { + var ret []string + + nonAttributeMap := map[string]interface{}{ + "ID": in.Id, + "Scope ID": in.Scope.Id, + "Version": in.Version, + "Type": in.Type, + "Created Time": in.CreatedTime.Local().Format(time.RFC3339), + "Updated Time": in.UpdatedTime.Local().Format(time.RFC3339), + "Default Port": in.DefaultPort, + } + + if in.Name != "" { + nonAttributeMap["Name"] = in.Name + } + if in.Description != "" { + nonAttributeMap["Description"] = in.Description + } + + maxLength := 0 + for k := range nonAttributeMap { + if len(k) > maxLength { + maxLength = len(k) + } + } + + var hostSetMaps []map[string]interface{} + if len(in.HostSets) > 0 { + for _, set := range in.HostSets { + m := map[string]interface{}{ + "ID": set.Id, + "Host Catalog ID": set.HostCatalogId, + } + hostSetMaps = append(hostSetMaps, m) + } + if l := len("Host Catalog ID"); l > maxLength { + maxLength = l + } + } + ret = append(ret, "", "Target information:") + + ret = append(ret, + // We do +2 because there is another +2 offset for host sets below + base.WrapMap(2, maxLength+2, nonAttributeMap), + ) + + if len(in.HostSets) > 0 { + ret = append(ret, + fmt.Sprintf(" Host Sets: %s", ""), + ) + for _, m := range hostSetMaps { + ret = append(ret, + base.WrapMap(4, maxLength, m), + "", + ) + } + } + + return base.WrapForHelpText(ret) +} + +func exampleOutput() string { + in := &targets.Target{ + Id: "ttcp_1234567890", + ScopeId: "global", + Scope: &scopes.ScopeInfo{ + Id: "global", + }, + Name: "foo", + Description: "The bar of foos", + CreatedTime: time.Now().Add(-5 * time.Minute), + UpdatedTime: time.Now(), + Version: 3, + Type: "tcp", + HostSetIds: []string{"hsst_1234567890", "hsst_0987654321"}, + HostSets: []*targets.HostSet{ + { + Id: "hsst_1234567890", + HostCatalogId: "hcst_1234567890", + }, + { + Id: "hsst_0987654321", + HostCatalogId: "hcst_1234567890", + }, + }, + DefaultPort: 22, + } + return generateTargetTableOutput(in) +} diff --git a/internal/cmd/commands/targets/target.go b/internal/cmd/commands/targets/target.go new file mode 100644 index 000000000..1498da371 --- /dev/null +++ b/internal/cmd/commands/targets/target.go @@ -0,0 +1,360 @@ +package targets + +import ( + "fmt" + "os" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/boundary/internal/types/resource" + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*Command)(nil) +var _ cli.CommandAutocomplete = (*Command)(nil) + +type Command struct { + *base.Command + + Func string + + flagHostSets []string +} + +func (c *Command) Synopsis() string { + switch c.Func { + case "create": + return "Create target resources within Boundary" + case "update": + return "Update target resources within Boundary" + default: + return common.SynopsisFunc(c.Func, "target") + } +} + +var flagsMap = map[string][]string{ + "read": {"id"}, + "delete": {"id"}, + "list": {"scope-id"}, + "add-host-sets": {"id", "host-set", "version"}, + "remove-host-sets": {"id", "host-set", "version"}, + "set-host-sets": {"id", "host-set", "version"}, +} + +func (c *Command) Help() string { + helpMap := common.HelpMap("target") + var helpStr string + switch c.Func { + case "": + return base.WrapForHelpText([]string{ + "Usage: boundary targets [sub command] [options] [args]", + "", + " This command allows operations on Boundary target resources. Example:", + "", + " Read a target:", + "", + ` $ boundary targets read -id ttcp_1234567890`, + "", + " Please see the targets subcommand help for detailed usage information.", + }) + case "create": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary targets create [type] [sub command] [options] [args]", + "", + " This command allows create operations on Boundary target resources. Example:", + "", + " Create a tcp-type target:", + "", + ` $ boundary targets create tcp -name prodops -description "For ProdOps usage"`, + "", + " Please see the typed subcommand help for detailed usage information.", + }) + case "update": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary targets update [type] [sub command] [options] [args]", + "", + " This command allows update operations on Boundary target resources. Example:", + "", + " Update a tcp-type target:", + "", + ` $ boundary targets update tcp -id ttcp_1234567890 -name devops -description "For DevOps usage"`, + "", + " Please see the typed subcommand help for detailed usage information.", + }) + case "add-host-sets": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary target add-host-sets [sub command] [options] [args]", + "", + " This command allows adding host-set resources to target resources. Example:", + "", + " Add host-set resources to a tcp-type target:", + "", + ` $ boundary targets add-host-sets -id ttcp_1234567890 -host-set hsst_1234567890 -host-set hsst_0987654321`, + }) + case "remove-host-sets": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary target remove-host-sets [sub command] [options] [args]", + "", + " This command allows removing host-set resources from target resources. Example:", + "", + " Remove host-set resources from a tcp-type target:", + "", + ` $ boundary targets add-host-sets -id ttcp_1234567890 -host hsst_1234567890 -host-set hsst_0987654321`, + }) + case "set-host-sets": + helpStr = base.WrapForHelpText([]string{ + "Usage: boundary target set-host-sets [sub command] [options] [args]", + "", + " This command allows setting the complete set of host-set resources on a target resource. Example:", + "", + " Set host-set resources on a tcp-type target:", + "", + ` $ boundary targets set-host-sets -id ttcp_1234567890 -host-set hsst_1234567890`, + }) + default: + helpStr = helpMap[c.Func]() + } + return helpStr + c.Flags().Help() +} + +func (c *Command) Flags() *base.FlagSets { + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + + f := set.NewFlagSet("Command Options") + + if len(flagsMap[c.Func]) > 0 { + common.PopulateCommonFlags(c.Command, f, resource.Target.String(), flagsMap[c.Func]) + } + + for _, name := range flagsMap[c.Func] { + switch name { + case "host-set": + f.StringSliceVar(&base.StringSliceVar{ + Name: "host-set", + Target: &c.flagHostSets, + Usage: "The host-set resources to add, remove, or set. May be specified multiple times.", + }) + } + } + + return set +} + +func (c *Command) AutocompleteArgs() complete.Predictor { + return complete.PredictAnything +} + +func (c *Command) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *Command) Run(args []string) int { + if os.Getenv("BOUNDARY_EXAMPLE_CLI_OUTPUT") != "" { + c.UI.Output(exampleOutput()) + return 0 + } + + switch c.Func { + case "", "create", "update": + return cli.RunResultHelp + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" { + c.UI.Error("ID is required but not passed in via -id") + return 1 + } + if strutil.StrListContains(flagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error())) + return 2 + } + + var opts []targets.Option + + switch c.FlagName { + case "": + case "null": + opts = append(opts, targets.DefaultName()) + default: + opts = append(opts, targets.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, targets.DefaultDescription()) + default: + opts = append(opts, targets.WithDescription(c.FlagDescription)) + } + + hostSets := c.flagHostSets + switch c.Func { + case "add-host-sets", "remove-host-sets": + if len(c.flagHostSets) == 0 { + c.UI.Error("No host-sets supplied via -host-set") + return 1 + } + + case "set-host-sets": + switch len(c.flagHostSets) { + case 0: + case 1: + if c.flagHostSets[0] == "null" { + hostSets = []string{} + } + } + if hostSets == nil { + c.UI.Error("No host-sets supplied via -host-set") + return 1 + } + } + + // Perform check-and-set when needed + var version uint32 + switch c.Func { + case "add-host-sets", "remove-host-sets", "set-host-sets": + switch c.FlagVersion { + case 0: + opts = append(opts, targets.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + default: + // The only other one that needs it is update, handled by the static + // file + } + + targetClient := targets.NewClient(client) + + var existed bool + var target *targets.Target + var listedCatalogs []*targets.Target + var apiErr *api.Error + + switch c.Func { + case "read": + target, apiErr, err = targetClient.Read(c.Context, c.FlagId, opts...) + case "delete": + existed, apiErr, err = targetClient.Delete(c.Context, c.FlagId, opts...) + case "list": + listedCatalogs, apiErr, err = targetClient.List(c.Context, c.FlagScopeId, opts...) + case "add-host-sets": + target, apiErr, err = targetClient.AddHostSets(c.Context, c.FlagId, version, c.flagHostSets, opts...) + case "remove-host-sets": + target, apiErr, err = targetClient.RemoveHostSets(c.Context, c.FlagId, version, c.flagHostSets, opts...) + case "set-host-sets": + target, apiErr, err = targetClient.SetHostSets(c.Context, c.FlagId, version, c.flagHostSets, opts...) + } + + plural := "target" + if c.Func == "list" { + plural = "targets" + } + if err != nil { + c.UI.Error(fmt.Sprintf("Error trying to %s %s: %s", c.Func, plural, err.Error())) + return 2 + } + if apiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing %s on %s: %s", c.Func, plural, pretty.Sprint(apiErr))) + return 1 + } + + switch c.Func { + case "delete": + switch base.Format(c.UI) { + case "json": + c.UI.Output("null") + case "table": + output := "The delete operation completed successfully" + switch existed { + case true: + output += "." + default: + output += ", however the resource did not exist at the time." + } + c.UI.Output(output) + } + return 0 + + case "list": + switch base.Format(c.UI) { + case "json": + if len(listedCatalogs) == 0 { + c.UI.Output("null") + return 0 + } + b, err := base.JsonFormatter{}.Format(listedCatalogs) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting as JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + + case "table": + if len(listedCatalogs) == 0 { + c.UI.Output("No targets found") + return 0 + } + var output []string + output = []string{ + "", + "Target information:", + } + for i, m := range listedCatalogs { + if i > 0 { + output = append(output, "") + } + if true { + output = append(output, + fmt.Sprintf(" ID: %s", m.Id), + fmt.Sprintf(" Version: %d", m.Version), + fmt.Sprintf(" Type: %s", m.Type), + ) + } + if m.Name != "" { + output = append(output, + fmt.Sprintf(" Name: %s", m.Name), + ) + } + if m.Description != "" { + output = append(output, + fmt.Sprintf(" Description: %s", m.Description), + ) + } + } + c.UI.Output(base.WrapForHelpText(output)) + } + return 0 + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateTargetTableOutput(target)) + case "json": + b, err := base.JsonFormatter{}.Format(target) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting as JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + } + + return 0 +} diff --git a/internal/cmd/commands/targets/tcp.go b/internal/cmd/commands/targets/tcp.go new file mode 100644 index 000000000..d104c82ee --- /dev/null +++ b/internal/cmd/commands/targets/tcp.go @@ -0,0 +1,176 @@ +package targets + +import ( + "fmt" + "net/textproto" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/cmd/common" + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*TcpCommand)(nil) +var _ cli.CommandAutocomplete = (*TcpCommand)(nil) + +type TcpCommand struct { + *base.Command + + Func string +} + +func (c *TcpCommand) Synopsis() string { + return fmt.Sprintf("%s a tcp-type target within Boundary", textproto.CanonicalMIMEHeaderKey(c.Func)) +} + +var tcpFlagsMap = map[string][]string{ + "create": {"scope-id", "name", "description"}, + "update": {"id", "name", "description", "version"}, +} + +func (c *TcpCommand) Help() string { + var info string + switch c.Func { + case "create": + info = base.WrapForHelpText([]string{ + "Usage: boundary targets tcp create [options] [args]", + "", + " Create a tcp-type target. Example:", + "", + ` $ boundary targets tcp create -name prodops -description "Tcp target for ProdOps"`, + "", + "", + }) + + case "update": + info = base.WrapForHelpText([]string{ + "Usage: boundary targets tcp update [options] [args]", + "", + " Update a tcp-type target given its ID. Example:", + "", + ` $ boundary targets tcp update -id ttcp_1234567890 -name "devops" -description "Tcp target for DevOps"`, + "", + "", + }) + } + return info + c.Flags().Help() +} + +func (c *TcpCommand) Flags() *base.FlagSets { + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + + if len(tcpFlagsMap[c.Func]) > 0 { + f := set.NewFlagSet("Command Options") + common.PopulateCommonFlags(c.Command, f, "tcp-type target", tcpFlagsMap[c.Func]) + } + + return set +} + +func (c *TcpCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictAnything +} + +func (c *TcpCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *TcpCommand) Run(args []string) int { + if c.Func == "" { + return cli.RunResultHelp + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if strutil.StrListContains(tcpFlagsMap[c.Func], "id") && c.FlagId == "" { + c.UI.Error("ID is required but not passed in via -id") + return 1 + } + if strutil.StrListContains(tcpFlagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error())) + return 2 + } + + var opts []targets.Option + + switch c.FlagName { + case "": + case "null": + opts = append(opts, targets.DefaultName()) + default: + opts = append(opts, targets.WithName(c.FlagName)) + } + + switch c.FlagDescription { + case "": + case "null": + opts = append(opts, targets.DefaultDescription()) + default: + opts = append(opts, targets.WithDescription(c.FlagDescription)) + } + + targetClient := targets.NewClient(client) + + // Perform check-and-set when needed + var version uint32 + switch c.Func { + case "create": + // These don't update so don't need the existing version + default: + switch c.FlagVersion { + case 0: + opts = append(opts, targets.WithAutomaticVersioning(true)) + default: + version = uint32(c.FlagVersion) + } + } + + var catalog *targets.Target + var apiErr *api.Error + + switch c.Func { + case "create": + catalog, apiErr, err = targetClient.Create(c.Context, "tcp", c.FlagScopeId, opts...) + case "update": + catalog, apiErr, err = targetClient.Update(c.Context, c.FlagId, version, opts...) + } + + plural := "tcp-type target" + if err != nil { + c.UI.Error(fmt.Sprintf("Error trying to %s %s: %s", c.Func, plural, err.Error())) + return 2 + } + if apiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing %s on %s: %s", c.Func, plural, pretty.Sprint(apiErr))) + return 1 + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateTargetTableOutput(catalog)) + case "json": + b, err := base.JsonFormatter{}.Format(catalog) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting as JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + } + + return 0 +} diff --git a/internal/cmd/commands/users/funcs.go b/internal/cmd/commands/users/funcs.go index 85f896242..091b1b710 100644 --- a/internal/cmd/commands/users/funcs.go +++ b/internal/cmd/commands/users/funcs.go @@ -12,6 +12,7 @@ func generateUserTableOutput(in *users.User) string { nonAttributeMap := map[string]interface{}{ "ID": in.Id, + "Scope ID": in.Scope.Id, "Version": in.Version, "Created Time": in.CreatedTime.Local().Format(time.RFC3339), "Updated Time": in.UpdatedTime.Local().Format(time.RFC3339), diff --git a/internal/cmd/commands/users/user.go b/internal/cmd/commands/users/user.go index 3efa77240..9f9995003 100644 --- a/internal/cmd/commands/users/user.go +++ b/internal/cmd/commands/users/user.go @@ -28,10 +28,11 @@ func (c *Command) Synopsis() string { } var flagsMap = map[string][]string{ - "create": {"name", "description"}, + "create": {"scope-id", "name", "description"}, "update": {"id", "name", "description", "version"}, "read": {"id"}, "delete": {"id"}, + "list": {"scope-id"}, } func (c *Command) Help() string { @@ -77,6 +78,10 @@ func (c *Command) Run(args []string) int { c.UI.Error("ID is required but not passed in via -id") return 1 } + if strutil.StrListContains(flagsMap[c.Func], "scope-id") && c.FlagScopeId == "" { + c.UI.Error("Scope ID must be passed in via -scope-id") + return 1 + } client, err := c.Client() if err != nil { @@ -112,7 +117,7 @@ func (c *Command) Run(args []string) int { default: switch c.FlagVersion { case 0: - opts = append(opts, users.WithAutomaticVersioning()) + opts = append(opts, users.WithAutomaticVersioning(true)) default: version = uint32(c.FlagVersion) } @@ -125,7 +130,7 @@ func (c *Command) Run(args []string) int { switch c.Func { case "create": - user, apiErr, err = userClient.Create(c.Context, opts...) + user, apiErr, err = userClient.Create(c.Context, c.FlagScopeId, opts...) case "update": user, apiErr, err = userClient.Update(c.Context, c.FlagId, version, opts...) case "read": @@ -133,7 +138,7 @@ func (c *Command) Run(args []string) int { case "delete": existed, apiErr, err = userClient.Delete(c.Context, c.FlagId, opts...) case "list": - listedUsers, apiErr, err = userClient.List(c.Context, opts...) + listedUsers, apiErr, err = userClient.List(c.Context, c.FlagScopeId, opts...) } plural := "user" diff --git a/internal/cmd/common/flags.go b/internal/cmd/common/flags.go index c30ce8111..e7510804f 100644 --- a/internal/cmd/common/flags.go +++ b/internal/cmd/common/flags.go @@ -4,11 +4,20 @@ import ( "fmt" "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/posener/complete" ) func PopulateCommonFlags(c *base.Command, f *base.FlagSet, resourceType string, flagNames []string) { for _, name := range flagNames { switch name { + case "scope-id": + f.StringVar(&base.StringVar{ + Name: "scope-id", + Target: &c.FlagScopeId, + Default: "global", + Completion: complete.PredictAnything, + Usage: `Scope in which to make the request. Defaults to "global".`, + }) case "id": f.StringVar(&base.StringVar{ Name: "id", diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 41a28a458..05970f236 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -289,6 +289,40 @@ ] } }, + "/v1/auth-methods/{auth_method_id}:authenticate": { + "post": { + "summary": "Authenticate a user to an scope and retrieve an authentication token.", + "operationId": "AuthenticationService_Authenticate", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.authtokens.v1.AuthToken" + } + } + }, + "parameters": [ + { + "name": "auth_method_id", + "description": "The id to the authmethod in the system being used for authentication. The auth method must be in the scope\nbeing logged in to.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.services.v1.AuthenticateRequest" + } + } + ], + "tags": [ + "controller.api.services.v1.AuthenticationService" + ] + } + }, "/v1/auth-methods/{id}": { "get": { "summary": "Gets a single AuthMethod", @@ -1881,7 +1915,7 @@ "/v1/scopes/*/auth-methods/{auth_method_id}:authenticate": { "post": { "summary": "Authenticate a user to an scope and retrieve an authentication token.", - "operationId": "AuthenticationService_Authenticate", + "operationId": "AuthenticationService_Authenticate2", "responses": { "200": { "description": "", @@ -4984,7 +5018,7 @@ "type": "integer", "format": "int64" }, - "old_password": { + "current_password": { "type": "string" }, "new_password": { diff --git a/internal/gen/controller/api/services/account_service.pb.go b/internal/gen/controller/api/services/account_service.pb.go index 6b7e34ce4..2efdb885f 100644 --- a/internal/gen/controller/api/services/account_service.pb.go +++ b/internal/gen/controller/api/services/account_service.pb.go @@ -659,10 +659,10 @@ type ChangePasswordRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - OldPassword string `protobuf:"bytes,3,opt,name=old_password,proto3" json:"old_password,omitempty"` - NewPassword string `protobuf:"bytes,4,opt,name=new_password,proto3" json:"new_password,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` + CurrentPassword string `protobuf:"bytes,3,opt,name=current_password,proto3" json:"current_password,omitempty"` + NewPassword string `protobuf:"bytes,4,opt,name=new_password,proto3" json:"new_password,omitempty"` } func (x *ChangePasswordRequest) Reset() { @@ -711,9 +711,9 @@ func (x *ChangePasswordRequest) GetVersion() uint32 { return 0 } -func (x *ChangePasswordRequest) GetOldPassword() string { +func (x *ChangePasswordRequest) GetCurrentPassword() string { if x != nil { - return x.OldPassword + return x.CurrentPassword } return "" } @@ -858,138 +858,139 @@ var file_controller_api_services_v1_account_service_proto_rawDesc = []byte{ 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, - 0x6d, 0x22, 0x89, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, + 0x6d, 0x22, 0x91, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 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, 0x22, 0x0a, 0x0c, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x6c, 0x64, - 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, 0x77, - 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x5b, 0x0a, - 0x16, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, - 0x01, 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, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0xfc, 0x0d, 0x0a, 0x0e, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xd9, 0x01, - 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2d, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6c, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x4c, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x5a, 0x31, 0x12, 0x29, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, - 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, - 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, - 0x41, 0x17, 0x12, 0x15, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, - 0x65, 0x20, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0xf0, 0x01, 0x0a, 0x0c, 0x4c, 0x69, - 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x45, 0x12, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x5a, 0x35, 0x12, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, - 0x2f, 0x7b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, - 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x92, 0x41, 0x2f, 0x12, 0x2d, 0x4c, - 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, - 0x61, 0x75, 0x74, 0x68, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x2e, 0x12, 0x99, 0x02, 0x0a, - 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0xa2, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x62, 0x22, 0x0c, 0x2f, 0x76, - 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x5a, 0x46, 0x22, 0x38, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, - 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x7b, 0x69, - 0x74, 0x65, 0x6d, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, - 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x3a, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, - 0x37, 0x12, 0x35, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, - 0x67, 0x6c, 0x65, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x61, 0x75, 0x74, 0x68, - 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x2e, 0x12, 0xec, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x76, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x58, 0x32, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x5a, 0x37, 0x32, 0x29, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, - 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x2a, 0x2f, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, - 0x41, 0x15, 0x12, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x12, 0xd4, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x40, 0x2a, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x5a, 0x2b, 0x2a, 0x29, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x15, 0x12, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x12, 0x93, - 0x02, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2e, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, - 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, 0x50, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0xa2, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x6c, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, - 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x01, 0x2a, 0x5a, 0x41, 0x22, 0x36, 0x2f, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x5b, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x41, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 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, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x32, 0xfc, 0x0d, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xd9, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x6c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4c, 0x12, 0x11, 0x2f, 0x76, 0x31, + 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x5a, 0x31, + 0x12, 0x29, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, + 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x47, 0x65, 0x74, 0x73, + 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0xf0, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x73, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x45, 0x12, 0x0c, 0x2f, + 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x5a, 0x35, 0x12, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, - 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x2d, 0x12, 0x2b, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x2e, 0x12, 0xa2, 0x02, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 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, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa8, - 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x72, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x2d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x01, 0x2a, 0x5a, 0x44, 0x22, - 0x39, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, - 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x2d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x2d, 0x12, 0x2b, 0x53, 0x65, - 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x20, - 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, - 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 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, + 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x7b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x73, 0x92, 0x41, 0x2f, 0x12, 0x2d, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, + 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x73, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x61, 0x75, 0x74, 0x68, 0x20, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x2e, 0x12, 0x99, 0x02, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa2, 0x01, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x62, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x5a, 0x46, 0x22, 0x38, 0x2f, 0x76, 0x31, 0x2f, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x7b, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x61, 0x75, 0x74, 0x68, + 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x37, 0x12, 0x35, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x64, 0x20, 0x61, 0x75, 0x74, 0x68, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x2e, + 0x12, 0xec, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x76, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x58, 0x32, + 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x5a, 0x37, 0x32, 0x29, 0x2f, 0x76, 0x31, 0x2f, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, + 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x15, 0x12, 0x13, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x12, + 0xd4, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x40, 0x2a, 0x11, + 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x5a, 0x2b, 0x2a, 0x29, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, + 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x2a, + 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, + 0x15, 0x12, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x12, 0x93, 0x02, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 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, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa2, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x6c, + 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x3a, 0x01, 0x2a, 0x5a, 0x41, 0x22, 0x36, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, + 0x2f, 0x2a, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x01, 0x2a, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x2d, 0x12, + 0x2b, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x64, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x12, 0xa2, 0x02, 0x0a, + 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, + 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 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, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa8, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x72, 0x22, + 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x3a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x3a, 0x01, 0x2a, 0x5a, 0x44, 0x22, 0x39, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, + 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x3a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x92, 0x41, 0x2d, 0x12, 0x2b, 0x53, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x2e, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, + 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/gen/controller/api/services/authenticate_service.pb.go b/internal/gen/controller/api/services/authenticate_service.pb.go index af62e00b5..bea4bbdf4 100644 --- a/internal/gen/controller/api/services/authenticate_service.pb.go +++ b/internal/gen/controller/api/services/authenticate_service.pb.go @@ -334,8 +334,8 @@ var file_controller_api_services_v1_authenticate_service_proto_rawDesc = []byte{ 0x17, 0x0a, 0x15, 0x44, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x32, 0xa0, 0x02, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x86, 0x02, 0x0a, + 0x73, 0x65, 0x32, 0xdb, 0x02, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xc1, 0x02, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2f, 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, 0x75, 0x74, 0x68, 0x65, @@ -343,21 +343,25 @@ var file_controller_api_services_v1_authenticate_service_proto_rawDesc = []byte{ 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, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x92, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42, 0x22, 0x37, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x7b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x47, 0x12, 0x45, - 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x75, - 0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x20, - 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20, - 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 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, + 0x22, 0xcd, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x7d, 0x22, 0x2e, 0x2f, 0x76, 0x31, 0x2f, 0x61, + 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x7b, 0x61, 0x75, 0x74, + 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x5a, 0x42, 0x22, 0x37, + 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x2a, 0x2f, 0x61, 0x75, 0x74, + 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x7b, 0x61, 0x75, 0x74, 0x68, 0x5f, + 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x65, + 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x47, 0x12, 0x45, 0x41, 0x75, 0x74, 0x68, 0x65, + 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x74, + 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, + 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, + 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, + 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/gen/controller/api/services/authenticate_service.pb.gw.go b/internal/gen/controller/api/services/authenticate_service.pb.gw.go index 3ff53e0af..d34881978 100644 --- a/internal/gen/controller/api/services/authenticate_service.pb.gw.go +++ b/internal/gen/controller/api/services/authenticate_service.pb.gw.go @@ -97,6 +97,74 @@ func local_request_AuthenticationService_Authenticate_0(ctx context.Context, mar } +func request_AuthenticationService_Authenticate_1(ctx context.Context, marshaler runtime.Marshaler, client AuthenticationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AuthenticateRequest + 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["auth_method_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "auth_method_id") + } + + protoReq.AuthMethodId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "auth_method_id", err) + } + + msg, err := client.Authenticate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_AuthenticationService_Authenticate_1(ctx context.Context, marshaler runtime.Marshaler, server AuthenticationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AuthenticateRequest + 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["auth_method_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "auth_method_id") + } + + protoReq.AuthMethodId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "auth_method_id", err) + } + + msg, err := server.Authenticate(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterAuthenticationServiceHandlerServer registers the http handlers for service AuthenticationService to "mux". // UnaryRPC :call AuthenticationServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -123,6 +191,26 @@ func RegisterAuthenticationServiceHandlerServer(ctx context.Context, mux *runtim }) + mux.Handle("POST", pattern_AuthenticationService_Authenticate_1, 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.AuthenticationService/Authenticate") + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_AuthenticationService_Authenticate_1(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AuthenticationService_Authenticate_1(ctx, mux, outboundMarshaler, w, req, response_AuthenticationService_Authenticate_1{resp}, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -184,6 +272,26 @@ func RegisterAuthenticationServiceHandlerClient(ctx context.Context, mux *runtim }) + mux.Handle("POST", pattern_AuthenticationService_Authenticate_1, 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.AuthenticationService/Authenticate") + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_AuthenticationService_Authenticate_1(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AuthenticationService_Authenticate_1(ctx, mux, outboundMarshaler, w, req, response_AuthenticationService_Authenticate_1{resp}, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -196,10 +304,23 @@ func (m response_AuthenticationService_Authenticate_0) XXX_ResponseBody() interf return response.Item } +type response_AuthenticationService_Authenticate_1 struct { + proto.Message +} + +func (m response_AuthenticationService_Authenticate_1) XXX_ResponseBody() interface{} { + response := m.Message.(*AuthenticateResponse) + return response.Item +} + var ( - pattern_AuthenticationService_Authenticate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "auth-methods", "auth_method_id"}, "authenticate")) + pattern_AuthenticationService_Authenticate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "auth-methods", "auth_method_id"}, "authenticate")) + + pattern_AuthenticationService_Authenticate_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "scopes", "auth-methods", "auth_method_id"}, "authenticate")) ) var ( forward_AuthenticationService_Authenticate_0 = runtime.ForwardResponseMessage + + forward_AuthenticationService_Authenticate_1 = runtime.ForwardResponseMessage ) diff --git a/internal/host/static/repository_host_set_member_test.go b/internal/host/static/repository_host_set_member_test.go index 1877abbea..2b9050c2c 100644 --- a/internal/host/static/repository_host_set_member_test.go +++ b/internal/host/static/repository_host_set_member_test.go @@ -178,16 +178,16 @@ func TestRepository_AddSetMembers_Combinations(t *testing.T) { // second call - add new set of hosts - should succeed set.Version = set.Version + 1 - hosts2 := TestHosts(t, conn, c.PublicId, 5) + Hosts := TestHosts(t, conn, c.PublicId, 5) var hostIds2 []string - for _, h := range hosts2 { + for _, h := range Hosts { hostIds2 = append(hostIds2, h.PublicId) } got2, err2 := repo.AddSetMembers(context.Background(), prj.PublicId, set.PublicId, set.Version, hostIds2) require.NoError(err2) require.NotNil(got2) - hosts = append(hosts, hosts2...) + hosts = append(hosts, Hosts...) assert.Len(got2, len(hosts)) assert.Empty(cmp.Diff(hosts, got2, opts...)) assert.NoError(db.TestVerifyOplog(t, rw, set.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))) @@ -391,9 +391,9 @@ func TestRepository_DeleteSetMembers_Combinations(t *testing.T) { assert.NoError(db.TestVerifyOplog(t, rw, set.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))) // verify no members remain - members2, err := getHosts(context.Background(), repo.reader, set.PublicId, unlimited) + Members, err := getHosts(context.Background(), repo.reader, set.PublicId, unlimited) require.NoError(err) - require.Empty(members2) + require.Empty(Members) } func TestRepository_SetSetMembers_Parameters(t *testing.T) { diff --git a/internal/host/static/repository_host_set_test.go b/internal/host/static/repository_host_set_test.go index ec4cc85f7..6a2e31b04 100644 --- a/internal/host/static/repository_host_set_test.go +++ b/internal/host/static/repository_host_set_test.go @@ -539,14 +539,14 @@ func TestRepository_UpdateSet(t *testing.T) { assert.Empty(gotHosts1) sB.Name = name - got2, gotHosts2, gotCount2, err := repo.UpdateSet(context.Background(), prj.GetPublicId(), sB, 1, []string{"name"}) + got2, gotHosts, gotCount2, err := repo.UpdateSet(context.Background(), prj.GetPublicId(), sB, 1, []string{"name"}) assert.Truef(errors.Is(err, db.ErrNotUnique), "want err: %v got: %v", db.ErrNotUnique, err) assert.Nil(got2) assert.Equal(db.NoRowsAffected, gotCount2, "row count") err = db.TestVerifyOplog(t, rw, sB.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) assert.Error(err) assert.True(errors.Is(db.ErrRecordNotFound, err)) - assert.Empty(gotHosts2) + assert.Empty(gotHosts) }) t.Run("valid-duplicate-names-diff-Catalogs", func(t *testing.T) { diff --git a/internal/iam/testing.go b/internal/iam/testing.go index c7632ede7..d01deccc6 100644 --- a/internal/iam/testing.go +++ b/internal/iam/testing.go @@ -46,16 +46,16 @@ func TestScopes(t *testing.T, repo *Repository, opt ...Option) (org *Scope, prj opts := getOpts(opt...) - org, err := NewOrg() + org, err := NewOrg(opt...) require.NoError(err) - org, err = repo.CreateScope(context.Background(), org, opts.withUserId) + org, err = repo.CreateScope(context.Background(), org, opts.withUserId, opt...) require.NoError(err) require.NotNil(org) require.NotEmpty(org.GetPublicId()) - prj, err = NewProject(org.GetPublicId()) + prj, err = NewProject(org.GetPublicId(), opt...) require.NoError(err) - prj, err = repo.CreateScope(context.Background(), prj, opts.withUserId) + prj, err = repo.CreateScope(context.Background(), prj, opts.withUserId, opt...) require.NoError(err) require.NotNil(prj) require.NotEmpty(prj.GetPublicId()) diff --git a/internal/proto/local/controller/api/services/v1/account_service.proto b/internal/proto/local/controller/api/services/v1/account_service.proto index 4e89b0272..c6fcfe083 100644 --- a/internal/proto/local/controller/api/services/v1/account_service.proto +++ b/internal/proto/local/controller/api/services/v1/account_service.proto @@ -205,7 +205,7 @@ message SetPasswordResponse { message ChangePasswordRequest { string id = 1; uint32 version = 2; - string old_password = 3 [json_name="old_password"]; + string current_password = 3 [json_name="current_password"]; string new_password = 4 [json_name="new_password"]; } diff --git a/internal/proto/local/controller/api/services/v1/authenticate_service.proto b/internal/proto/local/controller/api/services/v1/authenticate_service.proto index 46d54687e..0cc250cc6 100644 --- a/internal/proto/local/controller/api/services/v1/authenticate_service.proto +++ b/internal/proto/local/controller/api/services/v1/authenticate_service.proto @@ -13,9 +13,14 @@ service AuthenticationService { // Authenticate validates credentials provided and returns an auth token. rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse) { option (google.api.http) = { - post: "/v1/scopes/*/auth-methods/{auth_method_id}:authenticate" + post: "/v1/auth-methods/{auth_method_id}:authenticate" body: "*" response_body: "item" + additional_bindings: { + post: "/v1/scopes/*/auth-methods/{auth_method_id}:authenticate" + body: "*" + response_body: "item" + } }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { summary: "Authenticate a user to an scope and retrieve an authentication token." diff --git a/internal/servers/controller/handlers/accounts/account_service.go b/internal/servers/controller/handlers/accounts/account_service.go index a84af6ec3..fe5aac50f 100644 --- a/internal/servers/controller/handlers/accounts/account_service.go +++ b/internal/servers/controller/handlers/accounts/account_service.go @@ -141,7 +141,7 @@ func (s Service) ChangePassword(ctx context.Context, req *pbs.ChangePasswordRequ if authResults.Error != nil { return nil, authResults.Error } - u, err := s.changePasswordInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetVersion(), req.GetOldPassword(), req.GetNewPassword()) + u, err := s.changePasswordInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetVersion(), req.GetCurrentPassword(), req.GetNewPassword()) if err != nil { return nil, err } @@ -295,12 +295,12 @@ func (s Service) listFromRepo(ctx context.Context, authMethodId string) ([]*pb.A return outUl, nil } -func (s Service) changePasswordInRepo(ctx context.Context, scopeId, id string, version uint32, oldPassword, newPassword string) (*pb.Account, error) { +func (s Service) changePasswordInRepo(ctx context.Context, scopeId, id string, version uint32, currentPassword, newPassword string) (*pb.Account, error) { repo, err := s.repoFn() if err != nil { return nil, err } - out, err := repo.ChangePassword(ctx, scopeId, id, oldPassword, newPassword, version) + out, err := repo.ChangePassword(ctx, scopeId, id, currentPassword, newPassword, version) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to change password: %v.", err) } @@ -448,8 +448,8 @@ func validateChangePasswordRequest(req *pbs.ChangePasswordRequest) error { if req.GetNewPassword() == "" { badFields["new_password"] = "This is a required field." } - if req.GetOldPassword() == "" { - badFields["old_password"] = "This is a required field." + if req.GetCurrentPassword() == "" { + badFields["current_password"] = "This is a required field." } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields) diff --git a/internal/servers/controller/handlers/accounts/account_service_test.go b/internal/servers/controller/handlers/accounts/account_service_test.go index a832866d3..6c160b3a2 100644 --- a/internal/servers/controller/handlers/accounts/account_service_test.go +++ b/internal/servers/controller/handlers/accounts/account_service_test.go @@ -950,10 +950,10 @@ func TestChangePassword(t *testing.T) { acct := createAccount(t, "originalpassword") changeResp, err := tested.ChangePassword(auth.DisabledAuthTestContext(auth.WithScopeId(o.GetPublicId())), &pbs.ChangePasswordRequest{ - Id: acct.GetId(), - Version: acct.GetVersion(), - OldPassword: "originalpassword", - NewPassword: "a different password", + Id: acct.GetId(), + Version: acct.GetVersion(), + CurrentPassword: "originalpassword", + NewPassword: "a different password", }) require.NoError(err) assert.Equal(acct.GetVersion()+1, changeResp.GetItem().GetVersion()) @@ -969,10 +969,10 @@ func TestChangePassword(t *testing.T) { acct := createAccount(t, "originalpassword") changeResp, err := tested.ChangePassword(auth.DisabledAuthTestContext(auth.WithScopeId(o.GetPublicId())), &pbs.ChangePasswordRequest{ - Id: acct.GetId(), - Version: acct.GetVersion(), - OldPassword: "thewrongpassword", - NewPassword: "a different password", + Id: acct.GetId(), + Version: acct.GetVersion(), + CurrentPassword: "thewrongpassword", + NewPassword: "a different password", }) assert.Error(err) assert.Nil(changeResp) @@ -1042,10 +1042,10 @@ func TestChangePassword(t *testing.T) { assert := assert.New(t) changeResp, err := tested.ChangePassword(auth.DisabledAuthTestContext(auth.WithScopeId(o.GetPublicId())), &pbs.ChangePasswordRequest{ - Id: tt.accountId, - Version: tt.version, - OldPassword: tt.oldPW, - NewPassword: tt.newPW, + Id: tt.accountId, + Version: tt.version, + CurrentPassword: tt.oldPW, + NewPassword: tt.newPW, }) assert.Error(err) assert.Nil(changeResp) diff --git a/internal/servers/controller/handlers/authenticate/authenticate_service.go b/internal/servers/controller/handlers/authenticate/authenticate_service.go index 8d39faedb..404eb3d88 100644 --- a/internal/servers/controller/handlers/authenticate/authenticate_service.go +++ b/internal/servers/controller/handlers/authenticate/authenticate_service.go @@ -199,7 +199,7 @@ func validateAuthenticateRequest(req *pbs.AuthenticateRequest) error { } tType := strings.ToLower(strings.TrimSpace(req.GetTokenType())) if tType != "" && tType != "token" && tType != "cookie" { - badFields["token_type"] = "The only accepted type is 'token'." + badFields["token_type"] = `The only accepted types are "token" and "cookie".` } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Invalid fields provided in request.", badFields) diff --git a/internal/servers/controller/multi_test.go b/internal/servers/controller/multi_test.go index dfc574e08..a1fba3e95 100644 --- a/internal/servers/controller/multi_test.go +++ b/internal/servers/controller/multi_test.go @@ -36,14 +36,14 @@ func TestAuthenticationMulti(t *testing.T) { defer c2.Shutdown() auth := authmethods.NewClient(c1.Client()) - token1, apiErr, err := auth.Authenticate(c1.Context(), amId, user, password) + token1, apiErr, err := auth.Authenticate(c1.Context(), amId, map[string]interface{}{"login_name": user, "password": password}) require.Nil(err) require.Nil(apiErr) require.NotNil(token1) time.Sleep(5 * time.Second) auth = authmethods.NewClient(c2.Client()) - token2, apiErr, err := auth.Authenticate(c2.Context(), amId, user, password) + token2, apiErr, err := auth.Authenticate(c2.Context(), amId, map[string]interface{}{"login_name": user, "password": password}) require.Nil(err) require.Nil(apiErr) require.NotNil(token2) @@ -51,9 +51,7 @@ func TestAuthenticationMulti(t *testing.T) { assert.NotEqual(token1.Token, token2.Token) c1.Client().SetToken(token1.Token) - c1.Client().SetScopeId(scope.Global.String()) c2.Client().SetToken(token1.Token) // Same token, as it should work on both - c2.Client().SetScopeId(scope.Global.String()) // Create a project, read from the other org, apiErr, err := scopes.NewClient(c1.Client()).Create(c1.Context(), scope.Global.String()) diff --git a/internal/servers/controller/testing.go b/internal/servers/controller/testing.go index 14e2e3432..07320baf0 100644 --- a/internal/servers/controller/testing.go +++ b/internal/servers/controller/testing.go @@ -103,8 +103,10 @@ func (tc *TestController) Token() *authtokens.AuthToken { token, apiErr, err := authmethods.NewClient(tc.Client()).Authenticate( tc.Context(), tc.b.DevAuthMethodId, - tc.b.DevLoginName, - tc.b.DevPassword, + map[string]interface{}{ + "login_name": tc.b.DevLoginName, + "password": tc.b.DevPassword, + }, ) if err != nil { tc.t.Error(fmt.Errorf("error logging in: %w", err)) diff --git a/internal/servers/repository_test.go b/internal/servers/repository_test.go index 3f3c8b947..6f0f2af44 100644 --- a/internal/servers/repository_test.go +++ b/internal/servers/repository_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/servers/controller" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/recovery" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -40,7 +41,7 @@ func TestRecoveryNonces(t *testing.T) { // Token 1, try 1 client.SetToken(token1) roleClient := roles.NewClient(client) - _, apiErr, err := roleClient.Create(tc.Context()) + _, apiErr, err := roleClient.Create(tc.Context(), scope.Global.String()) require.NoError(err) assert.Nil(apiErr) nonces, err := repo.ListNonces(tc.Context()) @@ -48,7 +49,7 @@ func TestRecoveryNonces(t *testing.T) { assert.Len(nonces, 1) // Token 1, try 2 - _, apiErr, err = roleClient.Create(tc.Context()) + _, apiErr, err = roleClient.Create(tc.Context(), scope.Global.String()) require.NoError(err) assert.NotNil(apiErr) nonces, err = repo.ListNonces(tc.Context()) @@ -56,8 +57,8 @@ func TestRecoveryNonces(t *testing.T) { assert.Len(nonces, 1) // Token 2 - client.SetToken(token2) - _, apiErr, err = roleClient.Create(tc.Context()) + roleClient.ApiClient().SetToken(token2) + _, apiErr, err = roleClient.Create(tc.Context(), scope.Global.String()) require.NoError(err) assert.Nil(apiErr) nonces, err = repo.ListNonces(tc.Context()) @@ -72,8 +73,8 @@ func TestRecoveryNonces(t *testing.T) { // And finally, make sure they still can't be used for _, token := range []string{token1, token2} { - client.SetToken(token) - _, apiErr, err = roleClient.Create(tc.Context()) + roleClient.ApiClient().SetToken(token) + _, apiErr, err = roleClient.Create(tc.Context(), scope.Global.String()) require.NoError(err) assert.NotNil(apiErr) nonces, err = repo.ListNonces(tc.Context())