diff options
Diffstat (limited to 'path_static_roles.go')
-rw-r--r-- | path_static_roles.go | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/path_static_roles.go b/path_static_roles.go new file mode 100644 index 0000000..a9b3ee9 --- /dev/null +++ b/path_static_roles.go @@ -0,0 +1,202 @@ +package secretsengine + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/logical" +) + +const staticRolePath = "static-role" + +// staticRoleEntry defines the data required +// for a static role, using a set principal +type staticRoleEntry struct { + Principal string `json:"principal"` + Password string `json:"password"` + LastVaultRotation time.Time `json:"last_vault_rotation"` +} + +// toResponseData returns response data for a role +func (r *staticRoleEntry) toResponseData() map[string]interface{} { + respData := map[string]interface{}{ + "principal": r.Principal, + "last_vault_rotation": r.LastVaultRotation, + } + return respData +} + +// pathStaticRole extends the Vault API with a `/static-role` +// endpoint for the backend. +func pathStaticRole(b *krbBackend) []*framework.Path { + return []*framework.Path{ + { + Pattern: staticRolePath + "/" + framework.GenericNameRegex("name"), + Fields: map[string]*framework.FieldSchema{ + "name": { + Type: framework.TypeLowerCaseString, + Description: "Name of the role", + Required: true, + }, + "principal": { + Type: framework.TypeString, + Description: "The principal credentials should be generated for.", + Required: true, + }, + "last_vault_rotation": { + Type: framework.TypeDurationSecond, + Description: "Last time the credentials were rotated.", + }, + }, + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.pathRolesRead, + }, + logical.CreateOperation: &framework.PathOperation{ + Callback: b.pathRolesWrite, + }, + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.pathRolesWrite, + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.pathRolesDelete, + }, + }, + HelpSynopsis: pathRoleHelpSynopsis, + HelpDescription: pathRoleHelpDescription, + }, + { + Pattern: staticRolePath + "/?$", + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{ + Callback: b.pathRolesList, + }, + }, + HelpSynopsis: pathRoleListHelpSynopsis, + HelpDescription: pathRoleListHelpDescription, + }, + } +} + +// pathRolesList makes a request to Vault storage to retrieve a list of roles for the backend +func (b *krbBackend) pathRolesList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + entries, err := req.Storage.List(ctx, staticRolePath) + if err != nil { + return nil, err + } + + return logical.ListResponse(entries), nil +} + +// pathRolesRead makes a request to Vault storage to read a role and return response data +func (b *krbBackend) pathRolesRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + entry, err := b.getRole(ctx, req.Storage, d.Get("name").(string)) + if err != nil { + return nil, err + } + + if entry == nil { + return nil, nil + } + + return &logical.Response{ + Data: entry.toResponseData(), + }, nil +} + +// pathRolesWrite makes a request to Vault storage to update a role based on the attributes passed to the role configuration +func (b *krbBackend) pathRolesWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + name, ok := d.GetOk("name") + if !ok { + return logical.ErrorResponse("missing role name"), nil + } + + roleEntry, err := b.getRole(ctx, req.Storage, name.(string)) + if err != nil { + return nil, err + } + + if roleEntry == nil { + roleEntry = &staticRoleEntry{} + } + + createOperation := (req.Operation == logical.CreateOperation) + + if principal, ok := d.GetOk("principal"); ok { + roleEntry.Principal = principal.(string) + } else if !ok && createOperation { + return nil, fmt.Errorf("missing principal in role") + } + + roleEntry.LastVaultRotation = time.Unix(0, 0) + + if err := setRole(ctx, req.Storage, name.(string), roleEntry); err != nil { + return nil, err + } + + return nil, nil +} + +// pathRolesDelete makes a request to Vault storage to delete a role +func (b *krbBackend) pathRolesDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + err := req.Storage.Delete(ctx, staticRolePath+"/"+d.Get("name").(string)) + if err != nil { + return nil, fmt.Errorf("error deleting krb role: %w", err) + } + + return nil, nil +} + +// setRole adds the role to the Vault storage API +func setRole(ctx context.Context, s logical.Storage, name string, roleEntry *staticRoleEntry) error { + entry, err := logical.StorageEntryJSON(staticRolePath+"/"+name, roleEntry) + if err != nil { + return err + } + + if entry == nil { + return fmt.Errorf("failed to create storage entry for role") + } + + if err := s.Put(ctx, entry); err != nil { + return err + } + + return nil +} + +// getRole gets the role from the Vault storage API +func (b *krbBackend) getRole(ctx context.Context, s logical.Storage, name string) (*staticRoleEntry, error) { + if name == "" { + return nil, fmt.Errorf("missing role name") + } + + entry, err := s.Get(ctx, staticRolePath+"/"+name) + if err != nil { + return nil, err + } + + if entry == nil { + return nil, nil + } + + var role staticRoleEntry + + if err := entry.DecodeJSON(&role); err != nil { + return nil, err + } + return &role, nil +} + +const ( + pathRoleHelpSynopsis = `Manages the Vault roles for credentials for individual Kerberos principals.` + pathRoleHelpDescription = ` +This path allows you to read and write roles used to generate credentials for individual Kerberos principals. +You can configure a role to manage a principal's password by setting the principal field. +` + + pathRoleListHelpSynopsis = `List the existing roles in HashiCups backend` + pathRoleListHelpDescription = `Roles will be listed by the role name.` +) |