diff --git a/builtin/logical/aws/path_config_root.go b/builtin/logical/aws/path_config_root.go index 5174f3ceb8..b2d32ba3ef 100644 --- a/builtin/logical/aws/path_config_root.go +++ b/builtin/logical/aws/path_config_root.go @@ -81,6 +81,8 @@ func pathConfigRoot(b *backend) *framework.Path { }, }, + ExistenceCheck: b.pathConfigRootExistenceCheck, + Operations: map[logical.Operation]framework.OperationHandler{ logical.ReadOperation: &framework.PathOperation{ Callback: b.pathConfigRootRead, @@ -95,6 +97,13 @@ func pathConfigRoot(b *backend) *framework.Path { OperationSuffix: "root-iam-credentials", }, }, + logical.CreateOperation: &framework.PathOperation{ + Callback: b.pathConfigRootWrite, + DisplayAttrs: &framework.DisplayAttributes{ + OperationVerb: "configure", + OperationSuffix: "root-iam-credentials", + }, + }, }, HelpSynopsis: pathConfigRootHelpSyn, @@ -106,6 +115,16 @@ func pathConfigRoot(b *backend) *framework.Path { return p } +// Establishes dichotomy of request operation between CreateOperation and UpdateOperation. +// Returning 'true' forces an UpdateOperation, CreateOperation otherwise. +func (b *backend) pathConfigRootExistenceCheck(ctx context.Context, req *logical.Request, data *framework.FieldData) (bool, error) { + entry, err := getConfigFromStorage(ctx, req) + if err != nil { + return false, err + } + return entry != nil, nil +} + func (b *backend) pathConfigRootRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { b.clientMutex.RLock() defer b.clientMutex.RUnlock() diff --git a/builtin/logical/aws/path_config_root_test.go b/builtin/logical/aws/path_config_root_test.go index e9069a9fcb..c7b016a76c 100644 --- a/builtin/logical/aws/path_config_root_test.go +++ b/builtin/logical/aws/path_config_root_test.go @@ -29,7 +29,58 @@ func TestBackend_PathConfigRoot(t *testing.T) { t.Fatal(err) } + // Create operation configData := map[string]interface{}{ + "access_key": "AKIAEXAMPLE", + "secret_key": "RandomData", + "region": "us-west-2", + "iam_endpoint": "https://iam.amazonaws.com", + "sts_endpoint": "https://sts.us-west-2.amazonaws.com", + "sts_region": "", + "sts_fallback_endpoints": []string{}, + "sts_fallback_regions": []string{}, + "role_arn": "", + "identity_token_audience": "", + "identity_token_ttl": int64(0), + "rotation_schedule": "", + "rotation_period": time.Duration(0).Seconds(), + "rotation_window": time.Duration(0).Seconds(), + "disable_automated_rotation": false, + } + + configReq := &logical.Request{ + Operation: logical.CreateOperation, + Storage: config.StorageView, + Path: "config/root", + Data: configData, + } + + resp, err := b.HandleRequest(context.Background(), configReq) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: config writing failed: resp:%#v\n err: %v", resp, err) + } + + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.ReadOperation, + Storage: config.StorageView, + Path: "config/root", + }) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("bad: config reading failed: resp:%#v\n err: %v", resp, err) + } + + // Ensure default values are enforced + configData["max_retries"] = -1 + configData["username_template"] = defaultUserNameTemplate + + delete(configData, "secret_key") + require.Equal(t, configData, resp.Data) + if !reflect.DeepEqual(resp.Data, configData) { + t.Errorf("bad: expected to read config root as %#v, got %#v instead", configData, resp.Data) + } + + // Update operation + configData = map[string]interface{}{ "access_key": "AKIAEXAMPLE", "secret_key": "RandomData", "region": "us-west-2", @@ -49,14 +100,14 @@ func TestBackend_PathConfigRoot(t *testing.T) { "disable_automated_rotation": false, } - configReq := &logical.Request{ + configReq = &logical.Request{ Operation: logical.UpdateOperation, Storage: config.StorageView, Path: "config/root", Data: configData, } - resp, err := b.HandleRequest(context.Background(), configReq) + resp, err = b.HandleRequest(context.Background(), configReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: config writing failed: resp:%#v\n err: %v", resp, err) }