From 51609397f62dfb0d14a9cb9f09003cb8f0d86af1 Mon Sep 17 00:00:00 2001 From: Hugo Gonzalez Labrador Date: Thu, 31 Jan 2019 15:07:21 +0100 Subject: [PATCH 1/2] add broker discovery --- cmd/reva/broker-discover.go | 39 ++++++++ cmd/reva/broker-find.go | 2 +- cmd/reva/main.go | 1 + cmd/revad/httpsvr/httpsvr.go | 3 +- pkg/eosclient/eosclient.go | 84 ++++++++-------- pkg/storage/broker/static/static.go | 16 +++- pkg/storage/eos/eos.go | 96 +++++++++++-------- pkg/storage/local/local.go | 2 +- pkg/storage/storage.go | 4 +- .../storagebrokersvc/storagebrokersvc.go | 34 ++++++- services/httpsvc/handlers/auth.go | 73 ++++++++++++++ services/httpsvc/handlers/handlers.go | 4 + services/httpsvc/handlers/trace.go | 5 - 13 files changed, 265 insertions(+), 98 deletions(-) create mode 100644 cmd/reva/broker-discover.go create mode 100644 services/httpsvc/handlers/auth.go diff --git a/cmd/reva/broker-discover.go b/cmd/reva/broker-discover.go new file mode 100644 index 0000000000..ea8a73a1c5 --- /dev/null +++ b/cmd/reva/broker-discover.go @@ -0,0 +1,39 @@ +package main + +import ( + "context" + "fmt" + + rpcpb "github.com/cernbox/go-cs3apis/cs3/rpc" + storagebrokerv0alphapb "github.com/cernbox/go-cs3apis/cs3/storagebroker/v0alpha" +) + +func brokerDiscoverCommand() *command { + cmd := newCommand("broker-discover") + cmd.Description = func() string { + return "returns a list of all available storage providers known by the broker" + } + cmd.Action = func() error { + req := &storagebrokerv0alphapb.DiscoverRequest{} + client, err := getStorageBrokerClient() + if err != nil { + return err + } + ctx := context.Background() + res, err := client.Discover(ctx, req) + if err != nil { + return err + } + + if res.Status.Code != rpcpb.Code_CODE_OK { + return formatError(res.Status) + } + + providers := res.StorageProviders + for _, p := range providers { + fmt.Printf("%s => %s\n", p.MountPath, p.Endpoint) + } + return nil + } + return cmd +} diff --git a/cmd/reva/broker-find.go b/cmd/reva/broker-find.go index 31fc8a267e..2b34759a8f 100644 --- a/cmd/reva/broker-find.go +++ b/cmd/reva/broker-find.go @@ -36,7 +36,7 @@ func brokerFindCommand() *command { return formatError(res.Status) } - fmt.Printf("resource can be found at %s\n", res.ProviderInfo.Location) + fmt.Printf("resource can be found at %s\n", res.StorageProvider.Endpoint) return nil } return cmd diff --git a/cmd/reva/main.go b/cmd/reva/main.go index 917e1394d3..a017ed58e5 100644 --- a/cmd/reva/main.go +++ b/cmd/reva/main.go @@ -24,6 +24,7 @@ func main() { moveCommand(), mkdirCommand(), brokerFindCommand(), + brokerDiscoverCommand(), appRegistryFindCommand(), appProviderGetIFrameCommand(), } diff --git a/cmd/revad/httpsvr/httpsvr.go b/cmd/revad/httpsvr/httpsvr.go index f31f2f3b89..1dab074611 100644 --- a/cmd/revad/httpsvr/httpsvr.go +++ b/cmd/revad/httpsvr/httpsvr.go @@ -35,6 +35,7 @@ type config struct { OCDAVSvc map[string]interface{} `mapstructure:"ocdav_svc"` PromSvc map[string]interface{} `mapstructure:"prometheus_svc"` IFrameUISvc map[string]interface{} `mapstructure:"iframe_ui_svc"` + Auth map[string]interface{} `mapstructure:"auth"` } // Server contains the server info. @@ -140,5 +141,5 @@ func (s *Server) getHandler() http.Handler { } w.WriteHeader(http.StatusNotFound) }) - return handlers.TraceHandler(handlers.LogHandler(logger, h)) + return handlers.TraceHandler(handlers.LogHandler(logger, handlers.AuthHandler(h))) } diff --git a/pkg/eosclient/eosclient.go b/pkg/eosclient/eosclient.go index 71bccbd969..2c131c8089 100644 --- a/pkg/eosclient/eosclient.go +++ b/pkg/eosclient/eosclient.go @@ -16,8 +16,8 @@ import ( "github.com/cernbox/reva/pkg/log" + "github.com/cernbox/reva/pkg/err" "github.com/gofrs/uuid" - "github.com/pkg/errors" ) const ( @@ -56,6 +56,8 @@ const ( var ( errInvalidACL = errors.New("invalid acl") + logger = log.New("eosclient") + errors = err.New("eosclient") ) // ACL represents an EOS ACL. @@ -67,6 +69,15 @@ type ACL struct { // Options to configure the Client. type Options struct { + + // ForceSingleUserMode forces all connections to use only one user. + // This is the case when access to EOS is done from FUSE under apache or www-data. + ForceSingleUserMode bool + + // SingleUsername is the username to use when connecting to EOS. + // Defaults to apache + SingleUsername string + // Location of the eos binary. // Default is /usr/bin/eos. EosBinary string @@ -82,15 +93,13 @@ type Options struct { // Location on the local fs where to store reads. // Defaults to os.TempDir() CacheDirectory string - - // Writter to write logs to - LogOutput io.Writer - - // Key to get the trace Id from. - TraceKey interface{} } func (opt *Options) init() { + if opt.ForceSingleUserMode && opt.SingleUsername != "" { + opt.SingleUsername = "apache" + } + if opt.EosBinary == "" { opt.EosBinary = "/usr/bin/eos" } @@ -106,21 +115,12 @@ func (opt *Options) init() { if opt.CacheDirectory == "" { opt.CacheDirectory = os.TempDir() } - - if opt.LogOutput == nil { - opt.LogOutput = ioutil.Discard - } - - if opt.TraceKey == nil { - opt.TraceKey = "traceid" - } } // Client performs actions against a EOS management node (MGM). // It requires the eos-client and xrootd-client packages installed to work. type Client struct { - opt *Options - logger *log.Logger + opt *Options } // New creates a new client with the given options. @@ -128,11 +128,13 @@ func New(opt *Options) *Client { opt.init() c := new(Client) c.opt = opt - c.logger = log.New("eosclient") return c } -func getUnixUser(username string) (*gouser.User, error) { +func (c *Client) getUnixUser(username string) (*gouser.User, error) { + if c.opt.ForceSingleUserMode { + username = c.opt.SingleUsername + } return gouser.Lookup(username) } @@ -170,11 +172,12 @@ func (c *Client) execute(ctx context.Context, cmd *exec.Cmd) (string, string, er } } - msg := fmt.Sprintf("cmd=%v env=%v exit=%d", cmd.Args, cmd.Env, exitStatus) - c.logger.Println(ctx, msg) + args := fmt.Sprintf("%s", cmd.Args) + env := fmt.Sprintf("%s", cmd.Env) + logger.Build().Str("args", args).Str("env", env).Int("exit", exitStatus).Msg(ctx, "eos command executed") if err != nil { - err = errors.Wrap(err, "eosclient: error while executing command") + err = errors.Wrap(err, "error while executing command") } return outBuf.String(), errBuf.String(), err @@ -196,7 +199,7 @@ func (c *Client) AddACL(ctx context.Context, username, path string, a *ACL) erro sysACL := aclManager.serialize() // setting of the sys.acl is only possible from root user - unixUser, err := getUnixUser(rootUser) + unixUser, err := c.getUnixUser(rootUser) if err != nil { return err } @@ -233,7 +236,7 @@ func (c *Client) RemoveACL(ctx context.Context, username, path string, aclType s sysACL := aclManager.serialize() // setting of the sys.acl is only possible from root user - unixUser, err := getUnixUser(rootUser) + unixUser, err := c.getUnixUser(rootUser) if err != nil { return err } @@ -249,6 +252,7 @@ func (c *Client) UpdateACL(ctx context.Context, username, path string, a *ACL) e return c.AddACL(ctx, username, path, a) } +// GetACL for a file func (c *Client) GetACL(ctx context.Context, username, path, aclType, target string) (*ACL, error) { acls, err := c.ListACLs(ctx, username, path) if err != nil { @@ -271,7 +275,7 @@ func getUsername(uid string) (string, error) { return user.Username, nil } -// ListACLS returns the list of ACLs present under the given path. +// ListACLs returns the list of ACLs present under the given path. // EOS returns uids/gid for Citrine version and usernames for older versions. // For Citire we need to convert back the uid back to username. func (c *Client) ListACLs(ctx context.Context, username, path string) ([]*ACL, error) { @@ -285,7 +289,7 @@ func (c *Client) ListACLs(ctx context.Context, username, path string) ([]*ACL, e for _, a := range aclManager.getEntries() { username, err := getUsername(a.recipient) if err != nil { - c.logger.Error(ctx, err) + logger.Error(ctx, err) continue } acl := &ACL{ @@ -310,7 +314,7 @@ func (c *Client) getACLForPath(ctx context.Context, username, path string) (*acl // GetFileInfoByInode returns the FileInfo by the given inode func (c *Client) GetFileInfoByInode(ctx context.Context, username string, inode uint64) (*FileInfo, error) { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return nil, err } @@ -324,7 +328,7 @@ func (c *Client) GetFileInfoByInode(ctx context.Context, username string, inode // GetFileInfoByPath returns the FilInfo at the given path func (c *Client) GetFileInfoByPath(ctx context.Context, username, path string) (*FileInfo, error) { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return nil, err } @@ -339,7 +343,7 @@ func (c *Client) GetFileInfoByPath(ctx context.Context, username, path string) ( // GetQuota gets the quota of a user on the quota node defined by path func (c *Client) GetQuota(ctx context.Context, username, path string) (int, int, error) { // setting of the sys.acl is only possible from root user - unixUser, err := getUnixUser(rootUser) + unixUser, err := c.getUnixUser(rootUser) if err != nil { return 0, 0, err } @@ -353,7 +357,7 @@ func (c *Client) GetQuota(ctx context.Context, username, path string) (int, int, // CreateDir creates a directory at the given path func (c *Client) CreateDir(ctx context.Context, username, path string) error { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return err } @@ -365,7 +369,7 @@ func (c *Client) CreateDir(ctx context.Context, username, path string) error { // Remove removes the resource at the given path func (c *Client) Remove(ctx context.Context, username, path string) error { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return err } @@ -376,7 +380,7 @@ func (c *Client) Remove(ctx context.Context, username, path string) error { // Rename renames the resource referenced by oldPath to newPath func (c *Client) Rename(ctx context.Context, username, oldPath, newPath string) error { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return err } @@ -387,21 +391,21 @@ func (c *Client) Rename(ctx context.Context, username, oldPath, newPath string) // List the contents of the directory given by path func (c *Client) List(ctx context.Context, username, path string) ([]*FileInfo, error) { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return nil, err } cmd := exec.CommandContext(ctx, "/usr/bin/eos", "-r", unixUser.Uid, unixUser.Gid, "find", "--fileinfo", "--maxdepth", "1", path) stdout, _, err := c.execute(ctx, cmd) if err != nil { - return nil, errors.Wrapf(err, "eosclient: error listing fn=%s", path) + return nil, errors.Wrapf(err, "error listing fn=%s", path) } return c.parseFind(path, stdout) } // Read reads a file from the mgm func (c *Client) Read(ctx context.Context, username, path string) (io.ReadCloser, error) { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return nil, err } @@ -419,7 +423,7 @@ func (c *Client) Read(ctx context.Context, username, path string) (io.ReadCloser // Write writes a file to the mgm func (c *Client) Write(ctx context.Context, username, path string, stream io.ReadCloser) error { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return err } @@ -443,7 +447,7 @@ func (c *Client) Write(ctx context.Context, username, path string, stream io.Rea // ListDeletedEntries returns a list of the deleted entries. func (c *Client) ListDeletedEntries(ctx context.Context, username string) ([]*DeletedEntry, error) { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return nil, err } @@ -459,7 +463,7 @@ func (c *Client) ListDeletedEntries(ctx context.Context, username string) ([]*De // RestoreDeletedEntry restores a deleted entry. func (c *Client) RestoreDeletedEntry(ctx context.Context, username, key string) error { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return err } @@ -470,7 +474,7 @@ func (c *Client) RestoreDeletedEntry(ctx context.Context, username, key string) // PurgeDeletedEntries purges all entries from the recycle bin. func (c *Client) PurgeDeletedEntries(ctx context.Context, username string) error { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return err } @@ -499,7 +503,7 @@ func (c *Client) ListVersions(ctx context.Context, username, p string) ([]*FileI // RollbackToVersion rollbacks a file to a previous version. func (c *Client) RollbackToVersion(ctx context.Context, username, path, version string) error { - unixUser, err := getUnixUser(username) + unixUser, err := c.getUnixUser(username) if err != nil { return err } diff --git a/pkg/storage/broker/static/static.go b/pkg/storage/broker/static/static.go index af0652567e..996b2eeafe 100644 --- a/pkg/storage/broker/static/static.go +++ b/pkg/storage/broker/static/static.go @@ -16,6 +16,17 @@ type broker struct { rules map[string]string } +func (b *broker) ListProviders(ctx context.Context) ([]*storage.ProviderInfo, error) { + providers := []*storage.ProviderInfo{} + for k, v := range b.rules { + providers = append(providers, &storage.ProviderInfo{ + Endpoint: v, + MountPath: k, + }) + } + return providers, nil +} + func (b *broker) FindProvider(ctx context.Context, fn string) (*storage.ProviderInfo, error) { // find longest match var match string @@ -30,13 +41,14 @@ func (b *broker) FindProvider(ctx context.Context, fn string) (*storage.Provider } p := &storage.ProviderInfo{ - Location: b.rules[match], + MountPath: match, + Endpoint: b.rules[match], } return p, nil } type config struct { - Rules map[string]string + Rules map[string]string `mapstructure:"rules"` } func parseConfig(m map[string]interface{}) (*config, error) { diff --git a/pkg/storage/eos/eos.go b/pkg/storage/eos/eos.go index 52e7746856..2bcade87f7 100644 --- a/pkg/storage/eos/eos.go +++ b/pkg/storage/eos/eos.go @@ -15,6 +15,7 @@ import ( "github.com/cernbox/reva/pkg/mime" "github.com/cernbox/reva/pkg/storage" "github.com/cernbox/reva/pkg/user" + "github.com/mitchellh/mapstructure" "github.com/pkg/errors" ) @@ -33,44 +34,52 @@ type eosStorage struct { showHiddenSys bool } -// Options are the configuration options to pass to the New function. -type Options struct { - // Namespace for fn operations - Namespace string `json:"namespace"` - - // Where to write the logs - LogOut io.Writer +func parseConfig(m map[string]interface{}) (*config, error) { + c := &config{} + if err := mapstructure.Decode(m, c); err != nil { + return nil, err + } + return c, nil +} - // LogKey key to use for storing log traces - LogKey interface{} +// Options are the configuration options to pass to the New function. +type config struct { + // Namespace for metadata operations + Namespace string `mapstructure:"namespace"` // Location of the eos binary. // Default is /usr/bin/eos. - EosBinary string `json:"eos_binary"` + EosBinary string `mapstructure:"eos_binary"` // Location of the xrdcopy binary. // Default is /usr/bin/xrdcopy. - XrdcopyBinary string `json:"xrdcopy_binary"` + XrdcopyBinary string `mapstructure:"xrdcopy_binary"` // URL of the Master EOS MGM. // Default is root://eos-test.org - MasterURL string `json:"master_url"` + MasterURL string `mapstructure:"master_url"` // URL of the Slave EOS MGM. // Default is root://eos-test.org - SlaveURL string `json:"slave_url"` + SlaveURL string `mapstructure:"slave_url"` // Location on the local fs where to store reads. // Defaults to os.TempDir() - CacheDirectory string `json:"cache_directory"` + CacheDirectory string `mapstructure:"cache_directory"` // Enables logging of the commands executed // Defaults to false - EnableLogging bool `json:"enable_logging"` + EnableLogging bool `mapstructure:"enable_logging"` // ShowHiddenSysFiles shows internal EOS files like // .sys.v# and .sys.a# files. - ShowHiddenSysFiles bool `json:"show_hidden_sys_files"` + ShowHiddenSysFiles bool `mapstructure:"show_hidden_sys_files"` + + // ForceSingleUserMode will force connections to EOS to use SingleUsername + ForceSingleUserMode bool `mapstructure:"force_single_user_mode"` + + // SingleUsername is the username to use when SingleUserMode is enabled + SingleUsername string `mapstructure:"single_username"` } func getUser(ctx context.Context) (*user.User, error) { @@ -82,55 +91,59 @@ func getUser(ctx context.Context) (*user.User, error) { return u, nil } -func (opt *Options) init() { - opt.Namespace = path.Clean(opt.Namespace) - if !strings.HasPrefix(opt.Namespace, "/") { - opt.Namespace = "/" +func (c *config) init() { + c.Namespace = path.Clean(c.Namespace) + if !strings.HasPrefix(c.Namespace, "/") { + c.Namespace = "/" } - if opt.EosBinary == "" { - opt.EosBinary = "/usr/bin/eos" + if c.EosBinary == "" { + c.EosBinary = "/usr/bin/eos" } - if opt.XrdcopyBinary == "" { - opt.XrdcopyBinary = "/usr/bin/xrdcopy" + if c.XrdcopyBinary == "" { + c.XrdcopyBinary = "/usr/bin/xrdcopy" } - if opt.MasterURL == "" { - opt.MasterURL = "root://eos-example.org" + if c.MasterURL == "" { + c.MasterURL = "root://eos-example.org" } - if opt.SlaveURL == "" { - opt.SlaveURL = opt.MasterURL + if c.SlaveURL == "" { + c.SlaveURL = c.MasterURL } - if opt.CacheDirectory == "" { - opt.CacheDirectory = os.TempDir() + if c.CacheDirectory == "" { + c.CacheDirectory = os.TempDir() } } // New returns a new implementation of the storage.FS interface that connects to EOS. -func New(opt *Options) storage.FS { - opt.init() +func New(m map[string]interface{}) (storage.FS, error) { + c, err := parseConfig(m) + if err != nil { + return nil, err + } + c.init() eosClientOpts := &eosclient.Options{ - XrdcopyBinary: opt.XrdcopyBinary, - URL: opt.MasterURL, - EosBinary: opt.EosBinary, - CacheDirectory: opt.CacheDirectory, - LogOutput: opt.LogOut, - TraceKey: opt.LogKey, + XrdcopyBinary: c.XrdcopyBinary, + URL: c.MasterURL, + EosBinary: c.EosBinary, + CacheDirectory: c.CacheDirectory, + ForceSingleUserMode: c.ForceSingleUserMode, + SingleUsername: c.SingleUsername, } eosClient := eosclient.New(eosClientOpts) eosStorage := &eosStorage{ c: eosClient, - mountpoint: opt.Namespace, - showHiddenSys: opt.ShowHiddenSysFiles, + mountpoint: c.Namespace, + showHiddenSys: c.ShowHiddenSysFiles, } - return eosStorage + return eosStorage, nil } func (fs *eosStorage) getInternalPath(ctx context.Context, fn string) string { @@ -544,6 +557,5 @@ func (fs *eosStorage) getEosMetadata(finfo *eosclient.FileInfo) map[string]inter sys.TreeCount = finfo.TreeCount sys.TreeSize = finfo.TreeSize } - return map[string]interface{}{"eos": sys} } diff --git a/pkg/storage/local/local.go b/pkg/storage/local/local.go index 367c34daaa..1798a6422b 100644 --- a/pkg/storage/local/local.go +++ b/pkg/storage/local/local.go @@ -15,7 +15,7 @@ import ( ) type config struct { - Root string + Root string `mapstructure:"root"` } func parseConfig(m map[string]interface{}) (*config, error) { diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 51291949d5..8d96d282a5 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -97,12 +97,14 @@ type Revision struct { // for discovering storage providers type Broker interface { FindProvider(ctx context.Context, fn string) (*ProviderInfo, error) + ListProviders(ctx context.Context) ([]*ProviderInfo, error) } // ProviderInfo contains the information // about a StorageProvider type ProviderInfo struct { - Location string + MountPath string + Endpoint string } // FSTable contains descriptive information about the various file systems. diff --git a/services/grpcsvc/storagebrokersvc/storagebrokersvc.go b/services/grpcsvc/storagebrokersvc/storagebrokersvc.go index a48c01e550..3b333f8dc4 100644 --- a/services/grpcsvc/storagebrokersvc/storagebrokersvc.go +++ b/services/grpcsvc/storagebrokersvc/storagebrokersvc.go @@ -20,6 +20,7 @@ var errors = err.New("storagebrokersvc") type service struct { broker storage.Broker } + type config struct { Driver string `mapstructure:"driver"` Static map[string]interface{} `mapstructure:"static"` @@ -61,6 +62,28 @@ func getBroker(c *config) (storage.Broker, error) { return nil, fmt.Errorf("driver not found: %s", c.Driver) } } + +func (s *service) Discover(ctx context.Context, req *storagebrokerv0alphapb.DiscoverRequest) (*storagebrokerv0alphapb.DiscoverResponse, error) { + providers := []*storagebrokerv0alphapb.StorageProvider{} + pinfos, err := s.broker.ListProviders(ctx) + if err != nil { + res := &storagebrokerv0alphapb.DiscoverResponse{ + Status: &rpcpb.Status{Code: rpcpb.Code_CODE_INTERNAL}, + } + return res, nil + } + + for _, info := range pinfos { + providers = append(providers, format(info)) + } + + res := &storagebrokerv0alphapb.DiscoverResponse{ + Status: &rpcpb.Status{Code: rpcpb.Code_CODE_OK}, + StorageProviders: providers, + } + return res, nil +} + func (s *service) Find(ctx context.Context, req *storagebrokerv0alphapb.FindRequest) (*storagebrokerv0alphapb.FindResponse, error) { fn := req.Filename p, err := s.broker.FindProvider(ctx, fn) @@ -74,14 +97,15 @@ func (s *service) Find(ctx context.Context, req *storagebrokerv0alphapb.FindRequ provider := format(p) res := &storagebrokerv0alphapb.FindResponse{ - Status: &rpcpb.Status{Code: rpcpb.Code_CODE_OK}, - ProviderInfo: provider, + Status: &rpcpb.Status{Code: rpcpb.Code_CODE_OK}, + StorageProvider: provider, } return res, nil } -func format(p *storage.ProviderInfo) *storagebrokerv0alphapb.ProviderInfo { - return &storagebrokerv0alphapb.ProviderInfo{ - Location: p.Location, +func format(p *storage.ProviderInfo) *storagebrokerv0alphapb.StorageProvider { + return &storagebrokerv0alphapb.StorageProvider{ + Endpoint: p.Endpoint, + MountPath: p.MountPath, } } diff --git a/services/httpsvc/handlers/auth.go b/services/httpsvc/handlers/auth.go new file mode 100644 index 0000000000..f2e5730514 --- /dev/null +++ b/services/httpsvc/handlers/auth.go @@ -0,0 +1,73 @@ +package handlers + +import ( + "context" + "fmt" + "net/http" + "strings" + + oidc "github.com/coreos/go-oidc" + "github.com/gofrs/uuid" +) + +type user struct { + email string + groups []string +} + +// authorize verifies a bearer token and pulls user information form the claims. +func authorize(ctx context.Context, verifier *oidc.IDTokenVerifier, bearerToken string) (*user, error) { + + idToken, err := verifier.Verify(ctx, bearerToken) + if err != nil { + return nil, fmt.Errorf("could not verify bearer token: %v", err) + } + // Extract custom claims. + var claims struct { + Email string `json:"email"` + Verified bool `json:"email_verified"` + Groups []string `json:"groups"` + } + if err := idToken.Claims(&claims); err != nil { + return nil, fmt.Errorf("failed to parse claims: %v", err) + } + if !claims.Verified { + return nil, fmt.Errorf("email (%q) in returned claims was not verified", claims.Email) + } + return &user{claims.Email, claims.Groups}, nil +} + +// AuthHandler is the auth middlware that authenticates requests using OpenIDConnect, LDAP, +// or other auth mechanism. +func AuthHandler(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + // for time being just use OpenConnectID Connect + hdr := r.Header.Get("Authorization") + token := strings.TrimPrefix(hdr, "Bearer ") + + // Initialize a provider by specifying dex's issuer URL. + // provider needs to be cached as when it is created + // it will fetch the keys from the issuer using the .well-known + // endpoint + provider, err := oidc.NewProvider(ctx, "http://0.0.0.0:5556/dex") + if err != nil { + logger.Error(ctx, err) + w.WriteHeader(http.StatusUnauthorized) + return + } + verifier := provider.Verifier(&oidc.Config{ClientID: "example-app"}) + user, err := authorize(ctx, verifier, token) + if err != nil { + logger.Error(ctx, err) + w.WriteHeader(http.StatusUnauthorized) + return + } + logger.Println(ctx, "user logged in: ", user) + h.ServeHTTP(w, r) + }) +} + +func genTrace() string { + return uuid.Must(uuid.NewV4()).String() +} diff --git a/services/httpsvc/handlers/handlers.go b/services/httpsvc/handlers/handlers.go index 5ac8282f4b..7dc32a4215 100644 --- a/services/httpsvc/handlers/handlers.go +++ b/services/httpsvc/handlers/handlers.go @@ -1 +1,5 @@ package handlers + +import "github.com/cernbox/reva/pkg/log" + +var logger = log.New("handlers") diff --git a/services/httpsvc/handlers/trace.go b/services/httpsvc/handlers/trace.go index 53bafb3a71..078ad1ba4e 100644 --- a/services/httpsvc/handlers/trace.go +++ b/services/httpsvc/handlers/trace.go @@ -4,7 +4,6 @@ import ( "context" "net/http" - "github.com/gofrs/uuid" "google.golang.org/grpc/metadata" ) @@ -33,7 +32,3 @@ func TraceHandler(h http.Handler) http.Handler { h.ServeHTTP(w, r) }) } - -func genTrace() string { - return uuid.Must(uuid.NewV4()).String() -} From 33bf6125e8ec92e665098bfa04e9856c28b23cd9 Mon Sep 17 00:00:00 2001 From: Hugo Gonzalez Labrador Date: Thu, 31 Jan 2019 15:07:31 +0100 Subject: [PATCH 2/2] build: add go modules --- go.mod | 34 +++++++++++++++ go.sum | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000..51e4bfa6c2 --- /dev/null +++ b/go.mod @@ -0,0 +1,34 @@ +module github.com/cernbox/reva + +require ( + github.com/cernbox/go-cs3apis v0.0.0-20190131091639-e141d987d212 + github.com/cheggaaa/pb v1.0.27 + github.com/coreos/go-oidc v2.0.0+incompatible + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/go-sql-driver/mysql v1.4.1 + github.com/gofrs/uuid v3.2.0+incompatible + github.com/golang/protobuf v1.2.1-0.20181127190454-8d0c54c12466 + github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 + github.com/mattn/go-colorable v0.0.9 // indirect + github.com/mattn/go-isatty v0.0.4 // indirect + github.com/mattn/go-runewidth v0.0.4 // indirect + github.com/mitchellh/mapstructure v1.1.2 + github.com/pkg/errors v0.8.1 + github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect + github.com/prometheus/client_golang v0.9.2 + github.com/rs/zerolog v1.11.0 + github.com/spf13/viper v1.3.1 + golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 + golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 + google.golang.org/grpc v1.18.0 + gopkg.in/VividCortex/ewma.v1 v1.1.1 // indirect + gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect + gopkg.in/cheggaaa/pb.v2 v2.0.6 // indirect + gopkg.in/fatih/color.v1 v1.7.0 // indirect + gopkg.in/ldap.v2 v2.5.1 + gopkg.in/mattn/go-colorable.v0 v0.0.9 // indirect + gopkg.in/mattn/go-isatty.v0 v0.0.4 // indirect + gopkg.in/mattn/go-runewidth.v0 v0.0.4 // indirect + gopkg.in/square/go-jose.v2 v2.2.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000..ad1881393a --- /dev/null +++ b/go.sum @@ -0,0 +1,130 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/cernbox/go-cs3apis v0.0.0-20190131091639-e141d987d212 h1:1PnB1SyuGFLdva2HoaEGMHNb4QaFcDRNPoAUZZ2j1Co= +github.com/cernbox/go-cs3apis v0.0.0-20190131091639-e141d987d212/go.mod h1:PhtmaQ6Ivutykhij6AjevimTEvuNeyT6Z7Yfyv1Rygg= +github.com/cheggaaa/pb v1.0.27 h1:wIkZHkNfC7R6GI5w7l/PdAdzXzlrbcI3p8OAlnkTsnc= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/cheggaaa/pb v2.0.6+incompatible h1:sutSx+mRaNbeJUMCAtyqNWU/tQ0B/xBm+hyb1JQmQYs= +github.com/cheggaaa/pb v2.0.6+incompatible/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.0.0+incompatible h1:+RStIopZ8wooMx+Vs5Bt8zMXxV1ABl5LbakNExNmZIg= +github.com/coreos/go-oidc v2.0.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.1-0.20181127190454-8d0c54c12466 h1:Kz9p8XEhFbEfi4ka99LEEwIXF4jqyIdB5fuh7UbMFj4= +github.com/golang/protobuf v1.2.1-0.20181127190454-8d0c54c12466/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rs/zerolog v1.11.0 h1:DRuq/S+4k52uJzBQciUcofXx45GrMC6yrEbb/CoK6+M= +github.com/rs/zerolog v1.11.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38= +github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8= +golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA= +google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +gopkg.in/VividCortex/ewma.v1 v1.1.1 h1:tWHEKkKq802K/JT9RiqGCBU5fW3raAPnJGTE9ostZvg= +gopkg.in/VividCortex/ewma.v1 v1.1.1/go.mod h1:TekXuFipeiHWiAlO1+wSS23vTcyFau5u3rxXUSXj710= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v2 v2.0.6 h1:L2KAo2l2ZQTzxmh8b9RdQpzgLpK2mX3paGCMJSUugBk= +gopkg.in/cheggaaa/pb.v2 v2.0.6/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4= +gopkg.in/fatih/color.v1 v1.7.0 h1:bYGjb+HezBM6j/QmgBfgm1adxHpzzrss6bj4r9ROppk= +gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0= +gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU= +gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk= +gopkg.in/mattn/go-colorable.v0 v0.0.9 h1:hdmPK5LuJJJH1MRy2o35WJEtn2BlFmHOZEqMqlRCwdM= +gopkg.in/mattn/go-colorable.v0 v0.0.9/go.mod h1:BVJlBXzARQxdi3nZo6f6bnl5yR20/tOL6p+V0KejgSY= +gopkg.in/mattn/go-isatty.v0 v0.0.4 h1:NtS1rQGQr4IaFWBGz4Cz4BhB///gyys4gDVtKA7hIsc= +gopkg.in/mattn/go-isatty.v0 v0.0.4/go.mod h1:wt691ab7g0X4ilKZNmMII3egK0bTxl37fEn/Fwbd8gc= +gopkg.in/mattn/go-runewidth.v0 v0.0.4 h1:r0P71TnzQDlNIcizCqvPSSANoFa3WVGtcNJf3TWurcY= +gopkg.in/mattn/go-runewidth.v0 v0.0.4/go.mod h1:BmXejnxvhwdaATwiJbB1vZ2dtXkQKZGu9yLFCZb4msQ= +gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=