Skip to content
This repository was archived by the owner on Mar 15, 2024. It is now read-only.

Commit 1914b98

Browse files
authored
Merge pull request #27 from splunk/mw/gh-26-user-id-scheme
Shorten generated usernames, add option user_id_scheme for roles
2 parents 2983331 + d4d2bc9 commit 1914b98

File tree

8 files changed

+182
-28
lines changed

8 files changed

+182
-28
lines changed

backend_test.go

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,29 @@ func TestBackend_basic(t *testing.T) {
2020
t.Fatal(err)
2121
}
2222

23-
roleConfig := roleConfig{
24-
Connection: "testconn",
25-
Roles: []string{"admin"},
26-
UserPrefix: defaultUserPrefix,
23+
schemes := []string{
24+
userIDSchemeUUID4_v0_5_0,
25+
userIDSchemeUUID4,
26+
userIDSchemeBase58_64,
27+
userIDSchemeBase58_128,
2728
}
29+
for _, scheme := range schemes {
30+
roleConfig := roleConfig{
31+
Connection: "testconn",
32+
Roles: []string{"admin"},
33+
UserPrefix: defaultUserPrefix,
34+
UserIDScheme: scheme,
35+
}
2836

29-
logicaltest.Test(t, logicaltest.TestCase{
30-
LogicalBackend: b,
31-
Steps: []logicaltest.TestStep{
32-
testAccStepConfig(t),
33-
testAccStepRole(t, "test", roleConfig),
34-
testAccStepCredsRead(t, "test"),
35-
},
36-
})
37+
logicaltest.Test(t, logicaltest.TestCase{
38+
LogicalBackend: b,
39+
Steps: []logicaltest.TestStep{
40+
testAccStepConfig(t),
41+
testAccStepRole(t, "test", roleConfig),
42+
testAccStepCredsRead(t, "test"),
43+
},
44+
})
45+
}
3746
}
3847

3948
func TestBackend_RotateRoot(t *testing.T) {
@@ -92,6 +101,7 @@ func TestBackend_RoleCRUD(t *testing.T) {
92101
AllowedServerRoles: []string{"*"},
93102
PasswordSpec: DefaultPasswordSpec(),
94103
UserPrefix: "my-custom-prefix",
104+
UserIDScheme: userIDSchemeUUID4,
95105
}
96106

97107
logicaltest.Test(t, logicaltest.TestCase{
@@ -105,17 +115,23 @@ func TestBackend_RoleCRUD(t *testing.T) {
105115
testAccStepRoleDelete(t, "test"),
106116
},
107117
})
108-
emptyUserPrefixConfig := roleConfig{
109-
Connection: "testconn",
110-
Roles: []string{"admin"},
111-
UserPrefix: "",
112-
}
118+
emptyUserPrefixConfig := testRoleConfig
119+
emptyUserPrefixConfig.UserPrefix = ""
113120
logicaltest.Test(t, logicaltest.TestCase{
114121
LogicalBackend: b,
115122
Steps: []logicaltest.TestStep{
116123
testEmptyUserPrefix(t, "test", emptyUserPrefixConfig),
117124
},
118125
})
126+
127+
userIDSchemeConfig := testRoleConfig
128+
userIDSchemeConfig.UserIDScheme = "-invalid-"
129+
logicaltest.Test(t, logicaltest.TestCase{
130+
LogicalBackend: b,
131+
Steps: []logicaltest.TestStep{
132+
testUserIDScheme(t, "test", "-invalid-", userIDSchemeConfig),
133+
},
134+
})
119135
}
120136

121137
// Test steps
@@ -219,6 +235,22 @@ func testEmptyUserPrefix(t *testing.T, role string, config roleConfig) logicalte
219235
}
220236
}
221237

