Skip to content

Commit

Permalink
Removes policy, warden and groups from this project
Browse files Browse the repository at this point in the history
We have learned a lot over the last year in terms of how ORY Hydra is being used. Initially, we wanted to avoid the problems facing popular databases like MongoDB or others, which did not include authentication for their management APIs.

For this reason, the Warden API was born and primarily used internally and exposed via HTTP. We learned that access control policies are well received, but also add additional complexity to understanding the software. While we firmly believe that these policies implement best practices for access control in complex systems, we do understand that they add a barrier to getting started with ORY Hydra.

For this reason we are planning on moving the Warden API from this project to ORY Oathkeeper or potentially it's own server. We would add a migration path for existing policy definitions to the new service. The default docker image would combine the services in such a way, that ORY Hydra is protected. We would additionally have an (insecure) docker image without authentication which can be used for testing.

This also opens up the possibility of having more access control mechanisms than access control policies. For example, we can add ACL and RBAC and other mechanisms too.

First I think it makes good sense to move this functionality into a separate service and remove the warden calls internally completely. The reason being that not everyone wants to rely on Hydra's access control. Sometimes it's enough to use a gateway in front and require e.g. an API key for management or whatever. New adopters are always baffled by complexity involved with policies and scopes. Removing that from the core could really help. The user survey has also shown that this stuff is quite complex to grasp.

The idea is to have a separate service which is basically ladon as a HTTP API. I think it makes sense to add some functionality to resolve access tokens so it would basically be very similar to the current warden API - probably even equal. There would definitely be some backup mode where hydra's database tables and migrations are used as to make migration as easy as possible.

Then, we would ship docker images and example set ups where different configurations are shown. One of the configurations would be the current one, so basically what we have now in hydra but with the three services combined in one image.

Closes ory#807
  • Loading branch information
arekkas authored and arekkas committed Apr 29, 2018
1 parent ab5484b commit 3d0bf0b
Show file tree
Hide file tree
Showing 218 changed files with 190 additions and 18,951 deletions.
179 changes: 0 additions & 179 deletions client/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,18 @@ package client

import (
"encoding/json"
"fmt"
"net/http"

"github.com/julienschmidt/httprouter"
"github.com/ory/herodot"
"github.com/ory/hydra/firewall"
"github.com/ory/hydra/rand/sequence"
"github.com/ory/ladon"
"github.com/ory/pagination"
"github.com/pkg/errors"
)

type Handler struct {
Manager Manager
H herodot.Writer
W firewall.Firewall
ResourcePrefix string
}

