From 62d59e5f4e496b34436de3537200073a518c4475 Mon Sep 17 00:00:00 2001 From: Brian Kassouf Date: Thu, 6 Apr 2017 12:20:10 -0700 Subject: [PATCH] Move plugin code into sub directory --- builtin/logical/database/backend.go | 39 +-- builtin/logical/database/dbplugin/client.go | 148 ++++++++ .../{ => dbplugin}/databasemiddleware.go | 2 +- builtin/logical/database/dbplugin/plugin.go | 126 +++++++ .../database/{ => dbplugin}/plugin_test.go | 2 +- builtin/logical/database/dbplugin/server.go | 90 +++++ .../database/path_config_connection.go | 3 +- builtin/logical/database/path_roles.go | 11 +- builtin/logical/database/plugin.go | 324 ------------------ helper/pluginutil/runner.go | 5 + 10 files changed, 385 insertions(+), 365 deletions(-) create mode 100644 builtin/logical/database/dbplugin/client.go rename builtin/logical/database/{ => dbplugin}/databasemiddleware.go (99%) create mode 100644 builtin/logical/database/dbplugin/plugin.go rename builtin/logical/database/{ => dbplugin}/plugin_test.go (99%) create mode 100644 builtin/logical/database/dbplugin/server.go delete mode 100644 builtin/logical/database/plugin.go diff --git a/builtin/logical/database/backend.go b/builtin/logical/database/backend.go index a2fff4ba8..baa05a092 100644 --- a/builtin/logical/database/backend.go +++ b/builtin/logical/database/backend.go @@ -4,50 +4,23 @@ import ( "fmt" "strings" "sync" - "time" log "github.com/mgutz/logxi/v1" + "github.com/hashicorp/vault/builtin/logical/database/dbplugin" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) const databaseConfigPath = "database/dbs/" -// 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) -} - // DatabaseConfig is used by the Factory function to configure a DatabaseType // object. type DatabaseConfig struct { PluginName string `json:"plugin_name" structs:"plugin_name" mapstructure:"plugin_name"` // ConnectionDetails stores the database specific connection settings needed // by each database type. - ConnectionDetails map[string]interface{} `json:"connection_details" structs:"connection_details" mapstructure:"connection_details"` - MaxOpenConnections int `json:"max_open_connections" structs:"max_open_connections" mapstructure:"max_open_connections"` - MaxIdleConnections int `json:"max_idle_connections" structs:"max_idle_connections" mapstructure:"max_idle_connections"` - MaxConnectionLifetime time.Duration `json:"max_connection_lifetime" structs:"max_connection_lifetime" mapstructure:"max_connection_lifetime"` -} - -// 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"` + ConnectionDetails map[string]interface{} `json:"connection_details" structs:"connection_details" mapstructure:"connection_details"` } func Factory(conf *logical.BackendConfig) (logical.Backend, error) { @@ -83,12 +56,12 @@ func Backend(conf *logical.BackendConfig) *databaseBackend { } b.logger = conf.Logger - b.connections = make(map[string]DatabaseType) + b.connections = make(map[string]dbplugin.DatabaseType) return &b } type databaseBackend struct { - connections map[string]DatabaseType + connections map[string]dbplugin.DatabaseType logger log.Logger *framework.Backend @@ -108,7 +81,7 @@ func (b *databaseBackend) closeAllDBs() { // This function is used to retrieve a database object either from the cached // connection map or by using the database config in storage. The caller of this // function needs to hold the backend's lock. -func (b *databaseBackend) getOrCreateDBObj(s logical.Storage, name string) (DatabaseType, error) { +func (b *databaseBackend) getOrCreateDBObj(s logical.Storage, name string) (dbplugin.DatabaseType, error) { // if the object already is built and cached, return it db, ok := b.connections[name] if ok { @@ -128,7 +101,7 @@ func (b *databaseBackend) getOrCreateDBObj(s logical.Storage, name string) (Data return nil, err } - db, err = PluginFactory(&config, b.System(), b.logger) + db, err = dbplugin.PluginFactory(config.PluginName, b.System(), b.logger) if err != nil { return nil, err } diff --git a/builtin/logical/database/dbplugin/client.go b/builtin/logical/database/dbplugin/client.go new file mode 100644 index 000000000..db6b3d1fd --- /dev/null +++ b/builtin/logical/database/dbplugin/client.go @@ -0,0 +1,148 @@ +package dbplugin + +import ( + "fmt" + "net/rpc" + "sync" + "time" + + "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/helper/pluginutil" +) + +// DatabasePluginClient embeds a databasePluginRPCClient and wraps it's close +// method to also call Kill() on the plugin.Client. +type DatabasePluginClient struct { + client *plugin.Client + sync.Mutex + + *databasePluginRPCClient +} + +func (dc *DatabasePluginClient) Close() error { + err := dc.databasePluginRPCClient.Close() + dc.client.Kill() + + return err +} + +// newPluginClient returns a databaseRPCClient with a connection to a running +// plugin. The client is wrapped in a DatabasePluginClient object to ensure the +// plugin is killed on call of Close(). +func newPluginClient(sys pluginutil.Wrapper, pluginRunner *pluginutil.PluginRunner) (DatabaseType, error) { + // pluginMap is the map of plugins we can dispense. + var pluginMap = map[string]plugin.Plugin{ + "database": new(DatabasePlugin), + } + + client, err := pluginRunner.Run(sys, pluginMap, handshakeConfig, []string{}) + if err != nil { + return nil, err + } + + // Connect via RPC + rpcClient, err := client.Client() + if err != nil { + return nil, err + } + + // Request the plugin + raw, err := rpcClient.Dispense("database") + if err != nil { + return nil, err + } + + // We should have a Greeter now! This feels like a normal interface + // implementation but is in fact over an RPC connection. + databaseRPC := raw.(*databasePluginRPCClient) + + return &DatabasePluginClient{ + client: client, + databasePluginRPCClient: databaseRPC, + }, nil +} + +// ---- RPC client domain ---- + +// databasePluginRPCClient impliments DatabaseType and is used on the client to +// make RPC calls to a plugin. +type databasePluginRPCClient struct { + client *rpc.Client +} + +func (dr *databasePluginRPCClient) Type() string { + var dbType string + //TODO: catch error + dr.client.Call("Plugin.Type", struct{}{}, &dbType) + + return fmt.Sprintf("plugin-%s", dbType) +} + +func (dr *databasePluginRPCClient) CreateUser(statements Statements, username, password, expiration string) error { + req := CreateUserRequest{ + Statements: statements, + Username: username, + Password: password, + Expiration: expiration, + } + + err := dr.client.Call("Plugin.CreateUser", req, &struct{}{}) + + return err +} + +func (dr *databasePluginRPCClient) RenewUser(statements Statements, username, expiration string) error { + req := RenewUserRequest{ + Statements: statements, + Username: username, + Expiration: expiration, + } + + err := dr.client.Call("Plugin.RenewUser", req, &struct{}{}) + + return err +} + +func (dr *databasePluginRPCClient) RevokeUser(statements Statements, username string) error { + req := RevokeUserRequest{ + Statements: statements, + Username: username, + } + + err := dr.client.Call("Plugin.RevokeUser", req, &struct{}{}) + + return err +} + +func (dr *databasePluginRPCClient) Initialize(conf map[string]interface{}) error { + err := dr.client.Call("Plugin.Initialize", conf, &struct{}{}) + + return err +} + +func (dr *databasePluginRPCClient) Close() error { + err := dr.client.Call("Plugin.Close", struct{}{}, &struct{}{}) + + return err +} + +func (dr *databasePluginRPCClient) GenerateUsername(displayName string) (string, error) { + resp := &GenerateUsernameResponse{} + err := dr.client.Call("Plugin.GenerateUsername", displayName, resp) + + return resp.Username, err +} + +func (dr *databasePluginRPCClient) GeneratePassword() (string, error) { + resp := &GeneratePasswordResponse{} + err := dr.client.Call("Plugin.GeneratePassword", struct{}{}, resp) + + return resp.Password, err +} + +func (dr *databasePluginRPCClient) GenerateExpiration(duration time.Duration) (string, error) { + resp := &GenerateExpirationResponse{} + err := dr.client.Call("Plugin.GenerateExpiration", duration, resp) + + return resp.Expiration, err +} diff --git a/builtin/logical/database/databasemiddleware.go b/builtin/logical/database/dbplugin/databasemiddleware.go similarity index 99% rename from builtin/logical/database/databasemiddleware.go rename to builtin/logical/database/dbplugin/databasemiddleware.go index 5892e8064..b4a980950 100644 --- a/builtin/logical/database/databasemiddleware.go +++ b/builtin/logical/database/dbplugin/databasemiddleware.go @@ -1,4 +1,4 @@ -package database +package dbplugin import ( "time" diff --git a/builtin/logical/database/dbplugin/plugin.go b/builtin/logical/database/dbplugin/plugin.go new file mode 100644 index 000000000..994f3b0ce --- /dev/null +++ b/builtin/logical/database/dbplugin/plugin.go @@ -0,0 +1,126 @@ +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 +} diff --git a/builtin/logical/database/plugin_test.go b/builtin/logical/database/dbplugin/plugin_test.go similarity index 99% rename from builtin/logical/database/plugin_test.go rename to builtin/logical/database/dbplugin/plugin_test.go index 2ec01c955..849e1ebbf 100644 --- a/builtin/logical/database/plugin_test.go +++ b/builtin/logical/database/dbplugin/plugin_test.go @@ -1,4 +1,4 @@ -package database +package dbplugin import ( "crypto/sha256" diff --git a/builtin/logical/database/dbplugin/server.go b/builtin/logical/database/dbplugin/server.go new file mode 100644 index 000000000..018d9b8db --- /dev/null +++ b/builtin/logical/database/dbplugin/server.go @@ -0,0 +1,90 @@ +package dbplugin + +import ( + "time" + + "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/helper/pluginutil" +) + +// NewPluginServer is called from within a plugin and wraps the provided +// DatabaseType implimentation in a databasePluginRPCServer object and starts a +// RPC server. +func NewPluginServer(db DatabaseType) { + dbPlugin := &DatabasePlugin{ + impl: db, + } + + // pluginMap is the map of plugins we can dispense. + var pluginMap = map[string]plugin.Plugin{ + "database": dbPlugin, + } + + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: handshakeConfig, + Plugins: pluginMap, + TLSProvider: pluginutil.VaultPluginTLSProvider, + }) +} + +// ---- RPC server domain ---- + +// databasePluginRPCServer impliments DatabaseType and is run inside a plugin +type databasePluginRPCServer struct { + impl DatabaseType +} + +func (ds *databasePluginRPCServer) Type(_ struct{}, resp *string) error { + *resp = ds.impl.Type() + return nil +} + +func (ds *databasePluginRPCServer) CreateUser(args *CreateUserRequest, _ *struct{}) error { + err := ds.impl.CreateUser(args.Statements, args.Username, args.Password, args.Expiration) + + return err +} + +func (ds *databasePluginRPCServer) RenewUser(args *RenewUserRequest, _ *struct{}) error { + err := ds.impl.RenewUser(args.Statements, args.Username, args.Expiration) + + return err +} + +func (ds *databasePluginRPCServer) RevokeUser(args *RevokeUserRequest, _ *struct{}) error { + err := ds.impl.RevokeUser(args.Statements, args.Username) + + return err +} + +func (ds *databasePluginRPCServer) Initialize(args map[string]interface{}, _ *struct{}) error { + err := ds.impl.Initialize(args) + + return err +} + +func (ds *databasePluginRPCServer) Close(_ struct{}, _ *struct{}) error { + ds.impl.Close() + return nil +} + +func (ds *databasePluginRPCServer) GenerateUsername(args string, resp *GenerateUsernameResponse) error { + var err error + resp.Username, err = ds.impl.GenerateUsername(args) + + return err +} + +func (ds *databasePluginRPCServer) GeneratePassword(_ struct{}, resp *GeneratePasswordResponse) error { + var err error + resp.Password, err = ds.impl.GeneratePassword() + + return err +} + +func (ds *databasePluginRPCServer) GenerateExpiration(args time.Duration, resp *GenerateExpirationResponse) error { + var err error + resp.Expiration, err = ds.impl.GenerateExpiration(args) + + return err +} diff --git a/builtin/logical/database/path_config_connection.go b/builtin/logical/database/path_config_connection.go index 48d9b8880..4af6e70a0 100644 --- a/builtin/logical/database/path_config_connection.go +++ b/builtin/logical/database/path_config_connection.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/fatih/structs" + "github.com/hashicorp/vault/builtin/logical/database/dbplugin" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) @@ -163,7 +164,7 @@ func (b *databaseBackend) connectionWriteHandler() framework.OperationFunc { b.Lock() defer b.Unlock() - db, err := PluginFactory(config, b.System(), b.logger) + db, err := dbplugin.PluginFactory(config.PluginName, b.System(), b.logger) if err != nil { return logical.ErrorResponse(fmt.Sprintf("Error creating database object: %s", err)), nil } diff --git a/builtin/logical/database/path_roles.go b/builtin/logical/database/path_roles.go index d099ef178..b3ef6f753 100644 --- a/builtin/logical/database/path_roles.go +++ b/builtin/logical/database/path_roles.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/hashicorp/vault/builtin/logical/database/dbplugin" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) @@ -155,7 +156,7 @@ func (b *databaseBackend) pathRoleCreate(req *logical.Request, data *framework.F "Invalid max_ttl: %s", err)), nil } - statements := Statements{ + statements := dbplugin.Statements{ CreationStatements: creationStmts, RevocationStatements: revocationStmts, RollbackStatements: rollbackStmts, @@ -182,10 +183,10 @@ func (b *databaseBackend) pathRoleCreate(req *logical.Request, data *framework.F } type roleEntry struct { - DBName string `json:"db_name" mapstructure:"db_name" structs:"db_name"` - Statements Statements `json:"statments" mapstructure:"statements" structs:"statments"` - DefaultTTL time.Duration `json:"default_ttl" mapstructure:"default_ttl" structs:"default_ttl"` - MaxTTL time.Duration `json:"max_ttl" mapstructure:"max_ttl" structs:"max_ttl"` + DBName string `json:"db_name" mapstructure:"db_name" structs:"db_name"` + Statements dbplugin.Statements `json:"statments" mapstructure:"statements" structs:"statments"` + DefaultTTL time.Duration `json:"default_ttl" mapstructure:"default_ttl" structs:"default_ttl"` + MaxTTL time.Duration `json:"max_ttl" mapstructure:"max_ttl" structs:"max_ttl"` } const pathRoleHelpSyn = ` diff --git a/builtin/logical/database/plugin.go b/builtin/logical/database/plugin.go deleted file mode 100644 index 5a6a8e328..000000000 --- a/builtin/logical/database/plugin.go +++ /dev/null @@ -1,324 +0,0 @@ -package database - -import ( - "errors" - "fmt" - "net/rpc" - "sync" - "time" - - "github.com/hashicorp/go-plugin" - "github.com/hashicorp/vault/helper/pluginutil" - "github.com/hashicorp/vault/logical" - log "github.com/mgutz/logxi/v1" -) - -var ( - ErrEmptyPluginName = errors.New("empty plugin name") -) - -// PluginFactory is used to build plugin database types. It wraps the database -// object in a logging and metrics middleware. -func PluginFactory(conf *DatabaseConfig, sys logical.SystemView, logger log.Logger) (DatabaseType, error) { - if conf.PluginName == "" { - return nil, ErrEmptyPluginName - } - - pluginMeta, err := sys.LookupPlugin(conf.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 -} - -// DatabasePluginClient embeds a databasePluginRPCClient and wraps it's close -// method to also call Kill() on the plugin.Client. -type DatabasePluginClient struct { - client *plugin.Client - sync.Mutex - - *databasePluginRPCClient -} - -func (dc *DatabasePluginClient) Close() error { - err := dc.databasePluginRPCClient.Close() - dc.client.Kill() - - return err -} - -// newPluginClient returns a databaseRPCClient with a connection to a running -// plugin. The client is wrapped in a DatabasePluginClient object to ensure the -// plugin is killed on call of Close(). -func newPluginClient(sys pluginutil.Wrapper, pluginRunner *pluginutil.PluginRunner) (DatabaseType, error) { - // pluginMap is the map of plugins we can dispense. - var pluginMap = map[string]plugin.Plugin{ - "database": new(DatabasePlugin), - } - - client, err := pluginRunner.Run(sys, pluginMap, handshakeConfig, []string{}) - if err != nil { - return nil, err - } - - // Connect via RPC - rpcClient, err := client.Client() - if err != nil { - return nil, err - } - - // Request the plugin - raw, err := rpcClient.Dispense("database") - if err != nil { - return nil, err - } - - // We should have a Greeter now! This feels like a normal interface - // implementation but is in fact over an RPC connection. - databaseRPC := raw.(*databasePluginRPCClient) - - return &DatabasePluginClient{ - client: client, - databasePluginRPCClient: databaseRPC, - }, nil -} - -// NewPluginServer is called from within a plugin and wraps the provided -// DatabaseType implimentation in a databasePluginRPCServer object and starts a -// RPC server. -func NewPluginServer(db DatabaseType) { - dbPlugin := &DatabasePlugin{ - impl: db, - } - - // pluginMap is the map of plugins we can dispense. - var pluginMap = map[string]plugin.Plugin{ - "database": dbPlugin, - } - - plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: handshakeConfig, - Plugins: pluginMap, - TLSProvider: pluginutil.VaultPluginTLSProvider, - }) -} - -// ---- RPC client domain ---- - -// databasePluginRPCClient impliments DatabaseType and is used on the client to -// make RPC calls to a plugin. -type databasePluginRPCClient struct { - client *rpc.Client -} - -func (dr *databasePluginRPCClient) Type() string { - var dbType string - //TODO: catch error - dr.client.Call("Plugin.Type", struct{}{}, &dbType) - - return fmt.Sprintf("plugin-%s", dbType) -} - -func (dr *databasePluginRPCClient) CreateUser(statements Statements, username, password, expiration string) error { - req := CreateUserRequest{ - Statements: statements, - Username: username, - Password: password, - Expiration: expiration, - } - - err := dr.client.Call("Plugin.CreateUser", req, &struct{}{}) - - return err -} - -func (dr *databasePluginRPCClient) RenewUser(statements Statements, username, expiration string) error { - req := RenewUserRequest{ - Statements: statements, - Username: username, - Expiration: expiration, - } - - err := dr.client.Call("Plugin.RenewUser", req, &struct{}{}) - - return err -} - -func (dr *databasePluginRPCClient) RevokeUser(statements Statements, username string) error { - req := RevokeUserRequest{ - Statements: statements, - Username: username, - } - - err := dr.client.Call("Plugin.RevokeUser", req, &struct{}{}) - - return err -} - -func (dr *databasePluginRPCClient) Initialize(conf map[string]interface{}) error { - err := dr.client.Call("Plugin.Initialize", conf, &struct{}{}) - - return err -} - -func (dr *databasePluginRPCClient) Close() error { - err := dr.client.Call("Plugin.Close", struct{}{}, &struct{}{}) - - return err -} - -func (dr *databasePluginRPCClient) GenerateUsername(displayName string) (string, error) { - resp := &GenerateUsernameResponse{} - err := dr.client.Call("Plugin.GenerateUsername", displayName, resp) - - return resp.Username, err -} - -func (dr *databasePluginRPCClient) GeneratePassword() (string, error) { - resp := &GeneratePasswordResponse{} - err := dr.client.Call("Plugin.GeneratePassword", struct{}{}, resp) - - return resp.Password, err -} - -func (dr *databasePluginRPCClient) GenerateExpiration(duration time.Duration) (string, error) { - resp := &GenerateExpirationResponse{} - err := dr.client.Call("Plugin.GenerateExpiration", duration, resp) - - return resp.Expiration, err -} - -// ---- RPC server domain ---- - -// databasePluginRPCServer impliments DatabaseType and is run inside a plugin -type databasePluginRPCServer struct { - impl DatabaseType -} - -func (ds *databasePluginRPCServer) Type(_ struct{}, resp *string) error { - *resp = ds.impl.Type() - return nil -} - -func (ds *databasePluginRPCServer) CreateUser(args *CreateUserRequest, _ *struct{}) error { - err := ds.impl.CreateUser(args.Statements, args.Username, args.Password, args.Expiration) - - return err -} - -func (ds *databasePluginRPCServer) RenewUser(args *RenewUserRequest, _ *struct{}) error { - err := ds.impl.RenewUser(args.Statements, args.Username, args.Expiration) - - return err -} - -func (ds *databasePluginRPCServer) RevokeUser(args *RevokeUserRequest, _ *struct{}) error { - err := ds.impl.RevokeUser(args.Statements, args.Username) - - return err -} - -func (ds *databasePluginRPCServer) Initialize(args map[string]interface{}, _ *struct{}) error { - err := ds.impl.Initialize(args) - - return err -} - -func (ds *databasePluginRPCServer) Close(_ struct{}, _ *struct{}) error { - ds.impl.Close() - return nil -} - -func (ds *databasePluginRPCServer) GenerateUsername(args string, resp *GenerateUsernameResponse) error { - var err error - resp.Username, err = ds.impl.GenerateUsername(args) - - return err -} - -func (ds *databasePluginRPCServer) GeneratePassword(_ struct{}, resp *GeneratePasswordResponse) error { - var err error - resp.Password, err = ds.impl.GeneratePassword() - - return err -} - -func (ds *databasePluginRPCServer) GenerateExpiration(args time.Duration, resp *GenerateExpirationResponse) error { - var err error - resp.Expiration, err = ds.impl.GenerateExpiration(args) - - return err -} - -// ---- 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 -} - -// ---- Response Args Domain ---- - -type GenerateUsernameResponse struct { - Username string -} -type GenerateExpirationResponse struct { - Expiration string -} -type GeneratePasswordResponse struct { - Password string -} diff --git a/helper/pluginutil/runner.go b/helper/pluginutil/runner.go index 143a4c839..90569dd9a 100644 --- a/helper/pluginutil/runner.go +++ b/helper/pluginutil/runner.go @@ -12,6 +12,11 @@ type Looker interface { LookupPlugin(string) (*PluginRunner, error) } +type LookWrapper interface { + Looker + Wrapper +} + type PluginRunner struct { Name string `json:"name"` Command string `json:"command"`