-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 for v0.6 Release
- Loading branch information
Showing
37 changed files
with
2,563 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) | ||
// go-aah/security source code and usage is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package acrypto | ||
|
||
import "golang.org/x/crypto/bcrypt" | ||
|
||
// BcryptEncoder struct implements `PasswordEncoder` interface for `bycrpt` | ||
// hashing. | ||
type BcryptEncoder struct { | ||
} | ||
|
||
// Compare method compares given hash and password using bcrypt. | ||
func (be *BcryptEncoder) Compare(hash, password []byte) bool { | ||
err := bcrypt.CompareHashAndPassword(hash, password) | ||
return err == nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) | ||
// go-aah/security source code and usage is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package acrypto | ||
|
||
import ( | ||
"testing" | ||
|
||
"golang.org/x/crypto/bcrypt" | ||
|
||
"aahframework.org/test.v0/assert" | ||
) | ||
|
||
func TestBcryptHashing(t *testing.T) { | ||
bcryptEncoder := BcryptEncoder{} | ||
hashPassword, _ := bcrypt.GenerateFromPassword([]byte("welcome@123"), 10) | ||
result := bcryptEncoder.Compare(hashPassword, []byte("welcome@123")) | ||
assert.True(t, result) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) | ||
// go-aah/security source code and usage is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package acrypto | ||
|
||
import "fmt" | ||
|
||
// PasswordEncoder interface is used to encode and compare given hash and password | ||
// based chosen hashing type. Such as `bcrypt`, `scrypt`, `pbkdf2`, `sha1`, `sha256`, | ||
// `sha512` and `md5`. | ||
// | ||
// Currently `bcrypt` is supported by aah framework, remaining encoders are `upcoming`. | ||
// | ||
// Caution: If you're using an unsecure hashing, your application is exposed to security | ||
// issues. Consider using `bcrypt`, `scrypt`, or `pbkdf2`. Good read about | ||
// hashing security - https://crackstation.net/hashing-security.htm | ||
type PasswordEncoder interface { | ||
Compare(hash, password []byte) bool | ||
} | ||
|
||
// CreatePasswordEncoder method creates the instance of password encoder password, | ||
// based on given type. Currently `bcrypt` is supported. | ||
func CreatePasswordEncoder(etype string) (PasswordEncoder, error) { | ||
switch etype { | ||
case "bcrypt": | ||
return &BcryptEncoder{}, nil | ||
default: | ||
return nil, fmt.Errorf("acrypto: unsupported encoder type '%v'", etype) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) | ||
// go-aah/security source code and usage is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package acrypto | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"aahframework.org/test.v0/assert" | ||
) | ||
|
||
func TestCryptoPasswordEncoder(t *testing.T) { | ||
encoder, err := CreatePasswordEncoder("bcrypt") | ||
assert.Nil(t, err) | ||
assert.NotNil(t, encoder) | ||
|
||
result := encoder.Compare([]byte("$2y$10$2A4GsJ6SmLAMvDe8XmTam.MSkKojdobBVJfIU7GiyoM.lWt.XV3H6"), []byte("welcome123")) | ||
assert.True(t, result) | ||
|
||
result = encoder.Compare([]byte("$2y$10$2A4GsJ6SmLAMvDe8XmTam.MSkKojdobBVJfIU7GiyoM.lWt.XV3H6"), []byte("nomatch")) | ||
assert.False(t, result) | ||
|
||
// Unsupport password encoder type | ||
encoder, err = CreatePasswordEncoder("sha256") | ||
assert.NotNil(t, err) | ||
assert.True(t, strings.Contains(err.Error(), "acrypto: unsupported encoder type")) | ||
assert.Nil(t, encoder) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) | ||
// go-aah/security source code and usage is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package authc | ||
|
||
import ( | ||
"errors" | ||
"sync" | ||
|
||
"aahframework.org/config.v0" | ||
) | ||
|
||
var ( | ||
// ErrAuthenticatorIsNil error is returned when authenticator is nil in the auth scheme. | ||
ErrAuthenticatorIsNil = errors.New("security: authenticator is nil") | ||
|
||
// ErrAuthenticationFailed error is returned when user authentication fails; | ||
// such as subject password doesn't match, is-locked or is-Expired. | ||
ErrAuthenticationFailed = errors.New("security: authentication failed") | ||
|
||
// ErrSubjectNotExists error is returned when Subject is not exists in the application | ||
// datasource. Typically used by aah application. | ||
ErrSubjectNotExists = errors.New("security: subject not exists") | ||
|
||
authcInfoPool = &sync.Pool{New: func() interface{} { | ||
return &AuthenticationInfo{ | ||
Principals: make([]*Principal, 0), | ||
} | ||
}} | ||
) | ||
|
||
// Authenticator interface is implemented by user application to provide | ||
// authentication information during authentication process. | ||
type Authenticator interface { | ||
// Init method gets called by framework during an application start. | ||
Init(cfg *config.Config) error | ||
|
||
// GetAuthenticationInfo method gets called when authentication happens for | ||
// user provided credentials. | ||
GetAuthenticationInfo(authcToken *AuthenticationToken) (*AuthenticationInfo, error) | ||
} | ||
|
||
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | ||
// Package methods | ||
//___________________________________ | ||
|
||
// NewAuthenticationInfo method creates an `AuthenticationInfo` instance with zero | ||
// values. Then using this instance you fill-in user credential, principals, locked, | ||
// expried information. | ||
func NewAuthenticationInfo() *AuthenticationInfo { | ||
return authcInfoPool.Get().(*AuthenticationInfo) | ||
} | ||
|
||
// ReleaseAuthenticationInfo method resets instance and puts back to pool repurpose. | ||
func ReleaseAuthenticationInfo(authcInfo *AuthenticationInfo) { | ||
if authcInfo != nil { | ||
authcInfo.Reset() | ||
authcInfoPool.Put(authcInfo) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) | ||
// go-aah/security source code and usage is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package authc | ||
|
||
import "fmt" | ||
|
||
type ( | ||
// AuthenticationInfo represents a Subject's (aka user's) stored account | ||
// information relevant to the authentication/log-in process only. | ||
// | ||
// It is important to understand the difference between this interface and | ||
// the AuthenticationToken struct. AuthenticationInfo implementations represent | ||
// already-verified and stored account data, whereas an AuthenticationToken | ||
// represents data submitted for any given login attempt (which may or may not | ||
// successfully match the verified and stored account AuthenticationInfo). | ||
// | ||
// Because the act of authentication (log-in) is orthogonal to authorization | ||
// (access control), this struct is intended to represent only the account data | ||
// needed by aah framework during an authentication attempt. aah framework also | ||
// has a parallel AuthorizationInfo struct for use during the authorization | ||
// process that references access control data such as roles and permissions. | ||
AuthenticationInfo struct { | ||
Credential []byte | ||
IsLocked bool | ||
IsExpired bool | ||
Principals []*Principal | ||
} | ||
|
||
// Principal struct holds the principal associated with a corresponding Subject. | ||
// A principal is just a security term for an identifying attribute, such as a | ||
// username or user id or social security number or anything else that can be | ||
// considered an 'identifying' attribute for a Subject. | ||
Principal struct { | ||
Realm string | ||
Value string | ||
IsPrimary bool | ||
} | ||
) | ||
|
||
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | ||
// AuthenticationInfo methods | ||
//___________________________________ | ||
|
||
// PrimaryPrincipal method returns the primary Principal instance if principal | ||
// object has `IsPrimary` as true otherwise nil. | ||
// | ||
// Typically one principal is required for the subject aka user. | ||
func (a *AuthenticationInfo) PrimaryPrincipal() *Principal { | ||
for _, p := range a.Principals { | ||
if p.IsPrimary { | ||
return p | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// Merge method merges the given authentication information into existing | ||
// `AuthenticationInfo` instance. IsExpired and IsLocked values considered as latest | ||
// from the given object. | ||
func (a *AuthenticationInfo) Merge(oa *AuthenticationInfo) *AuthenticationInfo { | ||
a.Principals = append(a.Principals, oa.Principals...) | ||
a.IsExpired = oa.IsExpired | ||
a.IsLocked = oa.IsLocked | ||
return a | ||
} | ||
|
||
// Reset method reset the instance for repurpose. | ||
func (a *AuthenticationInfo) Reset() { | ||
a.Credential = nil | ||
a.Principals = make([]*Principal, 0) | ||
a.IsExpired = false | ||
a.IsLocked = false | ||
} | ||
|
||
// String method is stringer interface implementation. | ||
func (a *AuthenticationInfo) String() string { | ||
return fmt.Sprintf("AuthenticationInfo:: Principals%s, Credential: *******, IsLocked: %v, IsExpired: %v", | ||
a.Principals, a.IsLocked, a.IsExpired) | ||
} | ||
|
||
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | ||
// Principal methods | ||
//___________________________________ | ||
|
||
// String method is stringer interface implementation. | ||
func (p *Principal) String() string { | ||
return fmt.Sprintf("Realm: %v, Principal: %s, IsPrimary: %v", p.Realm, p.Value, p.IsPrimary) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) | ||
// go-aah/security source code and usage is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package authc | ||
|
||
import ( | ||
"testing" | ||
|
||
"aahframework.org/test.v0/assert" | ||
) | ||
|
||
func TestAuthcAuthenticationInfo(t *testing.T) { | ||
a1 := NewAuthenticationInfo() | ||
p1 := &Principal{Value: "user@sample.com", IsPrimary: true} | ||
p2 := &Principal{Value: "userid"} | ||
|
||
a1.Principals = append(a1.Principals, p1, p2) | ||
assert.False(t, a1.IsLocked) | ||
assert.False(t, a1.IsExpired) | ||
assert.Equal(t, "AuthenticationInfo:: Principals[Realm: , Principal: user@sample.com, IsPrimary: true Realm: , Principal: userid, IsPrimary: false], Credential: *******, IsLocked: false, IsExpired: false", a1.String()) | ||
|
||
p := a1.PrimaryPrincipal() | ||
assert.NotNil(t, p) | ||
assert.Equal(t, "user@sample.com", p.Value) | ||
assert.True(t, p.IsPrimary) | ||
assert.Equal(t, "Realm: , Principal: user@sample.com, IsPrimary: true", p.String()) | ||
|
||
assert.NotNil(t, a1.Principals) | ||
assert.True(t, len(a1.Principals) == 2) | ||
|
||
ReleaseAuthenticationInfo(a1) | ||
} | ||
|
||
func TestAuthcAuthenticationInfoMerge(t *testing.T) { | ||
a1 := NewAuthenticationInfo() | ||
a2 := NewAuthenticationInfo() | ||
a1.Principals = append(a1.Principals, &Principal{Value: "user@sample.com"}) | ||
a2.IsLocked = true | ||
a2.IsExpired = true | ||
|
||
a1.Merge(a2) | ||
assert.True(t, a1.IsLocked) | ||
assert.True(t, a1.IsExpired) | ||
assert.Nil(t, a1.PrimaryPrincipal()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) | ||
// go-aah/security source code and usage is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package authc | ||
|
||
import "fmt" | ||
|
||
// AuthenticationToken is an account's principals and supporting credentials | ||
// submitted by a user during an authentication attempt. | ||
// | ||
// The auth token is submitted to an Authenticator via the | ||
// GetAuthenticationInfo(authToken) method to get `AuthenticationInfo` for the | ||
// the authentication/log-in process. | ||
// | ||
// Common implementations of an AuthenticationToken would have username/password pairs, | ||
// auth token, or anything else you can think of. | ||
type AuthenticationToken struct { | ||
// Scheme denotes the authentication scheme. It is derived value. | ||
// For e.g.: form, basic, api, etc. | ||
Scheme string | ||
|
||
// Identity is an account username or principal or token. | ||
Identity string | ||
|
||
// Credential is an account or subject secret. | ||
Credential string | ||
} | ||
|
||
// String method is stringer interface implementation. | ||
func (a *AuthenticationToken) String() string { | ||
return fmt.Sprintf("AuthenticationToken:: Scheme: %v, Identity: %v, Credential: *******", a.Scheme, a.Identity) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) | ||
// go-aah/security source code and usage is governed by a MIT style | ||
// license that can be found in the LICENSE file. | ||
|
||
package authc | ||
|
||
import ( | ||
"testing" | ||
|
||
"aahframework.org/test.v0/assert" | ||
) | ||
|
||
func TestAuthcAuthenticationToken(t *testing.T) { | ||
authToken := &AuthenticationToken{ | ||
Scheme: "form", | ||
Identity: "jeeva", | ||
Credential: "welcome123", | ||
} | ||
|
||
assert.Equal(t, "AuthenticationToken:: Scheme: form, Identity: jeeva, Credential: *******", | ||
authToken.String()) | ||
} |
Oops, something went wrong.