package dbplugin import ( "errors" "net/rpc" "time" "github.com/hashicorp/go-plugin" "github.com/hashicorp/vault/helper/pluginutil" log "github.com/mgutz/logxi/v1" ) var ( ErrEmptyPluginName = errors.New("empty plugin name") ) // DatabaseType is the interface that all database objects must implement. type DatabaseType interface { Type() string CreateUser(statements Statements, username, password, expiration string) error RenewUser(statements Statements, username, expiration string) error RevokeUser(statements Statements, username string) error Initialize(map[string]interface{}) error Close() error GenerateUsername(displayName string) (string, error) GeneratePassword() (string, error) GenerateExpiration(ttl time.Duration) (string, error) } // Statements set in role creation and passed into the database type's functions. // TODO: Add a way of setting defaults here. type Statements struct { CreationStatements string `json:"creation_statments" mapstructure:"creation_statements" structs:"creation_statments"` RevocationStatements string `json:"revocation_statements" mapstructure:"revocation_statements" structs:"revocation_statements"` RollbackStatements string `json:"rollback_statements" mapstructure:"rollback_statements" structs:"rollback_statements"` RenewStatements string `json:"renew_statements" mapstructure:"renew_statements" structs:"renew_statements"` } // PluginFactory is used to build plugin database types. It wraps the database // object in a logging and metrics middleware. func PluginFactory(pluginName string, sys pluginutil.LookWrapper, logger log.Logger) (DatabaseType, error) { if pluginName == "" { return nil, ErrEmptyPluginName } pluginMeta, err := sys.LookupPlugin(pluginName) if err != nil { return nil, err } db, err := newPluginClient(sys, pluginMeta) if err != nil { return nil, err } // Wrap with metrics middleware db = &databaseMetricsMiddleware{ next: db, typeStr: db.Type(), } // Wrap with tracing middleware db = &databaseTracingMiddleware{ next: db, typeStr: db.Type(), logger: logger, } return db, nil } // handshakeConfigs are used to just do a basic handshake between // a plugin and host. If the handshake fails, a user friendly error is shown. // This prevents users from executing bad plugins or executing a plugin // directory. It is a UX feature, not a security feature. var handshakeConfig = plugin.HandshakeConfig{ ProtocolVersion: 1, MagicCookieKey: "VAULT_DATABASE_PLUGIN", MagicCookieValue: "926a0820-aea2-be28-51d6-83cdf00e8edb", } type DatabasePlugin struct { impl DatabaseType } func (d DatabasePlugin) Server(*plugin.MuxBroker) (interface{}, error) { return &databasePluginRPCServer{impl: d.impl}, nil } func (DatabasePlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { return &databasePluginRPCClient{client: c}, nil } // ---- RPC Request Args Domain ---- type CreateUserRequest struct { Statements Statements Username string Password string Expiration string } type RenewUserRequest struct { Statements Statements Username string Expiration string } type RevokeUserRequest struct { Statements Statements Username string } // ---- RPC Response Args Domain ---- type GenerateUsernameResponse struct { Username string } type GenerateExpirationResponse struct { Expiration string } type GeneratePasswordResponse struct { Password string }