Expand Down Expand Up @@ -79,16 +75,6 @@ func (h *Handler) SetRoutes(r *httprouter.Router) {
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.
//
// The subject making the request needs to be assigned to a policy containing:
//
// ```
// {
// "resources": ["rn:hydra:clients"],
// "actions": ["create"],
// "effect": "allow"
// }
// ```
//
// Additionally, the context key "owner" is set to the owner of the client, allowing policies such as:
//
// ```
Expand All @@ -108,34 +94,19 @@ func (h *Handler) SetRoutes(r *httprouter.Router) {
//
// Schemes: http, https
//
// Security:
// oauth2: hydra.clients
//
// Responses:
// 200: oAuth2Client
// 401: genericError
// 403: genericError
// 500: genericError
func (h *Handler) Create(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var c Client
var ctx = r.Context()

if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
h.H.WriteError(w, r, errors.WithStack(err))
return
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: h.PrefixResource(ClientsResource),
Action: "create",
Context: map[string]interface{}{
"owner": c.Owner,
},
}, Scope); err != nil {
h.H.WriteError(w, r, err)
return
}

if len(c.Secret) == 0 {
secret, err := sequence.RuneSequence(12, []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-.~"))
if err != nil {
Expand Down Expand Up @@ -171,27 +142,6 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request, _ httprouter.Pa
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.
//
// The subject making the request needs to be assigned to a policy containing:
//
// ```
// {
// "resources": ["rn:hydra:clients"],
// "actions": ["update"],
// "effect": "allow"
// }
// ```
//
// Additionally, the context key "owner" is set to the owner of the client, allowing policies such as:
//
// ```
// {
// "resources": ["rn:hydra:clients"],
// "actions": ["update"],
// "effect": "allow",
// "conditions": { "owner": { "type": "EqualsSubjectCondition" } }
// }
// ```
//
// Consumes:
// - application/json
//
Expand All @@ -200,40 +150,19 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request, _ httprouter.Pa
//
// Schemes: http, https
//
// Security:
// oauth2: hydra.clients
//
// Responses:
// 200: oAuth2Client
// 401: genericError
// 403: genericError
// 500: genericError
func (h *Handler) Update(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var c Client
var ctx = r.Context()

if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
h.H.WriteError(w, r, errors.WithStack(err))
return
}

o, err := h.Manager.GetConcreteClient(ps.ByName("id"))
if err != nil {
h.H.WriteError(w, r, err)
return
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: h.PrefixResource(ClientsResource),
Action: "update",
Context: ladon.Context{
"owner": o.Owner,
},
}, Scope); err != nil {
h.H.WriteError(w, r, err)
return
}

var secret string
if len(c.Secret) > 0 && len(c.Secret) < 6 {
h.H.WriteError(w, r, errors.New("The client secret must be at least 6 characters long"))
Expand All @@ -260,16 +189,6 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request, ps httprouter.P
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.
//
// The subject making the request needs to be assigned to a policy containing:
//
// ```
// {
// "resources": ["rn:hydra:clients"],
// "actions": ["get"],
// "effect": "allow"
// }
// ```
//
// Consumes:
// - application/json
//
Expand All @@ -278,25 +197,12 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request, ps httprouter.P
//
// Schemes: http, https
//
// Security:
// oauth2: hydra.clients
//
// Responses:
// 200: oAuth2ClientList
// 401: genericError
// 403: genericError
// 500: genericError
func (h *Handler) List(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var ctx = r.Context()

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: h.PrefixResource(ClientsResource),
Action: "get",
}, Scope); err != nil {
h.H.WriteError(w, r, err)
return
}

limit, offset := pagination.Parse(r, 100, 0, 500)
c, err := h.Manager.GetClients(limit, offset)
if err != nil {
Expand All @@ -323,26 +229,6 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request, ps httprouter.Par
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.
//
// The subject making the request needs to be assigned to a policy containing:
//
// ```
// {
// "resources": ["rn:hydra:clients:<some-id>"],
// "actions": ["get"],
// "effect": "allow"
// }
// ```
//
// Additionally, the context key "owner" is set to the owner of the client, allowing policies such as:
//
// ```
// {
// "resources": ["rn:hydra:clients:<some-id>"],
// "actions": ["get"],
// "effect": "allow",
// "conditions": { "owner": { "type": "EqualsSubjectCondition" } }
// }
// ```
//
// Consumes:
// - application/json
Expand All @@ -352,39 +238,16 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request, ps httprouter.Par
//
// Schemes: http, https
//
// Security:
// oauth2: hydra.clients
//
// Responses:
// 200: oAuth2Client
// 401: genericError
// 403: genericError
// 500: genericError
func (h *Handler) Get(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var ctx = r.Context()
var id = ps.ByName("id")

c, err := h.Manager.GetConcreteClient(id)
if err != nil {
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: fmt.Sprintf(h.PrefixResource(ClientResource), id),
Action: "get",
}, Scope); err != nil {
h.H.WriteError(w, r, err)
return
}

h.H.WriteError(w, r, err)
return
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: fmt.Sprintf(h.PrefixResource(ClientResource), id),
Action: "get",
Context: ladon.Context{
"owner": c.GetOwner(),
},
}, Scope); err != nil {
h.H.WriteError(w, r, err)
return
}
Expand All @@ -401,27 +264,6 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request, ps httprouter.Para
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.
//
// The subject making the request needs to be assigned to a policy containing:
//
// ```
// {
// "resources": ["rn:hydra:clients:<some-id>"],
// "actions": ["delete"],
// "effect": "allow"
// }
// ```
//
// Additionally, the context key "owner" is set to the owner of the client, allowing policies such as:
//
// ```
// {
// "resources": ["rn:hydra:clients:<some-id>"],
// "actions": ["delete"],
// "effect": "allow",
// "conditions": { "owner": { "type": "EqualsSubjectCondition" } }
// }
// ```
//
// Consumes:
// - application/json
//
Expand All @@ -430,35 +272,14 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request, ps httprouter.Para
//
// Schemes: http, https
//
// Security:
// oauth2: hydra.clients
//
// Responses:
// 204: emptyResponse
// 401: genericError
// 403: genericError
// 500: genericError
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var ctx = r.Context()
var id = ps.ByName("id")

c, err := h.Manager.GetConcreteClient(id)
if err != nil {
h.H.WriteError(w, r, err)
return
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: fmt.Sprintf(h.PrefixResource(ClientResource), id),
Action: "delete",
Context: ladon.Context{
"owner": c.GetOwner(),
},
}, Scope); err != nil {
h.H.WriteError(w, r, err)
return
}

if err := h.Manager.DeleteClient(id); err != nil {
h.H.WriteError(w, r, err)
return
Expand Down
14 changes: 0 additions & 14 deletions client/sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,9 @@ import (
"testing"

"github.com/julienschmidt/httprouter"
"github.com/ory/fosite"
"github.com/ory/herodot"
"github.com/ory/hydra/client"
"github.com/ory/hydra/compose"
hydra "github.com/ory/hydra/sdk/go/hydra/swagger"
"github.com/ory/ladon"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -56,26 +53,15 @@ func createTestClient(prefix string) hydra.OAuth2Client {

func TestClientSDK(t *testing.T) {
manager := client.NewMemoryManager(nil)

localWarden, httpClient := compose.NewMockFirewall("foo", "alice", fosite.Arguments{client.Scope}, &ladon.DefaultPolicy{
ID: "1",
Subjects: []string{"alice"},
Resources: []string{"rn:hydra:clients<.*>"},
Actions: []string{"create", "get", "delete", "update"},
Effect: ladon.AllowAccess,
})

handler := &client.Handler{
Manager: manager,
H: herodot.NewJSONWriter(nil),
W: localWarden,
}

router := httprouter.New()
handler.SetRoutes(router)
server := httptest.NewServer(router)
c := hydra.NewOAuth2ApiWithBasePath(server.URL)
c.Configuration.Transport = httpClient.Transport

t.Run("case=client is created and updated", func(t *testing.T) {
createClient := createTestClient("")
Expand Down
4 changes: 0 additions & 4 deletions cmd/cli/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,18 @@ import (

type Handler struct {
Clients *ClientHandler
Policies *PolicyHandler
Keys *JWKHandler
Warden *IntrospectionHandler
Token *TokenHandler
Groups *GroupHandler
Migration *MigrateHandler
}

func NewHandler(c *config.Config) *Handler {
return &Handler{
Clients: newClientHandler(c),
Policies: newPolicyHandler(c),
Keys: newJWKHandler(c),
Warden: newIntrospectionHandler(c),
Token: newTokenHandler(c),
Groups: newGroupHandler(c),
Migration: newMigrateHandler(c),
}
}
Loading

0 comments on commit 3d0bf0b

Please sign in to comment.