238+
func testUserIDScheme(t *testing.T, role, idScheme string, config roleConfig) logicaltest.TestStep {
239+
return logicaltest.TestStep{
240+
Operation: logical.CreateOperation,
241+
Path: rolesPrefix + role,
242+
Data: config.toResponseData(),
243+
ErrorOk: true,
244+
Check: func(resp *logical.Response) error {
245+
if resp == nil {
246+
return fmt.Errorf("response is nil")
247+
}
248+
assert.Error(t, resp.Error(), fmt.Sprintf("invalid user_id_scheme: %q", idScheme))
249+
return nil
250+
},
251+
}
252+
}
253+
222254
func testAccStepCredsRead(t *testing.T, role string) logicaltest.TestStep {
223255
return logicaltest.TestStep{
224256
Operation: logical.ReadOperation,
@@ -235,8 +267,11 @@ func testAccStepCredsRead(t *testing.T, role string) logicaltest.TestStep {
235267
if err := mapstructure.Decode(resp.Data, &d); err != nil {
236268
return err
237269
}
238-
t.Logf("[WARN] Generated credentials: %+v", d)
239-
// XXXX check that generated user can login
270+
// check that generated user can login
271+
conn := splunk.NewTestSplunkClient(d.URL, d.Username, d.Password)
272+
_, _, err := conn.Introspection.ServerInfo()
273+
assert.NilError(t, err)
274+
240275
// XXXX check that generated user is deleted if lease expires
241276
return nil
242277
},

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ require (
4141
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
4242
github.com/mitchellh/mapstructure v1.1.2
4343
github.com/mitchellh/reflectwalk v1.0.1 // indirect
44+
github.com/mr-tron/base58 v1.1.3
4445
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
4546
github.com/opencontainers/image-spec v1.0.1 // indirect
4647
github.com/opencontainers/runc v0.1.1 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
145145
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
146146
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
147147
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
148+
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
149+
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
148150
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
149151
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
150152
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=

path_creds_create.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,18 @@ func (b *backend) credsReadHandler(ctx context.Context, req *logical.Request, d
252252
}
253253

254254
func generateUserID(roleConfig *roleConfig) (string, error) {
255-
return uuid.GenerateUUID()
255+
switch roleConfig.UserIDScheme {
256+
case userIDSchemeUUID4_v0_5_0:
257+
fallthrough
258+
case userIDSchemeUUID4:
259+
return uuid.GenerateUUID()
260+
case userIDSchemeBase58_64:
261+
return GenerateShortUUID(8)
262+
case userIDSchemeBase58_128:
263+
return GenerateShortUUID(16)
264+
default:
265+
return "", fmt.Errorf("invalid user_id_scheme: %q", roleConfig.UserIDScheme)
266+
}
256267
}
257268

258269
func generateUserPassword(roleConfig *roleConfig) (string, error) {

path_roles.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,22 @@ package splunk
22

33
import (
44
"context"
5+
"fmt"
56
"time"
67

78
"github.com/hashicorp/vault/logical"
89
"github.com/hashicorp/vault/logical/framework"
910
)
1011

11-
const rolesPrefix = "roles/"
12-
const defaultUserPrefix = "vault"
12+
const (
13+
rolesPrefix = "roles/"
14+
defaultUserPrefix = "vault"
15+
16+
userIDSchemeUUID4_v0_5_0 = ""
17+
userIDSchemeUUID4 = "uuid4"
18+
userIDSchemeBase58_64 = "base58-64"
19+
userIDSchemeBase58_128 = "base58-128"
20+
)
1321

1422
func (b *backend) pathRoles() *framework.Path {
1523
return &framework.Path{
@@ -58,9 +66,15 @@ func (b *backend) pathRoles() *framework.Path {
5866
},
5967
"user_prefix": &framework.FieldSchema{
6068
Type: framework.TypeString,
61-
Description: "Prefix for creating new users",
69+
Description: "Prefix for creating new users.",
6270
Default: defaultUserPrefix,
6371
},
72+
"user_id_scheme": &framework.FieldSchema{
73+
Type: framework.TypeLowerCaseString,
74+
Description: fmt.Sprintf("ID generation scheme (%s, %s, %s). Default: %s",
75+
userIDSchemeUUID4, userIDSchemeBase58_64, userIDSchemeBase58_128, userIDSchemeBase58_64),
76+
Default: userIDSchemeBase58_64,
77+
},
6478
},
6579
Callbacks: map[logical.Operation]framework.OperationFunc{
6680
logical.ReadOperation: b.rolesReadHandler,
@@ -149,6 +163,18 @@ func (b *backend) rolesWriteHandler(ctx context.Context, req *logical.Request, d
149163
return logical.ErrorResponse("user_prefix can't be set to empty string"), nil
150164
}
151165

166+
if userIDSchemeRaw, ok := getValue(data, req.Operation, "user_id_scheme"); ok {
167+
role.UserIDScheme = userIDSchemeRaw.(string)
168+
}
169+
switch role.UserIDScheme {
170+
case userIDSchemeUUID4_v0_5_0:
171+
case userIDSchemeUUID4:
172+
case userIDSchemeBase58_64:
173+
case userIDSchemeBase58_128:
174+
default:
175+
return logical.ErrorResponse("invalid user_id_scheme: %q", role.UserIDScheme), nil
176+
}
177+
152178
if err := role.store(ctx, req.Storage, name); err != nil {
153179
return nil, err
154180
}

role.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ type roleConfig struct {
1818
PasswordSpec *PasswordSpec `json:"password_spec" structs:"password_spec"`
1919

2020
// Splunk user attributes
21-
Roles []string `json:"roles" structs:"roles"`
22-
DefaultApp string `json:"default_app,omitempty" structs:"default_app"`
23-
Email string `json:"email,omitempty" structs:"email"`
24-
TZ string `json:"tz,omitempty" structs:"tz"`
25-
UserPrefix string `json:"user_prefix,omitempty" structs:"user_prefix"`
21+
Roles []string `json:"roles" structs:"roles"`
22+
DefaultApp string `json:"default_app,omitempty" structs:"default_app"`
23+
Email string `json:"email,omitempty" structs:"email"`
24+
TZ string `json:"tz,omitempty" structs:"tz"`
25+
UserPrefix string `json:"user_prefix,omitempty" structs:"user_prefix"`
26+
UserIDScheme string `json:"user_id_scheme,omitempty" structs:"user_id_scheme"`
2627
}
2728

2829
// Role returns nil if role named `name` does not exist in `storage`, otherwise

uuid.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package splunk
2+
3+
import (
4+
"github.com/hashicorp/go-uuid"
5+
"github.com/mr-tron/base58"
6+
)
7+
8+
func GenerateShortUUID(size int) (string, error) {
9+
bytes, err := uuid.GenerateRandomBytes(size)
10+
if err != nil {
11+
return "", err
12+
}
13+
return FormatShortUUID(bytes), nil
14+
}
15+
16+
func FormatShortUUID(bytes []byte) string {
17+
return base58.Encode(bytes)
18+
}

uuid_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package splunk
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/mr-tron/base58"
8+
"gotest.tools/assert"
9+
)
10+
11+
func TestGenerateShortUUID(t *testing.T) {
12+
for _, size := range []int{8, 16} {
13+
uuid, err := GenerateShortUUID(size)
14+
assert.NilError(t, err)
15+
fmt.Println(uuid)
16+
bytes, err := base58.Decode(uuid)
17+
assert.NilError(t, err)
18+
assert.Equal(t, size, len(bytes))
19+
}
20+
}
21+
22+
func TestFormatShortUUID(t *testing.T) {
23+
type args struct {
24+
bytes []byte
25+
}
26+
tests := []struct {
27+
name string
28+
args args
29+
want string
30+
}{
31+
{
32+
name: "0_x_8",
33+
args: args{[]byte{0, 0, 0, 0, 0, 0, 0, 0}},
34+
want: "11111111",
35+
},
36+
{
37+
name: "1_x_8",
38+
args: args{[]byte{0, 0, 0, 0, 0, 0, 0, 1}},
39+
want: "11111112",
40+
},
41+
{
42+
name: "255_x_8",
43+
args: args{[]byte{255, 255, 255, 255, 255, 255, 255, 255}},
44+
want: "jpXCZedGfVQ",
45+
},
46+
{
47+
name: "uuid4",
48+
args: args{[]byte{0xb3, 0x3b, 0x6b, 0x76, 0xcb, 0x1f, 0xbe, 0x28, 0xbd, 0x5b, 0x86, 0xca, 0x76, 0x23, 0x72, 0x72}},
49+
want: "P8gD5AcMf2n6FkGz9nydEZ",
50+
},
51+
}
52+
53+
for _, tt := range tests {
54+
t.Run(tt.name, func(t *testing.T) {
55+
if got := FormatShortUUID(tt.args.bytes); got != tt.want {
56+
t.Errorf("FormatShortUUID() = %v, want %v", got, tt.want)
57+
}
58+
})
59+
}
60+
}

0 commit comments

Comments
 (0)