diff --git a/jwt-auth-handler.go b/jwt-auth-handler.go index 62739b0c8..62c17064d 100644 --- a/jwt-auth-handler.go +++ b/jwt-auth-handler.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - "github.com/dgrijalva/jwt-go" + jwtgo "github.com/dgrijalva/jwt-go" ) type authHandler struct { @@ -25,12 +25,12 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.handler.ServeHTTP(w, r) return } - authBackend := InitJWT() - token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { + jwt := InitJWT() + token, err := jwtgo.ParseFromRequest(r, func(token *jwtgo.Token) (interface{}, error) { + if _, ok := token.Method.(*jwtgo.SigningMethodRSA); !ok { return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) } - return authBackend.PublicKey, nil + return jwt.PublicKey, nil }) if err != nil || !token.Valid { w.WriteHeader(http.StatusUnauthorized) diff --git a/jwt.go b/jwt.go index 5ab31817c..e37e9e8ca 100644 --- a/jwt.go +++ b/jwt.go @@ -11,10 +11,12 @@ import ( "golang.org/x/crypto/bcrypt" ) -// JWTAuthBackend - jwt auth backend -type JWTAuthBackend struct { - privateKey *rsa.PrivateKey - PublicKey *rsa.PublicKey +// JWT - jwt auth backend +type JWT struct { + privateKey *rsa.PrivateKey + PublicKey *rsa.PublicKey + accessKeyID string + secretAccessKey string } const ( @@ -23,16 +25,22 @@ const ( ) // InitJWT - init. -func InitJWT() *JWTAuthBackend { - authBackendInstance := &JWTAuthBackend{ +func InitJWT() *JWT { + jwt := &JWT{ privateKey: getPrivateKey(), PublicKey: getPublicKey(), } - return authBackendInstance + config, err := loadConfigV2() + fatalIf(err.Trace("JWT"), "Unable to load configuration file.", nil) + + // Save access, secret keys. + jwt.accessKeyID = config.Credentials.AccessKeyID + jwt.secretAccessKey = config.Credentials.SecretAccessKey + return jwt } // GenerateToken - -func (b *JWTAuthBackend) GenerateToken(userName string) (string, error) { +func (b *JWT) GenerateToken(userName string) (string, error) { token := jwt.New(jwt.SigningMethodRS512) token.Claims["exp"] = time.Now().Add(time.Hour * time.Duration(jwtExpirationDelta)).Unix() token.Claims["iat"] = time.Now().Unix() @@ -45,16 +53,16 @@ func (b *JWTAuthBackend) GenerateToken(userName string) (string, error) { } // Authenticate - -func (b *JWTAuthBackend) Authenticate(args *LoginArgs, accessKeyID, secretAccessKey string) bool { - hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(secretAccessKey), 10) - if args.Username == accessKeyID { +func (b *JWT) Authenticate(args *LoginArgs) bool { + hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(b.secretAccessKey), 10) + if args.Username == b.accessKeyID { return bcrypt.CompareHashAndPassword(hashedPassword, []byte(args.Password)) == nil } return false } // -func (b *JWTAuthBackend) getTokenRemainingValidity(timestamp interface{}) int { +func (b *JWT) getTokenRemainingValidity(timestamp interface{}) int { if validity, ok := timestamp.(float64); ok { tm := time.Unix(int64(validity), 0) remainer := tm.Sub(time.Now()) @@ -66,7 +74,7 @@ func (b *JWTAuthBackend) getTokenRemainingValidity(timestamp interface{}) int { } // Logout - logout is not implemented yet. -func (b *JWTAuthBackend) Logout(tokenString string) error { +func (b *JWT) Logout(tokenString string) error { return nil } diff --git a/routers.go b/routers.go index 42bdc598a..8f8e5809c 100644 --- a/routers.go +++ b/routers.go @@ -17,27 +17,35 @@ package main import ( + "net" "net/http" router "github.com/gorilla/mux" jsonrpc "github.com/gorilla/rpc/v2" "github.com/gorilla/rpc/v2/json" + "github.com/minio/minio-go" + "github.com/minio/minio-xl/pkg/probe" "github.com/minio/minio/pkg/fs" ) // CloudStorageAPI container for S3 compatible API. type CloudStorageAPI struct { + // Do not check for incoming signatures, allow all requests. + Anonymous bool + // Once true log all incoming requests. + AccessLog bool + // Filesystem instance. Filesystem fs.Filesystem - Anonymous bool // do not checking for incoming signatures, allow all requests - AccessLog bool // if true log all incoming request } // WebAPI container for Web API. type WebAPI struct { - Anonymous bool - AccessLog bool - AccessKeyID string - SecretAccessKey string + // Do not check for incoming authorization header. + Anonymous bool + // Once true log all incoming request. + AccessLog bool + // Minio client instance. + Client minio.CloudStorageClient } func getWebAPIHandler(web *WebAPI) http.Handler { @@ -100,12 +108,22 @@ func registerCloudStorageAPI(mux *router.Router, a CloudStorageAPI) { // getNewWebAPI instantiate a new WebAPI. func getNewWebAPI(conf cloudServerConfig) *WebAPI { + // Split host port. + _, port, e := net.SplitHostPort(conf.Address) + fatalIf(probe.NewError(e), "Unable to parse web addess.", nil) + + // Default host to 'localhost'. + host := "localhost" + + // Initialize minio client for AWS Signature Version '4' + client, e := minio.NewV4(net.JoinHostPort(host, port), conf.AccessKeyID, conf.SecretAccessKey, true) + fatalIf(probe.NewError(e), "Unable to initialize minio client", nil) + web := &WebAPI{ Anonymous: conf.Anonymous, AccessLog: conf.AccessLog, + Client: client, } - web.AccessKeyID = conf.AccessKeyID - web.SecretAccessKey = conf.SecretAccessKey return web } diff --git a/web-definitions.go b/web-definitions.go index 48587dce2..fbecac57b 100644 --- a/web-definitions.go +++ b/web-definitions.go @@ -1,5 +1,12 @@ package main +import "time" + +// MakeBucketArgs - make bucket args. +type MakeBucketArgs struct { + BucketName string `json:"bucketName"` +} + // ListBucketsArgs - list bucket args. type ListBucketsArgs struct{} @@ -9,6 +16,24 @@ type ListObjectsArgs struct { Prefix string `json:"prefix"` } +// BucketInfo container for list buckets metadata. +type BucketInfo struct { + // The name of the bucket. + Name string `json:"name"` + // Date the bucket was created. + CreationDate time.Time `json:"creationDate"` +} + +// ObjectInfo container for list objects metadata. +type ObjectInfo struct { + // Name of the object + Key string `json:"name"` + // Date and time the object was last modified. + LastModified time.Time `json:"lastModified"` + // Size in bytes of the object. + Size int64 `json:"size"` +} + // GetObjectURLArgs - get object url. type GetObjectURLArgs struct { BucketName string `json:"bucketName"` diff --git a/web-handlers.go b/web-handlers.go index 587f228b2..186ddc096 100644 --- a/web-handlers.go +++ b/web-handlers.go @@ -5,17 +5,16 @@ import ( "net/http" "time" - "github.com/dgrijalva/jwt-go" - "github.com/minio/minio-go" + jwtgo "github.com/dgrijalva/jwt-go" ) func isAuthenticated(req *http.Request) bool { - authBackend := InitJWT() - tokenRequest, err := jwt.ParseFromRequest(req, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { + jwt := InitJWT() + tokenRequest, err := jwtgo.ParseFromRequest(req, func(token *jwtgo.Token) (interface{}, error) { + if _, ok := token.Method.(*jwtgo.SigningMethodRSA); !ok { return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) } - return authBackend.PublicKey, nil + return jwt.PublicKey, nil }) if err != nil { return false @@ -23,43 +22,50 @@ func isAuthenticated(req *http.Request) bool { return tokenRequest.Valid } -// ListBuckets - list buckets api. -func (web *WebAPI) ListBuckets(r *http.Request, args *ListBucketsArgs, reply *[]minio.BucketInfo) error { +// MakeBucket - make a bucket. +func (web *WebAPI) MakeBucket(r *http.Request, args *MakeBucketArgs, reply *string) error { if !isAuthenticated(r) { return errUnAuthorizedRequest } - client, err := minio.New("localhost:9000", web.AccessKeyID, web.SecretAccessKey, true) + return web.Client.MakeBucket(args.BucketName, "", "") +} + +// ListBuckets - list buckets api. +func (web *WebAPI) ListBuckets(r *http.Request, args *ListBucketsArgs, reply *[]BucketInfo) error { + if !isAuthenticated(r) { + return errUnAuthorizedRequest + } + buckets, err := web.Client.ListBuckets() if err != nil { return err } - buckets, err := client.ListBuckets() - if err != nil { - return err + for _, bucket := range buckets { + *reply = append(*reply, BucketInfo{ + Name: bucket.Name, + CreationDate: bucket.CreationDate, + }) } - *reply = buckets return nil } // ListObjects - list objects api. -func (web *WebAPI) ListObjects(r *http.Request, args *ListObjectsArgs, reply *[]minio.ObjectInfo) error { +func (web *WebAPI) ListObjects(r *http.Request, args *ListObjectsArgs, reply *[]ObjectInfo) error { if !isAuthenticated(r) { return errUnAuthorizedRequest } - client, err := minio.New("localhost:9000", web.AccessKeyID, web.SecretAccessKey, true) - if err != nil { - return err - } doneCh := make(chan struct{}) defer close(doneCh) - var objects []minio.ObjectInfo - for object := range client.ListObjects(args.BucketName, args.Prefix, false, doneCh) { + for object := range web.Client.ListObjects(args.BucketName, args.Prefix, false, doneCh) { if object.Err != nil { return object.Err } - objects = append(objects, object) + *reply = append(*reply, ObjectInfo{ + Key: object.Key, + LastModified: object.LastModified, + Size: object.Size, + }) } - *reply = objects return nil } @@ -68,11 +74,7 @@ func (web *WebAPI) GetObjectURL(r *http.Request, args *GetObjectURLArgs, reply * if !isAuthenticated(r) { return errUnAuthorizedRequest } - client, err := minio.New("localhost:9000", web.AccessKeyID, web.SecretAccessKey, true) - if err != nil { - return err - } - urlStr, err := client.PresignedGetObject(args.BucketName, args.ObjectName, time.Duration(60*60)*time.Second) + urlStr, err := web.Client.PresignedGetObject(args.BucketName, args.ObjectName, time.Duration(60*60)*time.Second) if err != nil { return err } @@ -82,9 +84,9 @@ func (web *WebAPI) GetObjectURL(r *http.Request, args *GetObjectURLArgs, reply * // Login - user login handler. func (web *WebAPI) Login(r *http.Request, args *LoginArgs, reply *AuthToken) error { - authBackend := InitJWT() - if authBackend.Authenticate(args, web.AccessKeyID, web.SecretAccessKey) { - token, err := authBackend.GenerateToken(args.Username) + jwt := InitJWT() + if jwt.Authenticate(args) { + token, err := jwt.GenerateToken(args.Username) if err != nil { return err } @@ -97,8 +99,8 @@ func (web *WebAPI) Login(r *http.Request, args *LoginArgs, reply *AuthToken) err // RefreshToken - refresh token handler. func (web *WebAPI) RefreshToken(r *http.Request, args *LoginArgs, reply *AuthToken) error { if isAuthenticated(r) { - authBackend := InitJWT() - token, err := authBackend.GenerateToken(args.Username) + jwt := InitJWT() + token, err := jwt.GenerateToken(args.Username) if err != nil { return err } @@ -111,9 +113,9 @@ func (web *WebAPI) RefreshToken(r *http.Request, args *LoginArgs, reply *AuthTok // Logout - user logout. func (web *WebAPI) Logout(r *http.Request, arg *string, reply *string) error { if isAuthenticated(r) { - authBackend := InitJWT() + jwt := InitJWT() tokenString := r.Header.Get("Authorization") - if err := authBackend.Logout(tokenString); err != nil { + if err := jwt.Logout(tokenString); err != nil { return err } return nil