Skip to content

Commit

Permalink
Merge pull request #6 from ory-am/unstaged
Browse files Browse the repository at this point in the history
Single Sign On
  • Loading branch information
arekkas committed Nov 1, 2015
2 parents 1ac0a23 + 6f365ff commit 7c04c61
Show file tree
Hide file tree
Showing 37 changed files with 1,655 additions and 621 deletions.
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,25 @@ Hydra is written in go and backed by PostgreSQL or any implementation of [accoun

## What is Hydra?

Authentication, authorization and user account management are always lengthy to plan and implement. If you're building an app in Go with
a micro service architecture in mind, you have come the right way.
Authentication, authorization and user account management are always lengthy to plan and implement. If you're building a micro service app
in need of these three, you are in the right place.

## Features

Hydra is a RESTful service providing you with things like:

* Account management: Sign up, settings, password recovery
* Authorization & Policy management backed by [ladon](https://github.com/ory-am/ladon)
* Authentication: Sign in, OAuth2 Consumer (Google Account, Facebook Login, ...)
* OAuth2 Provider: Hydra implements OAuth2 as specified at [rfc6749](http://tools.ietf.org/html/rfc6749) and [draft-ietf-oauth-v2-10](http://tools.ietf.org/html/draft-ietf-oauth-v2-10)
* **Account Management**: Sign up, settings, password recovery
* **Access Control / Policy Management** backed by [ladon](https://github.com/ory-am/ladon)
* Hydra comes with a rich set of **OAuth2** features:
* Hydra implements OAuth2 as specified at [rfc6749](http://tools.ietf.org/html/rfc6749) and [draft-ietf-oauth-v2-10](http://tools.ietf.org/html/draft-ietf-oauth-v2-10).
* Hydra uses self-contained Acccess Tokens as suggessted in [rfc6794#section-1.4](http://tools.ietf.org/html/rfc6749#section-1.4) by issuing JSON Web Tokens as specified at
[https://tools.ietf.org/html/rfc7519](https://tools.ietf.org/html/rfc7519) with [RSASSA-PKCS1-v1_5 with the SHA-256](https://tools.ietf.org/html/rfc7519#section-8) supported reducing overhead significantly. Access
* Hydra implements **OAuth2 Introspection** as specified in [rfc7662](https://tools.ietf.org/html/rfc7662)

To keep things sane, Hydra uses JWT with the [ECDSA signature algorithm](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm)
for bearer based http authorization.
## Good to know

Hydra does not provide a dedicated login page, instead a third party service has to act as a login page
and authenticating the user via the [password grant type](https://aaronparecki.com/articles/2012/07/29/1/oauth2-simplified#others).

## Attributions

Expand Down
2 changes: 1 addition & 1 deletion account/account.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package account

import "errors"
import "github.com/go-errors/errors"

var ErrNotFound = errors.New("Not found")

Expand Down
10 changes: 4 additions & 6 deletions account/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import (
"testing"
)

var cases = [][]string{
[]string{"1", "foo@bar", "secret"},
[]string{"2", "baz@foo", "top secret"},
}

func TestAccountCases(t *testing.T) {
for _, c := range cases {
for _, c := range [][]string{
[]string{"1", "foo@bar", "secret"},
[]string{"2", "baz@foo", "top secret"},
} {
a := &DefaultAccount{c[0], c[1], c[2], `{"foo": "bar"}`}
assert.Equal(t, c[0], a.GetID())
assert.Equal(t, c[1], a.GetEmail())
Expand Down
2 changes: 1 addition & 1 deletion account/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/gorilla/mux"
. "github.com/ory-am/hydra/account"
hydcon "github.com/ory-am/hydra/context"
"github.com/ory-am/hydra/handler/middleware"
"github.com/ory-am/hydra/middleware"
"github.com/pborman/uuid"
"golang.org/x/net/context"
"net/http"
Expand Down
43 changes: 22 additions & 21 deletions account/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
"github.com/dgrijalva/jwt-go"
"github.com/gorilla/mux"
"github.com/ory-am/dockertest"
"github.com/ory-am/hydra/account"
hydra "github.com/ory-am/hydra/account/postgres"
hcon "github.com/ory-am/hydra/context"
"github.com/ory-am/hydra/handler/middleware"
"github.com/ory-am/hydra/hash"
hjwt "github.com/ory-am/hydra/jwt"
"github.com/ory-am/hydra/middleware"
"github.com/ory-am/ladon/policy"
"github.com/pborman/uuid"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -64,7 +64,7 @@ type payload struct {
}

type test struct {
subject account.Account
subject string
token *jwt.Token
policies []policy.Policy
createData *payload
Expand All @@ -75,35 +75,35 @@ type test struct {

var cases = []*test{
&test{
&account.DefaultAccount{},
"peter",
&jwt.Token{Valid: false},
[]policy.Policy{},
&payload{},
http.StatusUnauthorized, 0, 0,
},
&test{
&account.DefaultAccount{},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{},
&payload{},
http.StatusForbidden, 0, 0,
},
&test{
&account.DefaultAccount{},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{},
&payload{},
http.StatusForbidden, 0, 0,
},
&test{
&account.DefaultAccount{ID: "max"},
"max",
&jwt.Token{Valid: true},
[]policy.Policy{},
&payload{},
http.StatusForbidden, 0, 0,
},
&test{
&account.DefaultAccount{ID: "max"},
"max",
&jwt.Token{Valid: true},
[]policy.Policy{
&policy.DefaultPolicy{"", "", []string{"peter"}, policy.AllowAccess, []string{"/users"}, []string{"create"}},
Expand All @@ -112,7 +112,7 @@ var cases = []*test{
http.StatusForbidden, 0, 0,
},
&test{
&account.DefaultAccount{ID: "peter"},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{
&policy.DefaultPolicy{"", "", []string{"peter"}, policy.AllowAccess, []string{"/users"}, []string{"create"}},
Expand All @@ -121,7 +121,7 @@ var cases = []*test{
http.StatusBadRequest, 0, 0,
},
&test{
&account.DefaultAccount{ID: "peter"},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{
&policy.DefaultPolicy{"", "", []string{"peter"}, policy.AllowAccess, []string{"/users"}, []string{"create"}},
Expand All @@ -130,7 +130,7 @@ var cases = []*test{
http.StatusBadRequest, 0, 0,
},
&test{
&account.DefaultAccount{ID: "peter"},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{
&policy.DefaultPolicy{"", "", []string{"peter"}, policy.AllowAccess, []string{"/users"}, []string{"create"}},
Expand All @@ -139,7 +139,7 @@ var cases = []*test{
http.StatusBadRequest, 0, 0,
},
&test{
&account.DefaultAccount{ID: "peter"},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{
&policy.DefaultPolicy{"", "", []string{"peter"}, policy.AllowAccess, []string{"/users"}, []string{"create"}},
Expand All @@ -148,7 +148,7 @@ var cases = []*test{
http.StatusBadRequest, 0, 0,
},
&test{
&account.DefaultAccount{ID: "peter"},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{
&policy.DefaultPolicy{"", "", []string{"peter"}, policy.AllowAccess, []string{"/users"}, []string{"create"}},
Expand All @@ -157,7 +157,7 @@ var cases = []*test{
http.StatusBadRequest, 0, 0,
},
&test{
&account.DefaultAccount{ID: "peter"},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{
&policy.DefaultPolicy{"", "", []string{"peter"}, policy.AllowAccess, []string{"/users"}, []string{"create"}},
Expand All @@ -166,7 +166,7 @@ var cases = []*test{
http.StatusOK, http.StatusForbidden, http.StatusForbidden,
},
&test{
&account.DefaultAccount{ID: "peter"},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{
&policy.DefaultPolicy{"", "", []string{"peter"}, policy.AllowAccess, []string{"/users"}, []string{"create"}},
Expand All @@ -176,7 +176,7 @@ var cases = []*test{
http.StatusOK, http.StatusOK, http.StatusForbidden,
},
&test{
&account.DefaultAccount{ID: "peter"},
"peter",
&jwt.Token{Valid: true},
[]policy.Policy{
&policy.DefaultPolicy{"", "", []string{"peter"}, policy.AllowAccess, []string{"/users"}, []string{"create"}},
Expand All @@ -191,7 +191,8 @@ var cases = []*test{
func mock(c *test) func(h hcon.ContextHandler) hcon.ContextHandler {
return func(h hcon.ContextHandler) hcon.ContextHandler {
return hcon.ContextHandlerFunc(func(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
ctx = hcon.NewContextFromAuthValues(ctx, c.subject, c.token, c.policies)
claims := hjwt.NewClaimsCarrier(uuid.New(), "hydra", c.subject, "tests", time.Now(), time.Now())
ctx = hcon.NewContextFromAuthValues(ctx, claims, c.token, c.policies)
h.ServeHTTPContext(ctx, rw, req)
})
}
Expand All @@ -208,7 +209,7 @@ func TestCreateGetDelete(t *testing.T) {
if http.StatusOK == res.Code || http.StatusAccepted == res.Code {
finish(testCase, res)
} else if res.Code == http.StatusNotFound {
log.Printf("404 case %d: %s", k, testCase.createData.ID)
t.Logf("404 case %d: %s", k, testCase.createData.ID)
}
}

Expand All @@ -223,7 +224,7 @@ func TestCreateGetDelete(t *testing.T) {
}, func(c *test, res *httptest.ResponseRecorder) {
code = res.Code
result := res.Body.Bytes()
log.Printf("POST case %d /users: %s", k, result)
t.Logf("POST case %d /users: %s", k, result)
require.Nil(t, json.Unmarshal(result, &p))
assert.Equal(t, c.createData.Email, p.Email)
assert.Equal(t, c.createData.Data, p.Data)
Expand All @@ -240,7 +241,7 @@ func TestCreateGetDelete(t *testing.T) {
}, func(c *test, res *httptest.ResponseRecorder) {
code = res.Code
result := res.Body.Bytes()
log.Printf("GET case %d /users/%s: %s", k, p.ID, result)
t.Logf("GET case %d /users/%s: %s", k, p.ID, result)
require.Nil(t, json.Unmarshal(result, &p))
assert.Equal(t, c.createData.Email, p.Email)
assert.Equal(t, c.createData.Data, p.Data)
Expand All @@ -256,7 +257,7 @@ func TestCreateGetDelete(t *testing.T) {
return req
}, func(c *test, res *httptest.ResponseRecorder) {
code = res.Code
log.Printf("DELETE case %d /users/%s", k, p.ID)
t.Logf("DELETE case %d /users/%s", k, p.ID)
})

if code != http.StatusAccepted {
Expand Down
4 changes: 2 additions & 2 deletions account/postgres/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package postgres

import (
"database/sql"
"errors"
"github.com/go-errors/errors"
"github.com/ory-am/hydra/account"
"github.com/ory-am/hydra/hash"
"log"
)

const accountSchema = `CREATE TABLE account (
const accountSchema = `CREATE TABLE IF NOT EXISTS account (
id text NOT NULL PRIMARY KEY,
email text NOT NULL UNIQUE,
password text NOT NULL,
Expand Down
15 changes: 6 additions & 9 deletions account/postgres/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,16 @@ func TestMain(m *testing.M) {
}

func TestCreateAndGetCases(t *testing.T) {
type tc struct {
for _, c := range []struct {
data []string
extra string
pass bool
find bool
}
cases := []tc{
tc{[]string{"1", "1@bar", "secret"}, `{"foo": "bar"}`, true, true},
tc{[]string{"1", "1@foo", "secret"}, `{"foo": "bar"}`, false, true},
tc{[]string{"2", "1@bar", "secret"}, `{"foo": "bar"}`, false, false},
}

for _, c := range cases {
}{
{[]string{"1", "1@bar", "secret"}, `{"foo": "bar"}`, true, true},
{[]string{"1", "1@foo", "secret"}, `{"foo": "bar"}`, false, true},
{[]string{"2", "1@bar", "secret"}, `{"foo": "bar"}`, false, false},
} {
result, err := store.Create(c.data[0], c.data[1], c.data[2], c.extra)
if c.pass {
assert.Nil(t, err)
Expand Down
1 change: 0 additions & 1 deletion account/storage.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package account


type Storage interface {
Create(id, email, password string, data string) (Account, error)

Expand Down
Loading

0 comments on commit 7c04c61

Please sign in to comment.