From ae2f15c6d036b6db9ee5fd86e9b9f643f3a8df63 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Mon, 25 Jan 2016 17:29:20 -0800 Subject: [PATCH] api: More cleanups at WebAPI. - Fixes a bug where bucketName was not denormalized. - Remove unneeded functions from jwt.go --- jwt-auth-handler.go | 21 +++++++++-- jwt.go | 85 ++++++++++++++++++++------------------------- pkg/fs/fs-object.go | 9 +++++ web-config.go | 22 ++++++++---- web-definitions.go | 16 +++++++++ web-handlers.go | 25 +++++++++---- 6 files changed, 117 insertions(+), 61 deletions(-) diff --git a/jwt-auth-handler.go b/jwt-auth-handler.go index 62c17064d..f89ad19a4 100644 --- a/jwt-auth-handler.go +++ b/jwt-auth-handler.go @@ -1,3 +1,19 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package main import ( @@ -12,7 +28,7 @@ type authHandler struct { } // AuthHandler - -// Verify if authorization header has signature version '2', reject it cleanly. +// Verify if authorization header is of form JWT, reject it otherwise. func AuthHandler(h http.Handler) http.Handler { return authHandler{h} } @@ -20,11 +36,12 @@ func AuthHandler(h http.Handler) http.Handler { // Ignore request if authorization header is not valid. func (h authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Let the top level caller handle if the requests should be - // allowed. + // allowed, if there are no Authorization headers. if r.Header.Get("Authorization") == "" { h.handler.ServeHTTP(w, r) return } + // Validate Authorization header to be valid. jwt := InitJWT() token, err := jwtgo.ParseFromRequest(r, func(token *jwtgo.Token) (interface{}, error) { if _, ok := token.Method.(*jwtgo.SigningMethodRSA); !ok { diff --git a/jwt.go b/jwt.go index e37e9e8ca..492cdd3cb 100644 --- a/jwt.go +++ b/jwt.go @@ -1,35 +1,60 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package main import ( "crypto/rsa" "crypto/x509" "encoding/pem" + "errors" "io/ioutil" "time" - jwt "github.com/dgrijalva/jwt-go" + jwtgo "github.com/dgrijalva/jwt-go" + "github.com/minio/minio-xl/pkg/probe" "golang.org/x/crypto/bcrypt" ) // JWT - jwt auth backend type JWT struct { + // Public value. + PublicKey *rsa.PublicKey + // private values. privateKey *rsa.PrivateKey - PublicKey *rsa.PublicKey accessKeyID string secretAccessKey string } const ( jwtExpirationDelta = 10 - expireOffset = 3600 ) -// InitJWT - init. +// InitJWT - initialize. func InitJWT() *JWT { jwt := &JWT{ privateKey: getPrivateKey(), - PublicKey: getPublicKey(), } + // Validate if public key is of algorithm *rsa.PublicKey. + var ok bool + jwt.PublicKey, ok = jwt.privateKey.Public().(*rsa.PublicKey) + if !ok { + fatalIf(probe.NewError(errors.New("")), "Unsupported type of public key algorithm found.", nil) + } + // Load credentials configuration. config, err := loadConfigV2() fatalIf(err.Trace("JWT"), "Unable to load configuration file.", nil) @@ -39,9 +64,10 @@ func InitJWT() *JWT { return jwt } -// GenerateToken - +// GenerateToken - generates a new Json Web Token based on the incoming user id. func (b *JWT) GenerateToken(userName string) (string, error) { - token := jwt.New(jwt.SigningMethodRS512) + token := jwtgo.New(jwtgo.SigningMethodRS512) + // Token expires in 10hrs. token.Claims["exp"] = time.Now().Add(time.Hour * time.Duration(jwtExpirationDelta)).Unix() token.Claims["iat"] = time.Now().Unix() token.Claims["sub"] = userName @@ -52,32 +78,16 @@ func (b *JWT) GenerateToken(userName string) (string, error) { return tokenString, nil } -// Authenticate - -func (b *JWT) Authenticate(args *LoginArgs) bool { +// Authenticate - authenticates the username and password. +func (b *JWT) Authenticate(username, password string) bool { hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(b.secretAccessKey), 10) - if args.Username == b.accessKeyID { - return bcrypt.CompareHashAndPassword(hashedPassword, []byte(args.Password)) == nil + if username == b.accessKeyID { + return bcrypt.CompareHashAndPassword(hashedPassword, []byte(password)) == nil } return false } -// -func (b *JWT) getTokenRemainingValidity(timestamp interface{}) int { - if validity, ok := timestamp.(float64); ok { - tm := time.Unix(int64(validity), 0) - remainer := tm.Sub(time.Now()) - if remainer > 0 { - return int(remainer.Seconds() + expireOffset) - } - } - return expireOffset -} - -// Logout - logout is not implemented yet. -func (b *JWT) Logout(tokenString string) error { - return nil -} - +// getPrivateKey - get the generated private key. func getPrivateKey() *rsa.PrivateKey { pemBytes, err := ioutil.ReadFile(mustGetPrivateKeyPath()) if err != nil { @@ -90,22 +100,3 @@ func getPrivateKey() *rsa.PrivateKey { } return privateKeyImported } - -func getPublicKey() *rsa.PublicKey { - pemBytes, err := ioutil.ReadFile(mustGetPublicKeyPath()) - if err != nil { - panic(err) - } - data, _ := pem.Decode([]byte(pemBytes)) - publicKeyImported, err := x509.ParsePKIXPublicKey(data.Bytes) - if err != nil { - panic(err) - } - - rsaPub, ok := publicKeyImported.(*rsa.PublicKey) - if !ok { - panic(err) - } - - return rsaPub -} diff --git a/pkg/fs/fs-object.go b/pkg/fs/fs-object.go index 57ab4b9a5..cd28bb054 100644 --- a/pkg/fs/fs-object.go +++ b/pkg/fs/fs-object.go @@ -113,6 +113,15 @@ func (fs Filesystem) GetObjectMetadata(bucket, object string) (ObjectMetadata, * return ObjectMetadata{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: bucket}) } + bucket = fs.denormalizeBucket(bucket) + bucketPath := filepath.Join(fs.path, bucket) + if _, e := os.Stat(bucketPath); e != nil { + if os.IsNotExist(e) { + return ObjectMetadata{}, probe.NewError(BucketNotFound{Bucket: bucket}) + } + return ObjectMetadata{}, probe.NewError(e) + } + metadata, err := getMetadata(fs.path, bucket, object) if err != nil { return ObjectMetadata{}, err.Trace(bucket, object) diff --git a/web-config.go b/web-config.go index 5cb868eb2..a5df87eb0 100644 --- a/web-config.go +++ b/web-config.go @@ -1,3 +1,19 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package main import ( @@ -46,9 +62,3 @@ func mustGetPrivateKeyPath() string { fatalIf(err.Trace(), "Unable to get config path.", nil) return webConfigDir + "/private.key" } - -func mustGetPublicKeyPath() string { - webConfigDir, err := getWebConfigDir() - fatalIf(err.Trace(), "Unable to get config path.", nil) - return webConfigDir + "/public.key" -} diff --git a/web-definitions.go b/web-definitions.go index fbecac57b..3bc868a83 100644 --- a/web-definitions.go +++ b/web-definitions.go @@ -1,3 +1,19 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package main import "time" diff --git a/web-handlers.go b/web-handlers.go index 186ddc096..b6d7fc452 100644 --- a/web-handlers.go +++ b/web-handlers.go @@ -1,3 +1,19 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package main import ( @@ -8,6 +24,8 @@ import ( jwtgo "github.com/dgrijalva/jwt-go" ) +// isAuthenticated validates if any incoming request to be a valid JWT +// authenticated request. func isAuthenticated(req *http.Request) bool { jwt := InitJWT() tokenRequest, err := jwtgo.ParseFromRequest(req, func(token *jwtgo.Token) (interface{}, error) { @@ -85,7 +103,7 @@ 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 { jwt := InitJWT() - if jwt.Authenticate(args) { + if jwt.Authenticate(args.Username, args.Password) { token, err := jwt.GenerateToken(args.Username) if err != nil { return err @@ -113,11 +131,6 @@ 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) { - jwt := InitJWT() - tokenString := r.Header.Get("Authorization") - if err := jwt.Logout(tokenString); err != nil { - return err - } return nil } return errUnAuthorizedRequest