Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for Issue 11863 - Panic when creating/updating approle role with token_type #11864

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion builtin/credential/approle/path_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -887,9 +887,11 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request
switch tokenTypeRaw.(string) {
case "default-service":
data.Raw["token_type"] = "service"
resp = &logical.Response{}
resp.AddWarning("default-service has no useful meaning; adjusting to service")
case "default-batch":
data.Raw["token_type"] = "batch"
resp = &logical.Response{}
resp.AddWarning("default-batch has no useful meaning; adjusting to batch")
}
}
Expand Down Expand Up @@ -976,7 +978,9 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request
}

if role.TokenMaxTTL > b.System().MaxLeaseTTL() {
resp = &logical.Response{}
if resp == nil {
resp = &logical.Response{}
}
resp.AddWarning("token_max_ttl is greater than the backend mount's maximum TTL value; issued tokens' max TTL value will be truncated")
}

Expand Down
130 changes: 130 additions & 0 deletions builtin/credential/approle/path_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,136 @@ func TestAppRole_RoleWithTokenBoundCIDRsCRUD(t *testing.T) {
}
}

func TestAppRole_RoleWithTokenTypeCRUD(t *testing.T) {
var resp *logical.Response
var err error
b, storage := createBackendWithStorage(t)

roleData := map[string]interface{}{
"policies": "p,q,r,s",
"secret_id_num_uses": 10,
"secret_id_ttl": 300,
"token_ttl": 400,
"token_max_ttl": 500,
"token_num_uses": 600,
"token_type": "default-service",
}
roleReq := &logical.Request{
Operation: logical.CreateOperation,
Path: "role/role1",
Storage: storage,
Data: roleData,
}

resp, err = b.HandleRequest(context.Background(), roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
if 0 == len(resp.Warnings) {
t.Fatalf("bad:\nexpected warning in resp:%#v\n", resp.Warnings)
}

roleReq.Operation = logical.ReadOperation
resp, err = b.HandleRequest(context.Background(), roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}

expected := map[string]interface{}{
"bind_secret_id": true,
"policies": []string{"p", "q", "r", "s"},
"secret_id_num_uses": 10,
"secret_id_ttl": 300,
"token_ttl": 400,
"token_max_ttl": 500,
"token_num_uses": 600,
"token_type": "service",
}

var expectedStruct roleStorageEntry
err = mapstructure.Decode(expected, &expectedStruct)
if err != nil {
t.Fatal(err)
}

var actualStruct roleStorageEntry
err = mapstructure.Decode(resp.Data, &actualStruct)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be missing something obvious, but can you skip decoding and just compare the two map[string]interface{} values? (along with setting the RoleID still)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, this test function is just a copy of the above test function TestAppRole_RoleWithTokenBoundCIDRsCRUD and then I removed the unnecessary bits.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't really know why the tests go through that additional mapstructure decode operation for the comparison. If I were to venture a guess, I suppose it does ensure that the fields being compared (some fields are being compared directly aside from the DeepEquals comparison) are properly tagged in the struct definition. That said, since it's clearly the established pattern in the test file, it feels wrong to break from the norm in a single test function.

if err != nil {
t.Fatal(err)
}

expectedStruct.RoleID = actualStruct.RoleID
if !reflect.DeepEqual(expectedStruct, actualStruct) {
t.Fatalf("bad:\nexpected:%#v\nactual:%#v\n", expectedStruct, actualStruct)
}

roleData = map[string]interface{}{
"role_id": "test_role_id",
"policies": "a,b,c,d",
"secret_id_num_uses": 100,
"secret_id_ttl": 3000,
"token_ttl": 4000,
"token_max_ttl": 5000,
"token_type": "default-service",
}
roleReq.Data = roleData
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to turn these into table-based tests so that the inputs for each test case can be treated as immutable and make it a bit clearer to see exactly what each test's inputs are.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean for just the various cases within this test function? The table-based tests are what I'm more used to doing, so I don't mind giving it a go. Seems like this test function could be combined with the TestAppRole_RoleCRUD function at the very least.

roleReq.Operation = logical.UpdateOperation

resp, err = b.HandleRequest(context.Background(), roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
if 0 == len(resp.Warnings) {
t.Fatalf("bad:\nexpected a warning in resp:%#v\n", resp.Warnings)
}

roleReq.Operation = logical.ReadOperation
resp, err = b.HandleRequest(context.Background(), roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}

expected = map[string]interface{}{
"policies": []string{"a", "b", "c", "d"},
"secret_id_num_uses": 100,
"secret_id_ttl": 3000,
"token_ttl": 4000,
"token_max_ttl": 5000,
"token_type": "service",
}
err = mapstructure.Decode(expected, &expectedStruct)
if err != nil {
t.Fatal(err)
}

err = mapstructure.Decode(resp.Data, &actualStruct)
if err != nil {
t.Fatal(err)
}

if !reflect.DeepEqual(expectedStruct, actualStruct) {
t.Fatalf("bad:\nexpected:%#v\nactual:%#v\n", expectedStruct, actualStruct)
}

// Delete test for role
roleReq.Path = "role/role1"
roleReq.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}

roleReq.Operation = logical.ReadOperation
resp, err = b.HandleRequest(context.Background(), roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}

if resp != nil {
t.Fatalf("expected a nil response")
}
}

func createRole(t *testing.T, b *backend, s logical.Storage, roleName, policies string) {
roleData := map[string]interface{}{
"policies": policies,
Expand Down
3 changes: 3 additions & 0 deletions changelog/11864.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
auth/approle: fixing dereference of nil pointer
```