// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package approle import ( "context" "strings" "testing" "github.com/stretchr/testify/require" "github.com/hashicorp/vault/sdk/logical" ) func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) { t.Helper() config := logical.TestBackendConfig() config.StorageView = &logical.InmemStorage{} b, err := Backend(config) if err != nil { t.Fatal(err) } if b == nil { t.Fatalf("failed to create backend") } err = b.Backend.Setup(context.Background(), config) if err != nil { t.Fatal(err) } return b, config.StorageView } func TestAppRole_RoleServiceToBatchNumUses(t *testing.T) { b, s := createBackendWithStorage(t) requestFunc := func(operation logical.Operation, data map[string]interface{}) { resp, err := b.HandleRequest(context.Background(), &logical.Request{ Path: "role/testrole", Operation: operation, Storage: s, Data: data, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: err: %#v\nresp: %#v", err, resp) } } data := map[string]interface{}{ "bind_secret_id": true, "secret_id_num_uses": 0, "secret_id_ttl": "10m", "token_policies": "policy", "token_ttl": "5m", "token_max_ttl": "10m", "token_num_uses": 2, "token_type": "default", } requestFunc(logical.CreateOperation, data) data["token_num_uses"] = 0 data["token_type"] = "batch" requestFunc(logical.UpdateOperation, data) resp, err := b.HandleRequest(context.Background(), &logical.Request{ Path: "role/testrole/role-id", Operation: logical.ReadOperation, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } roleID := resp.Data["role_id"] resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/testrole/secret-id", Operation: logical.UpdateOperation, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } secretID := resp.Data["secret_id"] resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } require.NotNil(t, resp.Auth) } func TestAppRole_RoleNameCaseSensitivity(t *testing.T) { testFunc := func(t *testing.T, roleName string) { var resp *logical.Response var err error b, s := createBackendWithStorage(t) // Create the role resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + roleName, Operation: logical.CreateOperation, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr:%v", resp, err) } // Get the role-id resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + roleName + "/role-id", Operation: logical.ReadOperation, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } roleID := resp.Data["role_id"] // Create a secret-id resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + roleName + "/secret-id", Operation: logical.UpdateOperation, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } secretID := resp.Data["secret_id"] secretIDAccessor := resp.Data["secret_id_accessor"] // Ensure login works resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } if resp.Auth == nil { t.Fatalf("failed to perform login") } // Destroy secret ID accessor resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + roleName + "/secret-id-accessor/destroy", Operation: logical.UpdateOperation, Storage: s, Data: map[string]interface{}{ "secret_id_accessor": secretIDAccessor, }, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } // Login again using the accessor's corresponding secret ID should fail resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil && err != logical.ErrInvalidCredentials { t.Fatal(err) } if resp == nil || !resp.IsError() { t.Fatalf("expected error due to invalid secret ID") } // Generate another secret ID resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + roleName + "/secret-id", Operation: logical.UpdateOperation, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } secretID = resp.Data["secret_id"] secretIDAccessor = resp.Data["secret_id_accessor"] // Ensure login works resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } if resp.Auth == nil { t.Fatalf("failed to perform login") } // Destroy the secret ID resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + roleName + "/secret-id/destroy", Operation: logical.UpdateOperation, Storage: s, Data: map[string]interface{}{ "secret_id": secretID, }, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } // Login again using the same secret ID should fail resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil && err != logical.ErrInvalidCredentials { t.Fatal(err) } if resp == nil || !resp.IsError() { t.Fatalf("expected error due to invalid secret ID") } // Generate another secret ID resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + roleName + "/secret-id", Operation: logical.UpdateOperation, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } secretID = resp.Data["secret_id"] secretIDAccessor = resp.Data["secret_id_accessor"] // Ensure login works resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } if resp.Auth == nil { t.Fatalf("failed to perform login") } // Destroy the secret ID using lower cased role name resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + strings.ToLower(roleName) + "/secret-id/destroy", Operation: logical.UpdateOperation, Storage: s, Data: map[string]interface{}{ "secret_id": secretID, }, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } // Login again using the same secret ID should fail resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil && err != logical.ErrInvalidCredentials { t.Fatal(err) } if resp == nil || !resp.IsError() { t.Fatalf("expected error due to invalid secret ID") } // Generate another secret ID resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + roleName + "/secret-id", Operation: logical.UpdateOperation, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } secretID = resp.Data["secret_id"] secretIDAccessor = resp.Data["secret_id_accessor"] // Ensure login works resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } if resp.Auth == nil { t.Fatalf("failed to perform login") } // Destroy the secret ID using upper cased role name resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + strings.ToUpper(roleName) + "/secret-id/destroy", Operation: logical.UpdateOperation, Storage: s, Data: map[string]interface{}{ "secret_id": secretID, }, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } // Login again using the same secret ID should fail resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil && err != logical.ErrInvalidCredentials { t.Fatal(err) } if resp == nil || !resp.IsError() { t.Fatalf("expected error due to invalid secret ID") } // Generate another secret ID resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/" + roleName + "/secret-id", Operation: logical.UpdateOperation, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } secretID = resp.Data["secret_id"] secretIDAccessor = resp.Data["secret_id_accessor"] // Ensure login works resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } if resp.Auth == nil { t.Fatalf("failed to perform login") } // Destroy the secret ID using mixed case name resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "role/saMpleRolEnaMe/secret-id/destroy", Operation: logical.UpdateOperation, Storage: s, Data: map[string]interface{}{ "secret_id": secretID, }, }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } // Login again using the same secret ID should fail resp, err = b.HandleRequest(context.Background(), &logical.Request{ Path: "login", Operation: logical.UpdateOperation, Data: map[string]interface{}{ "role_id": roleID, "secret_id": secretID, }, Storage: s, }) if err != nil && err != logical.ErrInvalidCredentials { t.Fatal(err) } if resp == nil || !resp.IsError() { t.Fatalf("expected error due to invalid secret ID") } } // Lower case role name testFunc(t, "samplerolename") // Upper case role name testFunc(t, "SAMPLEROLENAME") // Mixed case role name testFunc(t, "SampleRoleName") }