| // 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") |
| } |