Update help text and comments
This commit is contained in:
parent
c85b7be22f
commit
128f25c13d
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/hashicorp/vault/helper/pluginutil"
|
||||
)
|
||||
|
||||
// DatabasePluginClient embeds a databasePluginRPCClient and wraps it's close
|
||||
// DatabasePluginClient embeds a databasePluginRPCClient and wraps it's Close
|
||||
// method to also call Kill() on the plugin.Client.
|
||||
type DatabasePluginClient struct {
|
||||
client *plugin.Client
|
||||
|
@ -64,7 +64,7 @@ func newPluginClient(sys pluginutil.Wrapper, pluginRunner *pluginutil.PluginRunn
|
|||
|
||||
// ---- RPC client domain ----
|
||||
|
||||
// databasePluginRPCClient impliments DatabaseType and is used on the client to
|
||||
// databasePluginRPCClient implements DatabaseType and is used on the client to
|
||||
// make RPC calls to a plugin.
|
||||
type databasePluginRPCClient struct {
|
||||
client *rpc.Client
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
|
||||
// ---- Tracing Middleware Domain ----
|
||||
|
||||
// databaseTracingMiddleware wraps a implementation of DatabaseType and executes
|
||||
// trace logging on function call.
|
||||
type databaseTracingMiddleware struct {
|
||||
next DatabaseType
|
||||
logger log.Logger
|
||||
|
@ -77,6 +79,8 @@ func (mw *databaseTracingMiddleware) Close() (err error) {
|
|||
|
||||
// ---- Metrics Middleware Domain ----
|
||||
|
||||
// databaseMetricsMiddleware wraps an implementation of DatabaseTypes and on
|
||||
// function call logs metrics about this instance.
|
||||
type databaseMetricsMiddleware struct {
|
||||
next DatabaseType
|
||||
|
||||
|
|
|
@ -40,11 +40,13 @@ func PluginFactory(pluginName string, sys pluginutil.LookWrapper, logger log.Log
|
|||
return nil, ErrEmptyPluginName
|
||||
}
|
||||
|
||||
// Look for plugin in the plugin catalog
|
||||
pluginMeta, err := sys.LookupPlugin(pluginName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create a DatabasePluginClient instance
|
||||
db, err := newPluginClient(sys, pluginMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -76,6 +78,8 @@ var handshakeConfig = plugin.HandshakeConfig{
|
|||
MagicCookieValue: "926a0820-aea2-be28-51d6-83cdf00e8edb",
|
||||
}
|
||||
|
||||
// DatabasePlugin implements go-plugin's Plugin interface. It has methods for
|
||||
// retrieving a server and a client instance of the plugin.
|
||||
type DatabasePlugin struct {
|
||||
impl DatabaseType
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// NewPluginServer is called from within a plugin and wraps the provided
|
||||
// DatabaseType implimentation in a databasePluginRPCServer object and starts a
|
||||
// DatabaseType implementation in a databasePluginRPCServer object and starts a
|
||||
// RPC server.
|
||||
func NewPluginServer(db DatabaseType) {
|
||||
dbPlugin := &DatabasePlugin{
|
||||
|
@ -35,7 +35,8 @@ func NewPluginServer(db DatabaseType) {
|
|||
|
||||
// ---- RPC server domain ----
|
||||
|
||||
// databasePluginRPCServer impliments DatabaseType and is run inside a plugin
|
||||
// databasePluginRPCServer implements an RPC version of DatabaseType and is run
|
||||
// inside a plugin. It wraps an underlying implementation of DatabaseType.
|
||||
type databasePluginRPCServer struct {
|
||||
impl DatabaseType
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
// pathResetConnection configures a path to reset a plugin.
|
||||
func pathResetConnection(b *databaseBackend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: fmt.Sprintf("reset/%s", framework.GenericNameRegex("name")),
|
||||
|
@ -20,32 +21,36 @@ func pathResetConnection(b *databaseBackend) *framework.Path {
|
|||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: b.pathConnectionReset,
|
||||
logical.UpdateOperation: b.pathConnectionReset(),
|
||||
},
|
||||
|
||||
HelpSynopsis: pathConfigConnectionHelpSyn,
|
||||
HelpDescription: pathConfigConnectionHelpDesc,
|
||||
HelpSynopsis: pathResetConnectionHelpSyn,
|
||||
HelpDescription: pathResetConnectionHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *databaseBackend) pathConnectionReset(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
name := data.Get("name").(string)
|
||||
if name == "" {
|
||||
return logical.ErrorResponse("Empty name attribute given"), nil
|
||||
// pathConnectionReset resets a plugin by closing the existing instance and
|
||||
// creating a new one.
|
||||
func (b *databaseBackend) pathConnectionReset() framework.OperationFunc {
|
||||
return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
name := data.Get("name").(string)
|
||||
if name == "" {
|
||||
return logical.ErrorResponse("Empty name attribute given"), nil
|
||||
}
|
||||
|
||||
// Grab the mutex lock
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
b.clearConnection(name)
|
||||
|
||||
_, err := b.getOrCreateDBObj(req.Storage, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Grab the mutex lock
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
b.clearConnection(name)
|
||||
|
||||
_, err := b.getOrCreateDBObj(req.Storage, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// pathConfigurePluginConnection returns a configured framework.Path setup to
|
||||
|
@ -60,15 +65,17 @@ func pathConfigurePluginConnection(b *databaseBackend) *framework.Path {
|
|||
},
|
||||
|
||||
"verify_connection": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Default: true,
|
||||
Description: `If set, connection_url is verified by actually connecting to the database`,
|
||||
Type: framework.TypeBool,
|
||||
Default: true,
|
||||
Description: `If set, the connection details are verified by
|
||||
actually connecting to the database`,
|
||||
},
|
||||
|
||||
"plugin_name": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: `Maximum amount of time a connection may be reused;
|
||||
a zero or negative value reuses connections forever.`,
|
||||
Description: `The name of a builtin or previously registered
|
||||
plugin known to vault. This endpoint will create an instance of
|
||||
that plugin type.`,
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -198,16 +205,32 @@ func (b *databaseBackend) connectionWriteHandler() framework.OperationFunc {
|
|||
}
|
||||
|
||||
const pathConfigConnectionHelpSyn = `
|
||||
Configure the connection string to talk to PostgreSQL.
|
||||
Configure connection details to a database plugin.
|
||||
`
|
||||
|
||||
const pathConfigConnectionHelpDesc = `
|
||||
This path configures the connection string used to connect to PostgreSQL.
|
||||
The value of the string can be a URL, or a PG style string in the
|
||||
format of "user=foo host=bar" etc.
|
||||
This path configures the connection details used to connect to a particular
|
||||
database. This path runs the provided plugin name and passes the configured
|
||||
connection details to the plugin. See the documentation for the plugin specified
|
||||
for a full list of accepted connection details.
|
||||
|
||||
The URL looks like:
|
||||
"postgresql://user:pass@host:port/dbname"
|
||||
In addition to the database specific connection details, this endpoing also
|
||||
accepts:
|
||||
|
||||
When configuring the connection string, the backend will verify its validity.
|
||||
* "plugin_name" (required) - The name of a builtin or previously registered
|
||||
plugin known to vault. This endpoint will create an instance of that
|
||||
plugin type.
|
||||
|
||||
* "verify_connection" - A boolean value denoting if the plugin should verify
|
||||
it is able to connect to the database using the provided connection
|
||||
details.
|
||||
`
|
||||
|
||||
const pathResetConnectionHelpSyn = `
|
||||
Resets a database plugin.
|
||||
`
|
||||
|
||||
const pathResetConnectionHelpDesc = `
|
||||
This path resets the database connection by closing the existing database plugin
|
||||
instance and running a new one.
|
||||
`
|
||||
|
|
|
@ -19,7 +19,7 @@ func pathRoleCreate(b *databaseBackend) *framework.Path {
|
|||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ReadOperation: b.pathRoleCreateRead,
|
||||
logical.ReadOperation: b.pathRoleCreateRead(),
|
||||
},
|
||||
|
||||
HelpSynopsis: pathRoleCreateReadHelpSyn,
|
||||
|
@ -27,45 +27,47 @@ func pathRoleCreate(b *databaseBackend) *framework.Path {
|
|||
}
|
||||
}
|
||||
|
||||
func (b *databaseBackend) pathRoleCreateRead(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
name := data.Get("name").(string)
|
||||
func (b *databaseBackend) pathRoleCreateRead() framework.OperationFunc {
|
||||
return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
name := data.Get("name").(string)
|
||||
|
||||
// Get the role
|
||||
role, err := b.Role(req.Storage, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Get the role
|
||||
role, err := b.Role(req.Storage, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("Unknown role: %s", name)), nil
|
||||
}
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
// Get the Database object
|
||||
db, err := b.getOrCreateDBObj(req.Storage, role.DBName)
|
||||
if err != nil {
|
||||
// TODO: return a resp error instead?
|
||||
return nil, fmt.Errorf("cound not retrieve db with name: %s, got error: %s", role.DBName, err)
|
||||
}
|
||||
|
||||
expiration := time.Now().Add(role.DefaultTTL)
|
||||
|
||||
// Create the user
|
||||
username, password, err := db.CreateUser(role.Statements, req.DisplayName, expiration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := b.Secret(SecretCredsType).Response(map[string]interface{}{
|
||||
"username": username,
|
||||
"password": password,
|
||||
}, map[string]interface{}{
|
||||
"username": username,
|
||||
"role": name,
|
||||
})
|
||||
resp.Secret.TTL = role.DefaultTTL
|
||||
return resp, nil
|
||||
}
|
||||
if role == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("Unknown role: %s", name)), nil
|
||||
}
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
// Get the Database object
|
||||
db, err := b.getOrCreateDBObj(req.Storage, role.DBName)
|
||||
if err != nil {
|
||||
// TODO: return a resp error instead?
|
||||
return nil, fmt.Errorf("cound not retrieve db with name: %s, got error: %s", role.DBName, err)
|
||||
}
|
||||
|
||||
expiration := time.Now().Add(role.DefaultTTL)
|
||||
|
||||
// Create the user
|
||||
username, password, err := db.CreateUser(role.Statements, req.DisplayName, expiration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := b.Secret(SecretCredsType).Response(map[string]interface{}{
|
||||
"username": username,
|
||||
"password": password,
|
||||
}, map[string]interface{}{
|
||||
"username": username,
|
||||
"role": name,
|
||||
})
|
||||
resp.Secret.TTL = role.DefaultTTL
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
const pathRoleCreateReadHelpSyn = `
|
||||
|
|
|
@ -14,7 +14,7 @@ func pathListRoles(b *databaseBackend) *framework.Path {
|
|||
Pattern: "roles/?$",
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ListOperation: b.pathRoleList,
|
||||
logical.ListOperation: b.pathRoleList(),
|
||||
},
|
||||
|
||||
HelpSynopsis: pathRoleHelpSyn,
|
||||
|
@ -35,12 +35,13 @@ func pathRoles(b *databaseBackend) *framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "Name of the database this role acts on.",
|
||||
},
|
||||
|
||||
"creation_statements": {
|
||||
Type: framework.TypeString,
|
||||
Description: "SQL string to create a user. See help for more info.",
|
||||
Type: framework.TypeString,
|
||||
Description: `Statements to be executed to create a user. Must be a semicolon-separated
|
||||
string, a base64-encoded semicolon-separated string, a serialized JSON string
|
||||
array, or a base64-encoded serialized JSON string array. The '{{name}}',
|
||||
'{{password}}', and '{{expiration}}' values will be substituted.`,
|
||||
},
|
||||
|
||||
"revocation_statements": {
|
||||
Type: framework.TypeString,
|
||||
Description: `Statements to be executed to revoke a user. Must be a semicolon-separated
|
||||
|
@ -75,9 +76,9 @@ func pathRoles(b *databaseBackend) *framework.Path {
|
|||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ReadOperation: b.pathRoleRead,
|
||||
logical.UpdateOperation: b.pathRoleCreate,
|
||||
logical.DeleteOperation: b.pathRoleDelete,
|
||||
logical.ReadOperation: b.pathRoleRead(),
|
||||
logical.UpdateOperation: b.pathRoleCreate(),
|
||||
logical.DeleteOperation: b.pathRoleDelete(),
|
||||
},
|
||||
|
||||
HelpSynopsis: pathRoleHelpSyn,
|
||||
|
@ -85,101 +86,107 @@ func pathRoles(b *databaseBackend) *framework.Path {
|
|||
}
|
||||
}
|
||||
|
||||
func (b *databaseBackend) pathRoleDelete(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
err := req.Storage.Delete("role/" + data.Get("name").(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (b *databaseBackend) pathRoleDelete() framework.OperationFunc {
|
||||
return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
err := req.Storage.Delete("role/" + data.Get("name").(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *databaseBackend) pathRoleRead(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
role, err := b.Role(req.Storage, data.Get("name").(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"creation_statements": role.Statements.CreationStatements,
|
||||
"revocation_statements": role.Statements.RevocationStatements,
|
||||
"rollback_statements": role.Statements.RollbackStatements,
|
||||
"renew_statements": role.Statements.RenewStatements,
|
||||
"default_ttl": role.DefaultTTL.String(),
|
||||
"max_ttl": role.MaxTTL.String(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *databaseBackend) pathRoleList(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
entries, err := req.Storage.List("role/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (b *databaseBackend) pathRoleRead() framework.OperationFunc {
|
||||
return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
role, err := b.Role(req.Storage, data.Get("name").(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return logical.ListResponse(entries), nil
|
||||
return &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"creation_statements": role.Statements.CreationStatements,
|
||||
"revocation_statements": role.Statements.RevocationStatements,
|
||||
"rollback_statements": role.Statements.RollbackStatements,
|
||||
"renew_statements": role.Statements.RenewStatements,
|
||||
"default_ttl": role.DefaultTTL.String(),
|
||||
"max_ttl": role.MaxTTL.String(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *databaseBackend) pathRoleCreate(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
name := data.Get("name").(string)
|
||||
if name == "" {
|
||||
return logical.ErrorResponse("Empty role name attribute given"), nil
|
||||
func (b *databaseBackend) pathRoleList() framework.OperationFunc {
|
||||
return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
entries, err := req.Storage.List("role/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logical.ListResponse(entries), nil
|
||||
}
|
||||
}
|
||||
|
||||
dbName := data.Get("db_name").(string)
|
||||
if dbName == "" {
|
||||
return logical.ErrorResponse("Empty database name attribute given"), nil
|
||||
func (b *databaseBackend) pathRoleCreate() framework.OperationFunc {
|
||||
return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
name := data.Get("name").(string)
|
||||
if name == "" {
|
||||
return logical.ErrorResponse("Empty role name attribute given"), nil
|
||||
}
|
||||
|
||||
dbName := data.Get("db_name").(string)
|
||||
if dbName == "" {
|
||||
return logical.ErrorResponse("Empty database name attribute given"), nil
|
||||
}
|
||||
|
||||
// Get statements
|
||||
creationStmts := data.Get("creation_statements").(string)
|
||||
revocationStmts := data.Get("revocation_statements").(string)
|
||||
rollbackStmts := data.Get("rollback_statements").(string)
|
||||
renewStmts := data.Get("renew_statements").(string)
|
||||
|
||||
// Get TTLs
|
||||
defaultTTLRaw := data.Get("default_ttl").(string)
|
||||
maxTTLRaw := data.Get("max_ttl").(string)
|
||||
|
||||
defaultTTL, err := time.ParseDuration(defaultTTLRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"Invalid default_ttl: %s", err)), nil
|
||||
}
|
||||
maxTTL, err := time.ParseDuration(maxTTLRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"Invalid max_ttl: %s", err)), nil
|
||||
}
|
||||
|
||||
statements := dbplugin.Statements{
|
||||
CreationStatements: creationStmts,
|
||||
RevocationStatements: revocationStmts,
|
||||
RollbackStatements: rollbackStmts,
|
||||
RenewStatements: renewStmts,
|
||||
}
|
||||
|
||||
// Store it
|
||||
entry, err := logical.StorageEntryJSON("role/"+name, &roleEntry{
|
||||
DBName: dbName,
|
||||
Statements: statements,
|
||||
DefaultTTL: defaultTTL,
|
||||
MaxTTL: maxTTL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := req.Storage.Put(entry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Get statements
|
||||
creationStmts := data.Get("creation_statements").(string)
|
||||
revocationStmts := data.Get("revocation_statements").(string)
|
||||
rollbackStmts := data.Get("rollback_statements").(string)
|
||||
renewStmts := data.Get("renew_statements").(string)
|
||||
|
||||
// Get TTLs
|
||||
defaultTTLRaw := data.Get("default_ttl").(string)
|
||||
maxTTLRaw := data.Get("max_ttl").(string)
|
||||
|
||||
defaultTTL, err := time.ParseDuration(defaultTTLRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"Invalid default_ttl: %s", err)), nil
|
||||
}
|
||||
maxTTL, err := time.ParseDuration(maxTTLRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"Invalid max_ttl: %s", err)), nil
|
||||
}
|
||||
|
||||
statements := dbplugin.Statements{
|
||||
CreationStatements: creationStmts,
|
||||
RevocationStatements: revocationStmts,
|
||||
RollbackStatements: rollbackStmts,
|
||||
RenewStatements: renewStmts,
|
||||
}
|
||||
|
||||
// TODO: Think about preparing the statments to test.
|
||||
|
||||
// Store it
|
||||
entry, err := logical.StorageEntryJSON("role/"+name, &roleEntry{
|
||||
DBName: dbName,
|
||||
Statements: statements,
|
||||
DefaultTTL: defaultTTL,
|
||||
MaxTTL: maxTTL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := req.Storage.Put(entry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type roleEntry struct {
|
||||
|
@ -196,10 +203,14 @@ Manage the roles that can be created with this backend.
|
|||
const pathRoleHelpDesc = `
|
||||
This path lets you manage the roles that can be created with this backend.
|
||||
|
||||
The "sql" parameter customizes the SQL string used to create the role.
|
||||
This can be a sequence of SQL queries. Some substitution will be done to the
|
||||
SQL string for certain keys. The names of the variables must be surrounded
|
||||
by "{{" and "}}" to be replaced.
|
||||
The "db_name" parameter is required and configures the name of the database
|
||||
connection to use.
|
||||
|
||||
The "creation_statements" parameter customizes the string used to create the
|
||||
credentials. This can be a sequence of SQL queries, or other statement formats
|
||||
for a particular database type. Some substitution will be done to the statement
|
||||
strings for certain keys. The names of the variables must be surrounded by "{{"
|
||||
and "}}" to be replaced.
|
||||
|
||||
* "name" - The random username generated for the DB user.
|
||||
|
||||
|
@ -207,7 +218,7 @@ by "{{" and "}}" to be replaced.
|
|||
|
||||
* "expiration" - The timestamp when this user will expire.
|
||||
|
||||
Example of a decent SQL query to use:
|
||||
Example of a decent creation_statements for a postgresql database plugin:
|
||||
|
||||
CREATE ROLE "{{name}}" WITH
|
||||
LOGIN
|
||||
|
@ -215,14 +226,17 @@ Example of a decent SQL query to use:
|
|||
VALID UNTIL '{{expiration}}';
|
||||
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "{{name}}";
|
||||
|
||||
Note the above user would be able to access everything in schema public.
|
||||
For more complex GRANT clauses, see the PostgreSQL manual.
|
||||
|
||||
The "revocation_sql" parameter customizes the SQL string used to revoke a user.
|
||||
Example of a decent revocation SQL query to use:
|
||||
The "revocation_statements" parameter customizes the statement string used to
|
||||
revoke a user. Example of a decent revocation_statements for a postgresql
|
||||
database plugin:
|
||||
|
||||
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM {{name}};
|
||||
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM {{name}};
|
||||
REVOKE USAGE ON SCHEMA public FROM {{name}};
|
||||
DROP ROLE IF EXISTS {{name}};
|
||||
|
||||
The "renew_statements" parameter customizes the statement string used to renew a
|
||||
user.
|
||||
The "rollback_statements' parameter customizes the statement string used to
|
||||
rollback a change if needed.
|
||||
`
|
||||
|
|
|
@ -14,112 +14,116 @@ func secretCreds(b *databaseBackend) *framework.Secret {
|
|||
Type: SecretCredsType,
|
||||
Fields: map[string]*framework.FieldSchema{},
|
||||
|
||||
Renew: b.secretCredsRenew,
|
||||
Revoke: b.secretCredsRevoke,
|
||||
Renew: b.secretCredsRenew(),
|
||||
Revoke: b.secretCredsRevoke(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *databaseBackend) secretCredsRenew(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
// Get the username from the internal data
|
||||
usernameRaw, ok := req.Secret.InternalData["username"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret is missing username internal data")
|
||||
}
|
||||
username, ok := usernameRaw.(string)
|
||||
|
||||
roleNameRaw, ok := req.Secret.InternalData["role"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not find role with name: %s", req.Secret.InternalData["role"])
|
||||
}
|
||||
|
||||
role, err := b.Role(req.Storage, roleNameRaw.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return nil, fmt.Errorf("could not find role with name: %s", req.Secret.InternalData["role"])
|
||||
}
|
||||
|
||||
f := framework.LeaseExtend(role.DefaultTTL, role.MaxTTL, b.System())
|
||||
resp, err := f(req, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Grab the read lock
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
// Get our connection
|
||||
db, err := b.getOrCreateDBObj(req.Storage, role.DBName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find connection with name %s, got err: %s", role.DBName, err)
|
||||
}
|
||||
|
||||
// Make sure we increase the VALID UNTIL endpoint for this user.
|
||||
if expireTime := resp.Secret.ExpirationTime(); !expireTime.IsZero() {
|
||||
err := db.RenewUser(role.Statements, username, expireTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (b *databaseBackend) secretCredsRenew() framework.OperationFunc {
|
||||
return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
// Get the username from the internal data
|
||||
usernameRaw, ok := req.Secret.InternalData["username"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret is missing username internal data")
|
||||
}
|
||||
}
|
||||
username, ok := usernameRaw.(string)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
roleNameRaw, ok := req.Secret.InternalData["role"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not find role with name: %s", req.Secret.InternalData["role"])
|
||||
}
|
||||
|
||||
func (b *databaseBackend) secretCredsRevoke(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
// Get the username from the internal data
|
||||
usernameRaw, ok := req.Secret.InternalData["username"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret is missing username internal data")
|
||||
}
|
||||
username, ok := usernameRaw.(string)
|
||||
|
||||
var resp *logical.Response
|
||||
|
||||
roleNameRaw, ok := req.Secret.InternalData["role"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no role name was provided")
|
||||
}
|
||||
|
||||
role, err := b.Role(req.Storage, roleNameRaw.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return nil, fmt.Errorf("could not find role with name: %s", req.Secret.InternalData["role"])
|
||||
}
|
||||
|
||||
/* TODO: think about how to handle this case.
|
||||
if !ok {
|
||||
role, err := b.Role(req.Storage, roleNameRaw.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
if resp == nil {
|
||||
resp = &logical.Response{}
|
||||
}
|
||||
resp.AddWarning(fmt.Sprintf("Role %q cannot be found. Using default revocation SQL.", roleNameRaw.(string)))
|
||||
} else {
|
||||
revocationSQL = role.RevocationStatement
|
||||
return nil, fmt.Errorf("could not find role with name: %s", req.Secret.InternalData["role"])
|
||||
}
|
||||
}*/
|
||||
|
||||
// Grab the read lock
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
f := framework.LeaseExtend(role.DefaultTTL, role.MaxTTL, b.System())
|
||||
resp, err := f(req, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get our connection
|
||||
db, err := b.getOrCreateDBObj(req.Storage, role.DBName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find database with name: %s, got error: %s", role.DBName, err)
|
||||
// Grab the read lock
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
// Get our connection
|
||||
db, err := b.getOrCreateDBObj(req.Storage, role.DBName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find connection with name %s, got err: %s", role.DBName, err)
|
||||
}
|
||||
|
||||
// Make sure we increase the VALID UNTIL endpoint for this user.
|
||||
if expireTime := resp.Secret.ExpirationTime(); !expireTime.IsZero() {
|
||||
err := db.RenewUser(role.Statements, username, expireTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *databaseBackend) secretCredsRevoke() framework.OperationFunc {
|
||||
return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
// Get the username from the internal data
|
||||
usernameRaw, ok := req.Secret.InternalData["username"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret is missing username internal data")
|
||||
}
|
||||
username, ok := usernameRaw.(string)
|
||||
|
||||
var resp *logical.Response
|
||||
|
||||
roleNameRaw, ok := req.Secret.InternalData["role"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no role name was provided")
|
||||
}
|
||||
|
||||
role, err := b.Role(req.Storage, roleNameRaw.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return nil, fmt.Errorf("could not find role with name: %s", req.Secret.InternalData["role"])
|
||||
}
|
||||
|
||||
/* TODO: think about how to handle this case.
|
||||
if !ok {
|
||||
role, err := b.Role(req.Storage, roleNameRaw.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
if resp == nil {
|
||||
resp = &logical.Response{}
|
||||
}
|
||||
resp.AddWarning(fmt.Sprintf("Role %q cannot be found. Using default revocation SQL.", roleNameRaw.(string)))
|
||||
} else {
|
||||
revocationSQL = role.RevocationStatement
|
||||
}
|
||||
}*/
|
||||
|
||||
// Grab the read lock
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
// Get our connection
|
||||
db, err := b.getOrCreateDBObj(req.Storage, role.DBName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find database with name: %s, got error: %s", role.DBName, err)
|
||||
}
|
||||
|
||||
err = db.RevokeUser(role.Statements, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
err = db.RevokeUser(role.Statements, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
|
|
@ -88,11 +88,11 @@ func (d StaticSystemView) ReplicationState() consts.ReplicationState {
|
|||
}
|
||||
|
||||
func (d StaticSystemView) ResponseWrapData(data map[string]interface{}, ttl time.Duration, jwt bool) (string, error) {
|
||||
return "", errors.New("ResponseWrapData is not implimented in StaticSystemView")
|
||||
return "", errors.New("ResponseWrapData is not implemented in StaticSystemView")
|
||||
}
|
||||
|
||||
func (d StaticSystemView) LookupPlugin(name string) (*pluginutil.PluginRunner, error) {
|
||||
return nil, errors.New("LookupPlugin is not implimented in StaticSystemView")
|
||||
return nil, errors.New("LookupPlugin is not implemented in StaticSystemView")
|
||||
}
|
||||
|
||||
func (d StaticSystemView) MlockDisabled() bool {
|
||||
|
|
Loading…
Reference in New Issue