Skip to content

Commit 296a798

Browse files
authored
Implement static credentials (#93)
Implement static credentials Description Implement static users Resolves: #74 Tests Performed Acceptance tests Running acceptance tests... === RUN TestPlugin === RUN TestPlugin/TestCloudLifecycle === RUN TestPlugin/TestCloudLifecycle/WriteCloud === RUN TestPlugin/TestCloudLifecycle/ReadCloud === RUN TestPlugin/TestCloudLifecycle/ListClouds === RUN TestPlugin/TestCloudLifecycle/ListClouds/method-LIST === PAUSE TestPlugin/TestCloudLifecycle/ListClouds/method-LIST === RUN TestPlugin/TestCloudLifecycle/ListClouds/method-GET === PAUSE TestPlugin/TestCloudLifecycle/ListClouds/method-GET === CONT TestPlugin/TestCloudLifecycle/ListClouds/method-LIST === CONT TestPlugin/TestCloudLifecycle/ListClouds/method-GET === RUN TestPlugin/TestCloudLifecycle/DeleteCloud === RUN TestPlugin/TestCredsLifecycle === RUN TestPlugin/TestCredsLifecycle/root_token === RUN TestPlugin/TestCredsLifecycle/user_token === RUN TestPlugin/TestCredsLifecycle/user_password === RUN TestPlugin/TestCredsLifecycle/static_user_token === RUN TestPlugin/TestCredsLifecycle/static_user_password === RUN TestPlugin/TestInfo === RUN TestPlugin/TestRoleLifecycle roles_test.go:53: Cloud with name `gev5efsque` was created === RUN TestPlugin/TestRoleLifecycle/WriteRole === RUN TestPlugin/TestRoleLifecycle/ReadRole === RUN TestPlugin/TestRoleLifecycle/ListRoles === RUN TestPlugin/TestRoleLifecycle/ListRoles/method-LIST === PAUSE TestPlugin/TestRoleLifecycle/ListRoles/method-LIST === RUN TestPlugin/TestRoleLifecycle/ListRoles/method-GET === PAUSE TestPlugin/TestRoleLifecycle/ListRoles/method-GET === CONT TestPlugin/TestRoleLifecycle/ListRoles/method-LIST === CONT TestPlugin/TestRoleLifecycle/ListRoles/method-GET === RUN TestPlugin/TestRoleLifecycle/DeleteRole === CONT TestPlugin/TestRoleLifecycle plugin_test.go:337: Cloud with name `gev5efsque` has been removed === RUN TestPlugin/TestRootRotate rotate_test.go:65: Cloud with name `default1` was created rotate_test.go:68: Cloud with name `t5n5` was created plugin_test.go:337: Cloud with name `t5n5` has been removed plugin_test.go:337: Cloud with name `default1` has been removed --- PASS: TestPlugin (15.86s) --- PASS: TestPlugin/TestCloudLifecycle (0.41s) --- PASS: TestPlugin/TestCloudLifecycle/WriteCloud (0.40s) --- PASS: TestPlugin/TestCloudLifecycle/ReadCloud (0.00s) --- PASS: TestPlugin/TestCloudLifecycle/ListClouds (0.00s) --- PASS: TestPlugin/TestCloudLifecycle/ListClouds/method-LIST (0.00s) --- PASS: TestPlugin/TestCloudLifecycle/ListClouds/method-GET (0.00s) --- PASS: TestPlugin/TestCloudLifecycle/DeleteCloud (0.00s) --- PASS: TestPlugin/TestCredsLifecycle (9.36s) --- PASS: TestPlugin/TestCredsLifecycle/root_token (1.83s) --- PASS: TestPlugin/TestCredsLifecycle/user_token (2.23s) --- PASS: TestPlugin/TestCredsLifecycle/user_password (1.27s) --- PASS: TestPlugin/TestCredsLifecycle/static_user_token (2.11s) --- PASS: TestPlugin/TestCredsLifecycle/static_user_password (1.06s) --- PASS: TestPlugin/TestInfo (0.00s) --- PASS: TestPlugin/TestRoleLifecycle (0.02s) --- PASS: TestPlugin/TestRoleLifecycle/WriteRole (0.00s) --- PASS: TestPlugin/TestRoleLifecycle/ReadRole (0.00s) --- PASS: TestPlugin/TestRoleLifecycle/ListRoles (0.00s) --- PASS: TestPlugin/TestRoleLifecycle/ListRoles/method-GET (0.00s) --- PASS: TestPlugin/TestRoleLifecycle/ListRoles/method-LIST (0.00s) --- PASS: TestPlugin/TestRoleLifecycle/DeleteRole (0.00s) --- PASS: TestPlugin/TestRootRotate (4.87s) PASS ok github.com/opentelekomcloud/vault-plugin-secrets-openstack/acceptance 15.863s Unit tests === RUN TestBackend_sharedCloud --- PASS: TestBackend_sharedCloud (0.00s) === RUN TestBackend_sharedCloud/existing --- PASS: TestBackend_sharedCloud/existing (0.00s) === RUN TestBackend_sharedCloud/non-existing --- PASS: TestBackend_sharedCloud/non-existing (0.00s) === RUN TestSharedCloud_client --- PASS: TestSharedCloud_client (0.00s) === RUN TestSharedCloud_client/existing-client --- PASS: TestSharedCloud_client/existing-client (0.00s) === RUN TestSharedCloud_client/new-client --- PASS: TestSharedCloud_client/new-client (0.00s) === RUN TestCloudCreate --- PASS: TestCloudCreate (0.00s) === RUN TestCloudCreate/EmptyConfig --- PASS: TestCloudCreate/EmptyConfig (0.00s) === RUN TestCloudCreate/Create --- PASS: TestCloudCreate/Create (0.00s) === RUN TestCloudCreate/Update --- PASS: TestCloudCreate/Update (0.00s) === RUN TestCloudCreate/Read --- PASS: TestCloudCreate/Read (0.00s) === RUN TestCloudCreate/Delete --- PASS: TestCloudCreate/Delete (0.00s) === RUN TestCloudCreate/List --- PASS: TestCloudCreate/List (0.00s) === RUN TestCredentialsRead_ok --- PASS: TestCredentialsRead_ok (0.01s) === RUN TestCredentialsRead_ok/root_token --- PASS: TestCredentialsRead_ok/root_token (0.00s) === RUN TestCredentialsRead_ok/user_token --- PASS: TestCredentialsRead_ok/user_token (0.00s) === RUN TestCredentialsRead_ok/user_password --- PASS: TestCredentialsRead_ok/user_password (0.00s) === RUN TestCredentialsRead_ok/token_revoke --- PASS: TestCredentialsRead_ok/token_revoke (0.00s) === RUN TestCredentialsRead_ok/user_password_revoke --- PASS: TestCredentialsRead_ok/user_password_revoke (0.00s) === RUN TestCredentialsRead_error --- PASS: TestCredentialsRead_error (0.00s) === RUN TestCredentialsRead_error/read-fail --- PASS: TestCredentialsRead_error/read-fail (0.00s) === RUN TestCredentialsRead_error/no-user-post --- PASS: TestCredentialsRead_error/no-user-post (0.00s) === RUN TestCredentialsRead_error/no-users-token-post --- PASS: TestCredentialsRead_error/no-users-token-post (0.00s) === RUN TestCredentialsRevoke_error --- PASS: TestCredentialsRevoke_error (0.00s) === RUN TestCredentialsRevoke_error/no-token-delete --- PASS: TestCredentialsRevoke_error/no-token-delete (0.00s) === RUN TestCredentialsRevoke_error/no-user-delete --- PASS: TestCredentialsRevoke_error/no-user-delete (0.00s) === RUN TestInfoRead === PAUSE TestInfoRead === CONT TestInfoRead --- PASS: TestInfoRead (0.00s) === RUN TestRoleStoragePath --- PASS: TestRoleStoragePath (0.00s) === RUN TestRoleGet === PAUSE TestRoleGet === CONT TestRoleGet --- PASS: TestRoleGet (0.00s) === RUN TestRoleGet/existing === PAUSE TestRoleGet/existing === CONT TestRoleGet/existing --- PASS: TestRoleGet/existing (0.00s) === RUN TestRoleGet/not-existing === PAUSE TestRoleGet/not-existing === CONT TestRoleGet/not-existing --- PASS: TestRoleGet/not-existing (0.00s) === RUN TestRoleGet/get-err === PAUSE TestRoleGet/get-err === CONT TestRoleGet/get-err --- PASS: TestRoleGet/get-err (0.00s) === RUN TestRoleExistence === PAUSE TestRoleExistence === CONT TestRoleExistence --- PASS: TestRoleExistence (0.00s) === RUN TestRoleExistence/existing === PAUSE TestRoleExistence/existing === CONT TestRoleExistence/existing --- PASS: TestRoleExistence/existing (0.00s) === RUN TestRoleExistence/not-existing === PAUSE TestRoleExistence/not-existing === CONT TestRoleExistence/not-existing --- PASS: TestRoleExistence/not-existing (0.00s) === RUN TestRoleExistence/get-err === PAUSE TestRoleExistence/get-err === CONT TestRoleExistence/get-err --- PASS: TestRoleExistence/get-err (0.00s) === RUN TestRoleList === PAUSE TestRoleList === CONT TestRoleList --- PASS: TestRoleList (0.00s) === RUN TestRoleList/ok --- PASS: TestRoleList/ok (0.00s) === RUN TestRoleList/error === PAUSE TestRoleList/error === CONT TestRoleList/error --- PASS: TestRoleList/error (0.00s) === RUN TestRoleList/filter === PAUSE TestRoleList/filter === CONT TestRoleList/filter --- PASS: TestRoleList/filter (0.00s) === RUN TestRoleList/filter-get-err === PAUSE TestRoleList/filter-get-err === CONT TestRoleList/filter-get-err --- PASS: TestRoleList/filter-get-err (0.00s) === RUN TestRoleDelete === PAUSE TestRoleDelete === CONT TestRoleDelete --- PASS: TestRoleDelete (0.00s) === RUN TestRoleDelete/existing === PAUSE TestRoleDelete/existing === CONT TestRoleDelete/existing --- PASS: TestRoleDelete/existing (0.00s) === RUN TestRoleDelete/not-existing === PAUSE TestRoleDelete/not-existing === CONT TestRoleDelete/not-existing --- PASS: TestRoleDelete/not-existing (0.00s) === RUN TestRoleDelete/error === PAUSE TestRoleDelete/error === CONT TestRoleDelete/error --- PASS: TestRoleDelete/error (0.00s) === RUN TestRoleDelete/error-get === PAUSE TestRoleDelete/error-get === CONT TestRoleDelete/error-get --- PASS: TestRoleDelete/error-get (0.00s) === RUN TestRoleCreate === PAUSE TestRoleCreate === CONT TestRoleCreate --- PASS: TestRoleCreate (0.01s) === RUN TestRoleCreate/ok --- PASS: TestRoleCreate/ok (0.00s) === RUN TestRoleCreate/ok/admin === PAUSE TestRoleCreate/ok/admin === CONT TestRoleCreate/ok/admin --- PASS: TestRoleCreate/ok/admin (0.00s) === RUN TestRoleCreate/ok/token === PAUSE TestRoleCreate/ok/token === CONT TestRoleCreate/ok/token --- PASS: TestRoleCreate/ok/token (0.00s) === RUN TestRoleCreate/ok/password === PAUSE TestRoleCreate/ok/password === CONT TestRoleCreate/ok/password --- PASS: TestRoleCreate/ok/password (0.00s) === RUN TestRoleCreate/ok/ttl === PAUSE TestRoleCreate/ok/ttl === CONT TestRoleCreate/ok/ttl --- PASS: TestRoleCreate/ok/ttl (0.00s) === RUN TestRoleCreate/ok/username === PAUSE TestRoleCreate/ok/username === CONT TestRoleCreate/ok/username --- PASS: TestRoleCreate/ok/username (0.00s) === RUN TestRoleCreate/ok/endpoint-override === PAUSE TestRoleCreate/ok/endpoint-override === CONT TestRoleCreate/ok/endpoint-override --- PASS: TestRoleCreate/ok/endpoint-override (0.00s) === RUN TestRoleCreate/error --- PASS: TestRoleCreate/error (0.00s) === RUN TestRoleCreate/error/root-password === PAUSE TestRoleCreate/error/root-password === CONT TestRoleCreate/error/root-password --- PASS: TestRoleCreate/error/root-password (0.00s) === RUN TestRoleCreate/error/root-user-groups === PAUSE TestRoleCreate/error/root-user-groups === CONT TestRoleCreate/error/root-user-groups --- PASS: TestRoleCreate/error/root-user-groups (0.00s) === RUN TestRoleCreate/error/root-user-roles === PAUSE TestRoleCreate/error/root-user-roles === CONT TestRoleCreate/error/root-user-roles --- PASS: TestRoleCreate/error/root-user-roles (0.00s) === RUN TestRoleCreate/error/without-cloud === PAUSE TestRoleCreate/error/without-cloud === CONT TestRoleCreate/error/without-cloud --- PASS: TestRoleCreate/error/without-cloud (0.00s) === RUN TestRoleCreate/error/root-ttl === PAUSE TestRoleCreate/error/root-ttl === CONT TestRoleCreate/error/root-ttl --- PASS: TestRoleCreate/error/root-ttl (0.00s) === RUN TestRoleCreate/not-existing-cloud === PAUSE TestRoleCreate/not-existing-cloud === CONT TestRoleCreate/not-existing-cloud --- PASS: TestRoleCreate/not-existing-cloud (0.00s) === RUN TestRoleCreate/save-store-err === PAUSE TestRoleCreate/save-store-err === CONT TestRoleCreate/save-store-err --- PASS: TestRoleCreate/save-store-err (0.00s) === RUN TestRoleUpdate === PAUSE TestRoleUpdate === CONT TestRoleUpdate --- PASS: TestRoleUpdate (0.00s) === RUN TestRoleUpdate/ok --- PASS: TestRoleUpdate/ok (0.00s) === RUN TestRoleUpdate/not-existing --- PASS: TestRoleUpdate/not-existing (0.00s) === RUN TestRotateRootCredentials_ok --- PASS: TestRotateRootCredentials_ok (0.00s) === RUN TestRotateRootCredentials_error === PAUSE TestRotateRootCredentials_error === CONT TestRotateRootCredentials_error --- PASS: TestRotateRootCredentials_error (0.01s) === RUN TestRotateRootCredentials_error/read-fail --- PASS: TestRotateRootCredentials_error/read-fail (0.00s) === RUN TestRotateRootCredentials_error/no-change --- PASS: TestRotateRootCredentials_error/no-change (0.00s) === RUN TestRotateRootCredentials_error/no-post --- PASS: TestRotateRootCredentials_error/no-post (0.00s) === RUN TestRotateRootCredentials_error/no-get --- PASS: TestRotateRootCredentials_error/no-get (0.00s) PASS ok github.com/opentelekomcloud/vault-plugin-secrets-openstack/openstack (cached) ? github.com/opentelekomcloud/vault-plugin-secrets-openstack/openstack/fixtures [no test files] Process finished with the exit code 0 Reviewed-by: Anton Sidelnikov <None>
1 parent 60e9d0f commit 296a798

4 files changed

Lines changed: 95 additions & 32 deletions

File tree

acceptance/creds_test.go

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,24 @@ import (
88
"net/http"
99
"testing"
1010

11+
"github.com/gophercloud/gophercloud/acceptance/tools"
1112
"github.com/opentelekomcloud/vault-plugin-secrets-openstack/openstack"
1213
"github.com/opentelekomcloud/vault-plugin-secrets-openstack/openstack/fixtures"
1314
"github.com/stretchr/testify/assert"
1415
"github.com/stretchr/testify/require"
1516
)
1617

18+
type testCase struct {
19+
Cloud string
20+
Username string
21+
ProjectID string
22+
DomainID string
23+
Root bool
24+
SecretType string
25+
UserRoles []string
26+
Extensions map[string]interface{}
27+
}
28+
1729
func (p *PluginTest) TestCredsLifecycle() {
1830
t := p.T()
1931

@@ -22,16 +34,6 @@ func (p *PluginTest) TestCredsLifecycle() {
2234

2335
_, aux := openstackClient(t)
2436

25-
type testCase struct {
26-
Cloud string
27-
ProjectID string
28-
DomainID string
29-
Root bool
30-
SecretType string
31-
UserRoles []string
32-
Extensions map[string]interface{}
33-
}
34-
3537
cases := map[string]testCase{
3638
"root_token": {
3739
Cloud: cloud.Name,
@@ -60,6 +62,29 @@ func (p *PluginTest) TestCredsLifecycle() {
6062
"object_store_endpoint_override": "https://swift.example.com",
6163
},
6264
},
65+
"static_user_token": {
66+
Cloud: cloud.Name,
67+
Username: tools.RandomString("vault-iam-", 3),
68+
ProjectID: aux.ProjectID,
69+
DomainID: aux.DomainID,
70+
Root: false,
71+
SecretType: "token",
72+
UserRoles: []string{"member"},
73+
Extensions: map[string]interface{}{
74+
"identity_api_version": "3",
75+
},
76+
},
77+
"static_user_password": {
78+
Cloud: cloud.Name,
79+
Username: tools.RandomString("vault-iam-", 3),
80+
ProjectID: aux.ProjectID,
81+
DomainID: aux.DomainID,
82+
Root: false,
83+
SecretType: "password",
84+
Extensions: map[string]interface{}{
85+
"object_store_endpoint_override": "https://swift.example.com",
86+
},
87+
},
6388
}
6489

6590
for name, data := range cases {
@@ -88,7 +113,7 @@ func (p *PluginTest) TestCredsLifecycle() {
88113
resp, err = p.vaultDo(
89114
http.MethodPost,
90115
roleURL(roleName),
91-
cloudToRoleMap(data.Root, data.Cloud, data.ProjectID, data.DomainID, data.SecretType, data.UserRoles, data.Extensions),
116+
cloudToRoleMap(data),
92117
)
93118
require.NoError(t, err)
94119
assert.Equal(t, http.StatusNoContent, resp.StatusCode, readJSONResponse(t, resp))
@@ -152,14 +177,15 @@ func cloudToCloudMap(cloud *openstack.OsCloud) map[string]interface{} {
152177
}
153178
}
154179

155-
func cloudToRoleMap(root bool, cloud, projectID, domainID, secretType string, userRoles []string, extensions map[string]interface{}) map[string]interface{} {
180+
func cloudToRoleMap(data testCase) map[string]interface{} {
156181
return fixtures.SanitizedMap(map[string]interface{}{
157-
"cloud": cloud,
158-
"project_id": projectID,
159-
"domain_id": domainID,
160-
"root": root,
161-
"secret_type": secretType,
162-
"user_roles": userRoles,
163-
"extensions": extensions,
182+
"cloud": data.Cloud,
183+
"username": data.Username,
184+
"project_id": data.ProjectID,
185+
"domain_id": data.DomainID,
186+
"root": data.Root,
187+
"secret_type": data.SecretType,
188+
"user_roles": data.UserRoles,
189+
"extensions": data.Extensions,
164190
})
165191
}

openstack/path_creds.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,20 @@ func getRootCredentials(client *gophercloud.ServiceClient, opts *credsOpts) (*lo
132132
return &logical.Response{Data: data, Secret: secret}, nil
133133
}
134134

135-
func getTmpUserCredentials(client *gophercloud.ServiceClient, opts *credsOpts) (*logical.Response, error) {
135+
func getUserCredentials(client *gophercloud.ServiceClient, opts *credsOpts) (*logical.Response, error) {
136136
password, err := opts.PwdGenerator.Generate(context.Background())
137137
if err != nil {
138138
return nil, err
139139
}
140140

141-
username, err := RandomTemporaryUsername(opts.UsernameTemplate, opts.Role)
142-
if err != nil {
143-
return logical.ErrorResponse("error generating username for temporary user: %s", err), nil
141+
username := opts.Role.Username
142+
static := true
143+
if username == "" {
144+
username, err = RandomTemporaryUsername(opts.UsernameTemplate, opts.Role)
145+
if err != nil {
146+
return logical.ErrorResponse("error generating username for temporary user: %s", err), nil
147+
}
148+
static = false
144149
}
145150

146151
user, err := createUser(client, username, password, opts.Role)
@@ -182,6 +187,7 @@ func getTmpUserCredentials(client *gophercloud.ServiceClient, opts *credsOpts) (
182187
"user_id": user.ID,
183188
"cloud": opts.Config.Name,
184189
"expires_at": token.ExpiresAt.String(),
190+
"static": static,
185191
}
186192
case SecretPassword:
187193
authResponse := &authResponseData{
@@ -202,6 +208,7 @@ func getTmpUserCredentials(client *gophercloud.ServiceClient, opts *credsOpts) (
202208
"secret_type": backendSecretTypeUser,
203209
"user_id": user.ID,
204210
"cloud": opts.Config.Name,
211+
"static": static,
205212
}
206213
default:
207214
return nil, fmt.Errorf("invalid secret type: %s", r)
@@ -211,15 +218,19 @@ func getTmpUserCredentials(client *gophercloud.ServiceClient, opts *credsOpts) (
211218
data[extensionKey] = extensionValue
212219
}
213220

221+
secret := &logical.Secret{
222+
InternalData: secretInternal,
223+
}
224+
if !static {
225+
secret.LeaseOptions = logical.LeaseOptions{
226+
TTL: opts.Role.TTL * time.Second,
227+
IssueTime: time.Now(),
228+
}
229+
}
230+
214231
return &logical.Response{
215-
Data: data,
216-
Secret: &logical.Secret{
217-
LeaseOptions: logical.LeaseOptions{
218-
TTL: opts.Role.TTL * time.Second,
219-
IssueTime: time.Now(),
220-
},
221-
InternalData: secretInternal,
222-
},
232+
Data: data,
233+
Secret: secret,
223234
}, nil
224235
}
225236

@@ -252,7 +263,7 @@ func (b *backend) pathCredsRead(ctx context.Context, r *logical.Request, d *fram
252263
return getRootCredentials(client, opts)
253264
}
254265

255-
return getTmpUserCredentials(client, opts)
266+
return getUserCredentials(client, opts)
256267
}
257268

258269
func (b *backend) tokenRevoke(ctx context.Context, r *logical.Request, d *framework.FieldData) (*logical.Response, error) {

openstack/path_role.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ func (b *backend) pathRole() *framework.Path {
7474
Description: "Specifies whenever to use the root user as a role actor.",
7575
Default: false,
7676
},
77+
"username": {
78+
Type: framework.TypeString,
79+
Description: "Specifies the username of the static user.",
80+
},
7781
"ttl": {
7882
Type: framework.TypeDurationSecond,
7983
Description: "Specifies TTL value for the dynamically created users as a string duration with time suffix.",
@@ -158,6 +162,7 @@ type roleEntry struct {
158162
SecretType secretType `json:"secret_type"`
159163
UserGroups []string `json:"user_groups"`
160164
UserRoles []string `json:"user_roles"`
165+
Username string `json:"username"`
161166
ProjectID string `json:"project_id"`
162167
ProjectName string `json:"project_name"`
163168
DomainID string `json:"domain_id"`
@@ -205,6 +210,7 @@ func roleToMap(src *roleEntry) map[string]interface{} {
205210
"root": src.Root,
206211
"ttl": src.TTL,
207212
"secret_type": string(src.SecretType),
213+
"username": src.Username,
208214
"user_groups": src.UserGroups,
209215
"user_roles": src.UserRoles,
210216
"project_id": src.ProjectID,
@@ -319,6 +325,13 @@ func (b *backend) pathRoleUpdate(ctx context.Context, req *logical.Request, d *f
319325
entry.UserRoles = roles.([]string)
320326
}
321327

328+
if username, ok := d.GetOk("username"); ok {
329+
if entry.Root {
330+
return logical.ErrorResponse(errInvalidForRoot, "username"), nil
331+
}
332+
entry.Username = username.(string)
333+
}
334+
322335
if err := saveRole(ctx, entry, req.Storage); err != nil {
323336
return nil, err
324337
}

openstack/path_role_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ func randomRoleName() string {
3131
return tools.RandomString("k", 5) + "m"
3232
}
3333

34+
func randomUsername() string {
35+
return tools.RandomString("u", 5)
36+
}
37+
3438
func expectedRoleData(cloudName string) (*roleEntry, map[string]interface{}) {
3539
expTTL := time.Hour
3640
expected := &roleEntry{
@@ -42,6 +46,7 @@ func expectedRoleData(cloudName string) (*roleEntry, map[string]interface{}) {
4246
expectedMap := map[string]interface{}{
4347
"cloud": expected.Cloud,
4448
"ttl": expTTL,
49+
"username": "",
4550
"project_id": "",
4651
"project_name": expected.ProjectName,
4752
"domain_id": "",
@@ -349,6 +354,14 @@ func TestRoleCreate(t *testing.T) {
349354
UserGroups: []string{"default", "testing"},
350355
TTL: 24 * time.Hour,
351356
},
357+
"username": {
358+
Name: randomRoleName(),
359+
Cloud: cloudName,
360+
ProjectName: randomRoleName(),
361+
Username: randomUsername(),
362+
SecretType: SecretToken,
363+
UserGroups: []string{"default", "testing"},
364+
},
352365
"endpoint-override": {
353366
Name: randomRoleName(),
354367
Cloud: cloudName,

0 commit comments

Comments
 (0)