diff --git a/.travis.yml b/.travis.yml index 0112e0a783b..816f82c09c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,8 @@ script: - hydra clients create --id foobar - hydra clients delete foobar # Test token on arbitrary endpoints - - curl --header "Authorization: bearer $(hydra token client)" http://localhost:4444/clients + - |- + curl --header "Authorization: bearer $(hydra token client)" http://localhost:4444/clients # Test token validation - hydra token validate $(hydra token client) diff --git a/README.md b/README.md index 77622080545..615b412b410 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,15 @@ [![HTTP API Documentation](https://img.shields.io/badge/docs-http%20api-blue.svg)](http://docs.hdyra.apiary.io/) [![Code Documentation](https://img.shields.io/badge/docs-godoc-blue.svg)](https://godoc.org/github.com/ory-am/hydra) -Hydra is a runnable server implementation of the OAuth 2.0 authorization framework and the OpenID Connect Core 1.0. Hydra acts as a middle-man infrastructure between your existing application and any OAuth 2.0 / OpenID Connect clients that consume your APIs. +Hydra is a runnable server implementation of the OAuth 2.0 authorization framework and the OpenID Connect Core 1.0. + +Hydra is different from [keycloak](https://github.com/keycloak/keycloak), [coreos/dex](https://github.com/coreos/dex), +[IdentityServer3](http://identityserver.io/), and others because those projects usually solve the whole authentication, +authorization, user and profile management stack. While they usually integrate with LDAP or other common standards, +they will not always be able to cover 100% of what you need. Hydra however externalises +the concern of authentication to the *consent app* and works with 2fa, retina scan, touch id, or your 5 years +old, legacy, PHP login app. Hydra acts as a middle-man infrastructure between your existing application and +any OAuth 2.0 / OpenID Connect clients that consume your APIs. Join our [newsletter](http://eepurl.com/bKT3N9) to stay on top of new developments. We answer basic support requests on [Google Groups](https://groups.google.com/forum/#!forum/ory-hydra/new) and [Gitter](https://gitter.im/ory-am/hydra) @@ -46,7 +54,10 @@ the access control SDK [Ladon](https://github.com/ory-am/ladon). - [HTTP API Documentation](#http-api-documentation) - [Command Line Documentation](#command-line-documentation) - [Develop](#develop) -- [Third-party libraries and projects](#third-party-libraries-and-projects) +- [Hydra Ecosystem](#hydra-ecosystem) + - [Libraries](#libraries) + - [Articles](#articles) +- [Testimonials](#testimonials) @@ -200,6 +211,23 @@ docker run --name some-rethink -d -p 8080:8080 -p 28015:28015 rethinkdb DATABASE_URL=rethinkdb://localhost:28015/hydra go run main.go host ``` -## Third-party libraries and projects +## Hydra Ecosystem + +This is a list of useful links for working with Hydra: + +### Libraries * [Hydra middleware for Gin](https://github.com/janekolszak/gin-hydra) +* [Exemplary consent app](https://github.com/ory-am/hydra-idp-react/) +* [Hydra NodeJS SDK](https://github.com/ory-am/hydra-js) +* [Identity Provider SDK for Go](https://github.com/janekolszak/idp) + +### Articles + +* [Creating an oauth2 custom lamda authorizer for use with Amazons (AWS) API Gateway using Hydra](https://blogs.edwardwilde.com/2017/01/12/creating-an-oauth2-custom-lamda-authorizer-for-use-with-amazons-aws-api-gateway-using-hydra/) +* Warning, Hydra has changed a lot since this article: [Hydra: Run your own Identity and Access Management service in <5 Minutes](https://blog.gopheracademy.com/advent-2015/hydra-auth/) + +## Testimonials + +Ping @arekkas on gitter if you want your project / company listed here. The testimonial is optional, +but we would really appreciate it. \ No newline at end of file diff --git a/config/backend_connections.go b/config/backend_connections.go index 1cbad76ddbf..00e2ec4452c 100644 --- a/config/backend_connections.go +++ b/config/backend_connections.go @@ -19,6 +19,7 @@ import ( r "gopkg.in/dancannon/gorethink.v2" "gopkg.in/redis.v5" "strings" + "runtime" ) type MemoryConnection struct{} @@ -53,9 +54,54 @@ func (c *SQLConnection) GetDatabase() *sqlx.DB { logrus.Fatalf("Could not connect to SQL: %s", err) } + + + maxConns := maxParallelism() * 2 + if v := c.URL.Query().Get("max_conns"); v != "" { + s, err := strconv.ParseInt(v, 10, 64) + if err != nil { + logrus.Warnf("max_conns value %s could not be parsed to int: %s", v, err) + } else { + maxConns = int(s) + } + } + + maxIdleConns := maxParallelism() + if v := c.URL.Query().Get("max_idle_conns"); v != "" { + s, err := strconv.ParseInt(v, 10, 64) + if err != nil { + logrus.Warnf("max_idle_conns value %s could not be parsed to int: %s", v, err) + } else { + maxIdleConns = int(s) + } + } + + maxConnLifetime := time.Duration(0) + if v := c.URL.Query().Get("max_conn_lifetime"); v != "" { + s, err := time.ParseDuration(v) + if err != nil { + logrus.Warnf("max_conn_lifetime value %s could not be parsed to int: %s", v, err) + } else { + maxConnLifetime = s + } + } + + c.db.SetMaxOpenConns(maxConns) + c.db.SetMaxIdleConns(maxIdleConns) + c.db.SetConnMaxLifetime(maxConnLifetime) + return c.db } +func maxParallelism() int { + maxProcs := runtime.GOMAXPROCS(0) + numCPU := runtime.NumCPU() + if maxProcs < numCPU { + return maxProcs + } + return numCPU +} + type RethinkDBConnection struct { session *r.Session URL *url.URL diff --git a/jwk/manager_sql.go b/jwk/manager_sql.go index 3fa5d6110c3..5b0954a049e 100644 --- a/jwk/manager_sql.go +++ b/jwk/manager_sql.go @@ -84,11 +84,17 @@ func (m *SQLManager) AddKeySet(set string, keys *jose.JsonWebKeySet) error { for _, key := range keys.Keys { out, err := json.Marshal(key) if err != nil { + if re := tx.Rollback(); re != nil { + return errors.Wrap(err, re.Error()) + } return errors.WithStack(err) } encrypted, err := m.Cipher.Encrypt(out) if err != nil { + if re := tx.Rollback(); re != nil { + return errors.Wrap(err, re.Error()) + } return errors.WithStack(err) } @@ -98,11 +104,17 @@ func (m *SQLManager) AddKeySet(set string, keys *jose.JsonWebKeySet) error { Version: 0, Key: encrypted, }); err != nil { + if re := tx.Rollback(); re != nil { + return errors.Wrap(err, re.Error()) + } return errors.WithStack(err) } } if err := tx.Commit(); err != nil { + if re := tx.Rollback(); re != nil { + return errors.Wrap(err, re.Error()) + } return errors.WithStack(err) } return nil diff --git a/oauth2/fosite_store_redis.go b/oauth2/fosite_store_redis.go index 1c7740c7be7..186855c8e4c 100644 --- a/oauth2/fosite_store_redis.go +++ b/oauth2/fosite_store_redis.go @@ -24,7 +24,7 @@ type redisSchema struct { RequestedAt time.Time `json:"requestedAt"` Client *client.Client `json:"client"` Scopes fosite.Arguments `json:"scopes"` - GrantedScopes fosite.Arguments `json:"grantedScopes""` + GrantedScopes fosite.Arguments `json:"grantedScopes"` Form url.Values `json:"form"` Session json.RawMessage `json:"session"` } diff --git a/warden/group/manager_sql.go b/warden/group/manager_sql.go index f7f12b5e6d5..0770c135ccf 100644 --- a/warden/group/manager_sql.go +++ b/warden/group/manager_sql.go @@ -84,11 +84,17 @@ func (m *SQLManager) AddGroupMembers(group string, subjects []string) error { } for _, subject := range subjects { if _, err := tx.Exec(m.DB.Rebind("INSERT INTO hydra_warden_group_member (group_id, member) VALUES (?, ?)"), group, subject); err != nil { + if re := tx.Rollback(); re != nil { + return errors.Wrap(err, re.Error()) + } return errors.WithStack(err) } } if err := tx.Commit(); err != nil { + if re := tx.Rollback(); re != nil { + return errors.Wrap(err, re.Error()) + } return errors.Wrap(err, "Could not commit transaction") } return nil @@ -101,11 +107,17 @@ func (m *SQLManager) RemoveGroupMembers(group string, subjects []string) error { } for _, subject := range subjects { if _, err := m.DB.Exec(m.DB.Rebind("DELETE FROM hydra_warden_group_member WHERE member=? AND group_id=?"), subject, group); err != nil { + if re := tx.Rollback(); re != nil { + return errors.Wrap(err, re.Error()) + } return errors.WithStack(err) } } if err := tx.Commit(); err != nil { + if re := tx.Rollback(); re != nil { + return errors.Wrap(err, re.Error()) + } return errors.Wrap(err, "Could not commit transaction") } return nil