Update help text and comments

This commit is contained in:
Brian Kassouf 2017-04-11 11:50:34 -07:00
parent c85b7be22f
commit 128f25c13d
9 changed files with 323 additions and 271 deletions

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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.
`

View File

@ -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 = `

View File

@ -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.
`

View File

@ -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
}

View File

@ -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 {