Seperate endpoints for read/delete using secret-id and accessor
This commit is contained in:
parent
865ca94032
commit
dfe73733d5
|
@ -75,8 +75,10 @@ type roleIDStorageEntry struct {
|
||||||
// role/<role_name>/period - For updating the param
|
// role/<role_name>/period - For updating the param
|
||||||
// role/<role_name>/role-id - For fetching the role_id of an role
|
// role/<role_name>/role-id - For fetching the role_id of an role
|
||||||
// role/<role_name>/secret-id - For issuing a secret_id against an role, also to list the secret_id_accessorss
|
// role/<role_name>/secret-id - For issuing a secret_id against an role, also to list the secret_id_accessorss
|
||||||
// role/<role_name>/secret-id/<secret_id_accessor> - For reading the properties of, or deleting a secret_id
|
|
||||||
// role/<role_name>/custom-secret-id - For assigning a custom SecretID against an role
|
// role/<role_name>/custom-secret-id - For assigning a custom SecretID against an role
|
||||||
|
// role/<role_name>/secret-id/<secret_id> - For reading the properties of, or deleting a secret_id
|
||||||
|
// role/<role_name>/secret-id-accessor/<secret_id_accessor> - For reading the
|
||||||
|
// properties of, or deleting a secret_id, using the accessor of secret_id.
|
||||||
func rolePaths(b *backend) []*framework.Path {
|
func rolePaths(b *backend) []*framework.Path {
|
||||||
return []*framework.Path{
|
return []*framework.Path{
|
||||||
&framework.Path{
|
&framework.Path{
|
||||||
|
@ -363,7 +365,28 @@ formatted string containing the metadata in key value pairs.`,
|
||||||
HelpDescription: strings.TrimSpace(roleHelp["role-secret-id"][1]),
|
HelpDescription: strings.TrimSpace(roleHelp["role-secret-id"][1]),
|
||||||
},
|
},
|
||||||
&framework.Path{
|
&framework.Path{
|
||||||
Pattern: "role/" + framework.GenericNameRegex("role_name") + "/secret-id/" + framework.GenericNameRegex("secret_id_accessor"),
|
Pattern: "role/" +
|
||||||
|
framework.GenericNameRegex("role_name") + "/secret-id/" + framework.GenericNameRegex("secret_id"),
|
||||||
|
Fields: map[string]*framework.FieldSchema{
|
||||||
|
"role_name": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: "Name of the role.",
|
||||||
|
},
|
||||||
|
"secret_id": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: "SecretID attached to the role.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.ReadOperation: b.pathRoleSecretIDSecretIDRead,
|
||||||
|
logical.DeleteOperation: b.pathRoleSecretIDSecretIDDelete,
|
||||||
|
},
|
||||||
|
HelpSynopsis: strings.TrimSpace(roleHelp["role-secret-id-secret-id"][0]),
|
||||||
|
HelpDescription: strings.TrimSpace(roleHelp["role-secret-id-secret-id"][1]),
|
||||||
|
},
|
||||||
|
&framework.Path{
|
||||||
|
Pattern: "role/" +
|
||||||
|
framework.GenericNameRegex("role_name") + "/secret-id-accessor/" + framework.GenericNameRegex("secret_id_accessor"),
|
||||||
Fields: map[string]*framework.FieldSchema{
|
Fields: map[string]*framework.FieldSchema{
|
||||||
"role_name": &framework.FieldSchema{
|
"role_name": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
|
@ -741,6 +764,131 @@ func (b *backend) pathRoleDelete(req *logical.Request, data *framework.FieldData
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the properties of the SecretID
|
||||||
|
func (b *backend) pathRoleSecretIDSecretIDRead(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
roleName := data.Get("role_name").(string)
|
||||||
|
if roleName == "" {
|
||||||
|
return logical.ErrorResponse("missing role_name"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
secretID := data.Get("secret_id").(string)
|
||||||
|
if secretID == "" {
|
||||||
|
return logical.ErrorResponse("missing secret_id"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the role
|
||||||
|
role, err := b.roleEntry(req.Storage, strings.ToLower(roleName))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if role == nil {
|
||||||
|
return nil, fmt.Errorf("role %s does not exist", roleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the HMAC of the secret ID using the per-role HMAC key
|
||||||
|
secretIDHMAC, err := createHMAC(role.HMACKey, secretID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create HMAC of secret_id: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the HMAC of the roleName using the per-role HMAC key
|
||||||
|
roleNameHMAC, err := createHMAC(role.HMACKey, roleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create HMAC of role_name: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the index at which the secret_id would've been stored
|
||||||
|
entryIndex := fmt.Sprintf("secret_id/%s/%s", roleNameHMAC, secretIDHMAC)
|
||||||
|
|
||||||
|
lock := b.secretIDLock(secretIDHMAC)
|
||||||
|
lock.RLock()
|
||||||
|
defer lock.RUnlock()
|
||||||
|
|
||||||
|
result := secretIDStorageEntry{}
|
||||||
|
if entry, err := req.Storage.Get(entryIndex); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if entry == nil {
|
||||||
|
return nil, nil
|
||||||
|
} else if err := entry.DecodeJSON(&result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result.SecretIDTTL /= time.Second
|
||||||
|
d := structs.New(result).Map()
|
||||||
|
|
||||||
|
// Converting the time values to RFC3339Nano format.
|
||||||
|
//
|
||||||
|
// Map() from 'structs' package formats time in RFC3339Nano.
|
||||||
|
// In order to not break the API due to a modification in the
|
||||||
|
// third party package, converting the time values again.
|
||||||
|
d["creation_time"] = (d["creation_time"].(time.Time)).Format(time.RFC3339Nano)
|
||||||
|
d["expiration_time"] = (d["expiration_time"].(time.Time)).Format(time.RFC3339Nano)
|
||||||
|
d["last_updated_time"] = (d["last_updated_time"].(time.Time)).Format(time.RFC3339Nano)
|
||||||
|
|
||||||
|
return &logical.Response{
|
||||||
|
Data: d,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathRoleSecretIDSecretIDDelete(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
roleName := data.Get("role_name").(string)
|
||||||
|
if roleName == "" {
|
||||||
|
return logical.ErrorResponse("missing role_name"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
secretID := data.Get("secret_id").(string)
|
||||||
|
if secretID == "" {
|
||||||
|
return logical.ErrorResponse("missing secret_id"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
role, err := b.roleEntry(req.Storage, strings.ToLower(roleName))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if role == nil {
|
||||||
|
return nil, fmt.Errorf("role %s does not exist", roleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretIDHMAC, err := createHMAC(role.HMACKey, secretID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create HMAC of secret_id: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
roleNameHMAC, err := createHMAC(role.HMACKey, roleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create HMAC of role_name: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entryIndex := fmt.Sprintf("secret_id/%s/%s", roleNameHMAC, secretIDHMAC)
|
||||||
|
|
||||||
|
lock := b.secretIDLock(secretIDHMAC)
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
|
result := secretIDStorageEntry{}
|
||||||
|
if entry, err := req.Storage.Get(entryIndex); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if entry == nil {
|
||||||
|
return nil, nil
|
||||||
|
} else if err := entry.DecodeJSON(&result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accessorEntryIndex := "accessor/" + b.salt.SaltID(result.SecretIDAccessor)
|
||||||
|
|
||||||
|
// Delete the accessor of the SecretID first
|
||||||
|
if err := req.Storage.Delete(accessorEntryIndex); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to delete accessor storage entry: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the storage entry that corresponds to the SecretID
|
||||||
|
if err := req.Storage.Delete(entryIndex); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to delete SecretID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the properties of the SecretID
|
// Returns the properties of the SecretID
|
||||||
func (b *backend) pathRoleSecretIDAccessorRead(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
func (b *backend) pathRoleSecretIDAccessorRead(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
roleName := data.Get("role_name").(string)
|
roleName := data.Get("role_name").(string)
|
||||||
|
@ -1592,8 +1740,13 @@ that are generated against the role using 'role/<role_name>/secret-id' or
|
||||||
'role/<role_name>/custom-secret-id' endpoints.`,
|
'role/<role_name>/custom-secret-id' endpoints.`,
|
||||||
``,
|
``,
|
||||||
},
|
},
|
||||||
"role-secret-id-accessor": {
|
"role-secret-id-secret-id": {
|
||||||
"Read or delete a issued secret_id",
|
"Read or delete a issued secret_id",
|
||||||
|
`This endpoint is used to either read the properties of a
|
||||||
|
secret_id associated to a role or to invalidate it.`,
|
||||||
|
},
|
||||||
|
"role-secret-id-accessor": {
|
||||||
|
"Read or delete a issued secret_id, using its accessor",
|
||||||
`This is particularly useful to clean-up the non-expiring 'secret_id's.
|
`This is particularly useful to clean-up the non-expiring 'secret_id's.
|
||||||
The list operation on the 'role/<role_name>/secret-id' endpoint will return
|
The list operation on the 'role/<role_name>/secret-id' endpoint will return
|
||||||
the 'secret_id_accessor's. This endpoint can be used to read the properties
|
the 'secret_id_accessor's. This endpoint can be used to read the properties
|
||||||
|
|
|
@ -155,6 +155,52 @@ func TestAppRole_RoleSecretIDReadDelete(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
b, storage := createBackendWithStorage(t)
|
b, storage := createBackendWithStorage(t)
|
||||||
|
|
||||||
|
createRole(t, b, storage, "role1", "a,b")
|
||||||
|
secretIDCreateReq := &logical.Request{
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Storage: storage,
|
||||||
|
Path: "role/role1/secret-id",
|
||||||
|
}
|
||||||
|
resp, err = b.HandleRequest(secretIDCreateReq)
|
||||||
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
||||||
|
}
|
||||||
|
secretID := resp.Data["secret_id"].(string)
|
||||||
|
|
||||||
|
secretIDReq := &logical.Request{
|
||||||
|
Operation: logical.ReadOperation,
|
||||||
|
Storage: storage,
|
||||||
|
Path: "role/role1/secret-id/" + secretID,
|
||||||
|
}
|
||||||
|
resp, err = b.HandleRequest(secretIDReq)
|
||||||
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
||||||
|
}
|
||||||
|
if resp.Data == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretIDReq.Operation = logical.DeleteOperation
|
||||||
|
resp, err = b.HandleRequest(secretIDReq)
|
||||||
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretIDReq.Operation = logical.ReadOperation
|
||||||
|
resp, err = b.HandleRequest(secretIDReq)
|
||||||
|
if resp != nil && resp.IsError() {
|
||||||
|
t.Fatalf("error response:%#v", err, resp)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppRole_RoleSecretIDAccessorReadDelete(t *testing.T) {
|
||||||
|
var resp *logical.Response
|
||||||
|
var err error
|
||||||
|
b, storage := createBackendWithStorage(t)
|
||||||
|
|
||||||
createRole(t, b, storage, "role1", "a,b")
|
createRole(t, b, storage, "role1", "a,b")
|
||||||
secretIDReq := &logical.Request{
|
secretIDReq := &logical.Request{
|
||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
|
@ -180,7 +226,7 @@ func TestAppRole_RoleSecretIDReadDelete(t *testing.T) {
|
||||||
hmacReq := &logical.Request{
|
hmacReq := &logical.Request{
|
||||||
Operation: logical.ReadOperation,
|
Operation: logical.ReadOperation,
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
Path: "role/role1/secret-id/" + hmacSecretID,
|
Path: "role/role1/secret-id-accessor/" + hmacSecretID,
|
||||||
}
|
}
|
||||||
resp, err = b.HandleRequest(hmacReq)
|
resp, err = b.HandleRequest(hmacReq)
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
|
|
@ -545,7 +545,76 @@ $ curl -XPOST -H "X-Vault-Token:xxx" "http://127.0.0.1:8200/v1/auth/approle/logi
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
### /auth/approle/role/[role_name]/secret-id/<secret_id_accessor>
|
### /auth/approle/role/[role_name]/secret-id/<secret_id>
|
||||||
|
#### GET
|
||||||
|
<dl class="api">
|
||||||
|
<dt>Description</dt>
|
||||||
|
<dd>
|
||||||
|
Reads out the properties of a SecretID.
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dt>Method</dt>
|
||||||
|
<dd>`GET`</dd>
|
||||||
|
|
||||||
|
<dt>URL</dt>
|
||||||
|
<dd>`/auth/approle/role/[role_name]/secret-id/<secret_id>`</dd>
|
||||||
|
|
||||||
|
<dt>Parameters</dt>
|
||||||
|
<dd>
|
||||||
|
None.
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dt>Returns</dt>
|
||||||
|
<dd>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"auth": null,
|
||||||
|
"warnings": null,
|
||||||
|
"wrap_info": null,
|
||||||
|
"data": {
|
||||||
|
"secret_id_ttl": 600,
|
||||||
|
"secret_id_num_uses": 40,
|
||||||
|
"secret_id_accessor": "5e222f10-278d-a829-4e74-10d71977bb53",
|
||||||
|
"metadata": {},
|
||||||
|
"last_updated_time": "2016-06-29T05:31:09.407042587Z",
|
||||||
|
"expiration_time": "2016-06-29T05:41:09.407042587Z",
|
||||||
|
"creation_time": "2016-06-29T05:31:09.407042587Z"
|
||||||
|
},
|
||||||
|
"lease_duration": 0,
|
||||||
|
"renewable": false,
|
||||||
|
"lease_id": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
#### DELETE
|
||||||
|
<dl class="api">
|
||||||
|
<dt>Description</dt>
|
||||||
|
<dd>
|
||||||
|
Deletes a SecretID.
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dt>Method</dt>
|
||||||
|
<dd>`DELETE`</dd>
|
||||||
|
|
||||||
|
<dt>URL</dt>
|
||||||
|
<dd>`/auth/approle/role/[role_name]/secret-id/<secret_id>`</dd>
|
||||||
|
|
||||||
|
<dt>Parameters</dt>
|
||||||
|
<dd>
|
||||||
|
None.
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dt>Returns</dt>
|
||||||
|
<dd>
|
||||||
|
`204` response code.
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
### /auth/approle/role/[role_name]/secret-id-accessor/<secret_id_accessor>
|
||||||
#### GET
|
#### GET
|
||||||
<dl class="api">
|
<dl class="api">
|
||||||
<dt>Description</dt>
|
<dt>Description</dt>
|
||||||
|
@ -558,7 +627,7 @@ $ curl -XPOST -H "X-Vault-Token:xxx" "http://127.0.0.1:8200/v1/auth/approle/logi
|
||||||
<dd>`GET`</dd>
|
<dd>`GET`</dd>
|
||||||
|
|
||||||
<dt>URL</dt>
|
<dt>URL</dt>
|
||||||
<dd>`/auth/approle/role/[role_name]/secret-id/<secret_id_accessor>`</dd>
|
<dd>`/auth/approle/role/[role_name]/secret-id-accessor/<secret_id_accessor>`</dd>
|
||||||
|
|
||||||
<dt>Parameters</dt>
|
<dt>Parameters</dt>
|
||||||
<dd>
|
<dd>
|
||||||
|
@ -602,7 +671,7 @@ $ curl -XPOST -H "X-Vault-Token:xxx" "http://127.0.0.1:8200/v1/auth/approle/logi
|
||||||
<dd>`DELETE`</dd>
|
<dd>`DELETE`</dd>
|
||||||
|
|
||||||
<dt>URL</dt>
|
<dt>URL</dt>
|
||||||
<dd>`/auth/approle/role/[role_name]/secret-id/<secret_id_accessor>`</dd>
|
<dd>`/auth/approle/role/[role_name]/secret-id-accessor/<secret_id_accessor>`</dd>
|
||||||
|
|
||||||
<dt>Parameters</dt>
|
<dt>Parameters</dt>
|
||||||
<dd>
|
<dd>
|
||||||
|
|
Loading…
Reference in a new issue