Skip to content

Commit

Permalink
Merge pull request #7 for v0.6 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
jeevatkm committed Aug 1, 2017
2 parents 65baa01 + a964d72 commit c1052bc
Show file tree
Hide file tree
Showing 37 changed files with 2,563 additions and 66 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ go:

go_import_path: aahframework.org/security.v0

install:
- git config --global http.https://aahframework.org.followRedirects true
- go get -t -v ./...

script:
- bash <(curl -s https://aahframework.org/go-test)

Expand Down
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
# Security - aah framework
[![Build Status](https://travis-ci.org/go-aah/security.svg?branch=master)](https://travis-ci.org/go-aah/security) [![codecov](https://codecov.io/gh/go-aah/security/branch/master/graph/badge.svg)](https://codecov.io/gh/go-aah/security/branch/master) [![Go Report Card](https://goreportcard.com/badge/aahframework.org/security.v0)](https://goreportcard.com/report/aahframework.org/security.v0) [![Version](https://img.shields.io/badge/version-0.5-blue.svg)](https://github.com/go-aah/security/releases/latest) [![GoDoc](https://godoc.org/aahframework.org/security.v0?status.svg)](https://godoc.org/aahframework.org/security.v0) [![License](https://img.shields.io/github/license/go-aah/security.svg)](LICENSE)
[![Build Status](https://travis-ci.org/go-aah/security.svg?branch=master)](https://travis-ci.org/go-aah/security) [![codecov](https://codecov.io/gh/go-aah/security/branch/master/graph/badge.svg)](https://codecov.io/gh/go-aah/security/branch/master) [![Go Report Card](https://goreportcard.com/badge/aahframework.org/security.v0)](https://goreportcard.com/report/aahframework.org/security.v0) [![Version](https://img.shields.io/badge/version-0.6-blue.svg)](https://github.com/go-aah/security/releases/latest) [![GoDoc](https://godoc.org/aahframework.org/security.v0?status.svg)](https://godoc.org/aahframework.org/security.v0) [![License](https://img.shields.io/github/license/go-aah/security.svg)](LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@aahframework-55acee.svg)](https://twitter.com/aahframework)

***v0.5 [released](https://github.com/go-aah/security/releases/latest) and tagged on May 08, 2017***
***v0.6 [released](https://github.com/go-aah/security/releases/latest) and tagged on Aug 01, 2017***

Security library houses all the application security implementation (Session, Basic Auth, Token Auth, CORS, CSRF, Security Headers, etc.) by aah framework.

### Session - HTTP State Management

Features:
* Extensible session store interface (just key-value pair)
* HMAC Signed session data
* AES Encrypted session data
Security library houses all the application security implementation (Authentication, Authorization, Session Management, CORS, CSRF, Security Headers, etc.) by aah framework.

*`security` developed for aah framework. However, it's an independent library, can be used separately with any `Go` language project. Feel free to use it.*

Expand Down
18 changes: 18 additions & 0 deletions acrypto/bcrypt.go
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
}
20 changes: 20 additions & 0 deletions acrypto/bcrypt_test.go
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)
}
31 changes: 31 additions & 0 deletions acrypto/password_encoder.go
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)
}
}
30 changes: 30 additions & 0 deletions acrypto/password_encoder_test.go
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)
}
61 changes: 61 additions & 0 deletions authc/authc.go
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)
}
}
90 changes: 90 additions & 0 deletions authc/authentication_info.go
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)
}
46 changes: 46 additions & 0 deletions authc/authentication_info_test.go
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())
}
33 changes: 33 additions & 0 deletions authc/authentication_token.go
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)
}
22 changes: 22 additions & 0 deletions authc/authentication_token_test.go
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())
}
Loading

0 comments on commit c1052bc

Please sign in to comment.