move credentials as separate package (#5115)

This commit is contained in:
Bala FA
2017-10-31 11:54:32 -07:00
committed by Dee Koder
parent 8d584bd819
commit 32c6b62932
26 changed files with 307 additions and 277 deletions

123
pkg/auth/credentials.go Normal file
View File

@@ -0,0 +1,123 @@
/*
* Minio Cloud Storage, (C) 2015, 2016, 2017 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 auth
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"fmt"
)
const (
// Minimum length for Minio access key.
accessKeyMinLen = 5
// Maximum length for Minio access key.
// There is no max length enforcement for access keys
accessKeyMaxLen = 20
// Minimum length for Minio secret key for both server and gateway mode.
secretKeyMinLen = 8
// Maximum secret key length for Minio, this
// is used when autogenerating new credentials.
// There is no max length enforcement for secret keys
secretKeyMaxLen = 40
// Alpha numeric table used for generating access keys.
alphaNumericTable = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// Total length of the alpha numeric table.
alphaNumericTableLen = byte(len(alphaNumericTable))
)
// Common errors generated for access and secret key validation.
var (
ErrInvalidAccessKeyLength = fmt.Errorf("access key must be minimum %v or more characters long", accessKeyMinLen)
ErrInvalidSecretKeyLength = fmt.Errorf("secret key must be minimum %v or more characters long", secretKeyMinLen)
)
// IsAccessKeyValid - validate access key for right length.
func IsAccessKeyValid(accessKey string) bool {
return len(accessKey) >= accessKeyMinLen
}
// isSecretKeyValid - validate secret key for right length.
func isSecretKeyValid(secretKey string) bool {
return len(secretKey) >= secretKeyMinLen
}
// Credentials holds access and secret keys.
type Credentials struct {
AccessKey string `json:"accessKey,omitempty"`
SecretKey string `json:"secretKey,omitempty"`
}
// IsValid - returns whether credential is valid or not.
func (cred Credentials) IsValid() bool {
return IsAccessKeyValid(cred.AccessKey) && isSecretKeyValid(cred.SecretKey)
}
// Equal - returns whether two credentials are equal or not.
func (cred Credentials) Equal(ccred Credentials) bool {
if !ccred.IsValid() {
return false
}
return cred.AccessKey == ccred.AccessKey && subtle.ConstantTimeCompare([]byte(cred.SecretKey), []byte(ccred.SecretKey)) == 1
}
// MustGetNewCredentials generates and returns new credential.
func MustGetNewCredentials() (cred Credentials) {
readBytes := func(size int) (data []byte) {
data = make([]byte, size)
if n, err := rand.Read(data); err != nil {
panic(err)
} else if n != size {
panic(fmt.Errorf("not enough data read. expected: %v, got: %v", size, n))
}
return
}
// Generate access key.
keyBytes := readBytes(accessKeyMaxLen)
for i := 0; i < accessKeyMaxLen; i++ {
keyBytes[i] = alphaNumericTable[keyBytes[i]%alphaNumericTableLen]
}
cred.AccessKey = string(keyBytes)
// Generate secret key.
keyBytes = readBytes(secretKeyMaxLen)
cred.SecretKey = string([]byte(base64.StdEncoding.EncodeToString(keyBytes))[:secretKeyMaxLen])
return cred
}
// CreateCredentials returns new credential with the given access key and secret key.
// Error is returned if given access key or secret key are invalid length.
func CreateCredentials(accessKey, secretKey string) (cred Credentials, err error) {
if !IsAccessKeyValid(accessKey) {
return cred, ErrInvalidAccessKeyLength
}
if !isSecretKeyValid(secretKey) {
return cred, ErrInvalidSecretKeyLength
}
cred.AccessKey = accessKey
cred.SecretKey = secretKey
return cred, nil
}

View File

@@ -0,0 +1,135 @@
/*
* Minio Cloud Storage, (C) 2017 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 auth
import "testing"
func TestIsAccessKeyValid(t *testing.T) {
testCases := []struct {
accessKey string
expectedResult bool
}{
{alphaNumericTable[:accessKeyMinLen], true},
{alphaNumericTable[:accessKeyMinLen+1], true},
{alphaNumericTable[:accessKeyMinLen-1], false},
}
for i, testCase := range testCases {
result := IsAccessKeyValid(testCase.accessKey)
if result != testCase.expectedResult {
t.Fatalf("test %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
}
}
}
func TestIsSecretKeyValid(t *testing.T) {
testCases := []struct {
secretKey string
expectedResult bool
}{
{alphaNumericTable[:secretKeyMinLen], true},
{alphaNumericTable[:secretKeyMinLen+1], true},
{alphaNumericTable[:secretKeyMinLen-1], false},
}
for i, testCase := range testCases {
result := isSecretKeyValid(testCase.secretKey)
if result != testCase.expectedResult {
t.Fatalf("test %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
}
}
}
func TestMustGetNewCredentials(t *testing.T) {
cred := MustGetNewCredentials()
if !cred.IsValid() {
t.Fatalf("Failed to get new valid credential")
}
if len(cred.AccessKey) != accessKeyMaxLen {
t.Fatalf("access key length: expected: %v, got: %v", secretKeyMaxLen, len(cred.AccessKey))
}
if len(cred.SecretKey) != secretKeyMaxLen {
t.Fatalf("secret key length: expected: %v, got: %v", secretKeyMaxLen, len(cred.SecretKey))
}
}
func TestCreateCredentials(t *testing.T) {
testCases := []struct {
accessKey string
secretKey string
valid bool
expectedErr error
}{
// Valid access and secret keys with minimum length.
{alphaNumericTable[:accessKeyMinLen], alphaNumericTable[:secretKeyMinLen], true, nil},
// Valid access and/or secret keys are longer than minimum length.
{alphaNumericTable[:accessKeyMinLen+1], alphaNumericTable[:secretKeyMinLen+1], true, nil},
// Smaller access key.
{alphaNumericTable[:accessKeyMinLen-1], alphaNumericTable[:secretKeyMinLen], false, ErrInvalidAccessKeyLength},
// Smaller secret key.
{alphaNumericTable[:accessKeyMinLen], alphaNumericTable[:secretKeyMinLen-1], false, ErrInvalidSecretKeyLength},
}
for i, testCase := range testCases {
cred, err := CreateCredentials(testCase.accessKey, testCase.secretKey)
if err != nil {
if testCase.expectedErr == nil {
t.Fatalf("test %v: error: expected = <nil>, got = %v", i+1, err)
}
if testCase.expectedErr.Error() != err.Error() {
t.Fatalf("test %v: error: expected = %v, got = %v", i+1, testCase.expectedErr, err)
}
} else {
if testCase.expectedErr != nil {
t.Fatalf("test %v: error: expected = %v, got = <nil>", i+1, testCase.expectedErr)
}
if !cred.IsValid() {
t.Fatalf("test %v: got invalid credentials", i+1)
}
}
}
}
func TestCredentialsEqual(t *testing.T) {
cred := MustGetNewCredentials()
testCases := []struct {
cred Credentials
ccred Credentials
expectedResult bool
}{
// Same Credentialss.
{cred, cred, true},
// Empty credentials to compare.
{cred, Credentials{}, false},
// Empty credentials.
{Credentials{}, cred, false},
// Two different credentialss
{cred, MustGetNewCredentials(), false},
// Access key is different in credentials to compare.
{cred, Credentials{AccessKey: "myuser", SecretKey: cred.SecretKey}, false},
// Secret key is different in credentials to compare.
{cred, Credentials{AccessKey: cred.AccessKey, SecretKey: "mypassword"}, false},
}
for i, testCase := range testCases {
result := testCase.cred.Equal(testCase.ccred)
if result != testCase.expectedResult {
t.Fatalf("test %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
}
}
}