Skip to content

Commit

Permalink
Update to Cerbos 0.3.0 (#8)
Browse files Browse the repository at this point in the history
* Add licence header

* Update library version
charithe authored Jul 16, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent fbbf6bc commit a1649de
Showing 10 changed files with 314 additions and 123 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,6 +4,26 @@ Securing a REST API with Cerbos
This project demonstrates how to secure a REST API using Cerbos policies. It also shows how to run Cerbos as a sidecar.


How it works
------------

HTTP middleware checks the username and password sent with each request against the user database and builds a Cerbos principal object containing roles and attributes.

```go
principal := cerbos.NewPrincipal(username).
WithRoles(record.Roles...).
WithAttr("aisles", record.Aisles).
WithAttr("ipAddress", r.RemoteAddr)
```

Checking access is as simple as making a call to Cerbos PDP.

```go
resource := cerbos.NewResource("inventory", item.ID).WithAttr("aisle", item.Aisle)
allowed, err := cerbos.IsAllowed(ctx, principal, resource, "DELETE")
```


The Store API
-------------

@@ -277,3 +297,11 @@ curl -i -u bella:bellasStrongPassword -XDELETE http://localhost:9999/backoffice/
}
```
</details>


Get help
--------

- Visit the [Cerbos website](https://cerbos.dev)
- [Join the Cerbos community on Slack](http://go.cerbos.io/slack)
- Email us at [email protected]
2 changes: 1 addition & 1 deletion cerbos/start_server.sh
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONTAINER_IMG=${CONTAINER_IMG:-"pkg.cerbos.dev/containers/cerbos"}
CONTAINER_TAG=${CONTAINER_TAG:-"0.2.1"}
CONTAINER_TAG=${CONTAINER_TAG:-"0.3.0"}

docker run -i -t -p 3592:3592 -p 3593:3593 \
-v ${SCRIPT_DIR}/policies:/policies \
1 change: 1 addition & 0 deletions db/inventory.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2021 Zenauth Ltd.
// SPDX-License-Identifier: Apache-2.0

package db

1 change: 1 addition & 0 deletions db/orders.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2021 Zenauth Ltd.
// SPDX-License-Identifier: Apache-2.0

package db

1 change: 1 addition & 0 deletions db/users.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2021 Zenauth Ltd.
// SPDX-License-Identifier: Apache-2.0

package db

4 changes: 2 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
version: "3.9"
services:
cerbos:
image: pkg.cerbos.dev/containers/cerbos:0.2.1
image: pkg.cerbos.dev/containers/cerbos:0.3.0
command: ["server", "--config=/data/config.yaml"]
volumes:
- ./cerbos:/data
- shared-tmpfs:/sock
demo:
image: pkg.cerbos.dev/containers/demo-rest:0.0.2
image: pkg.cerbos.dev/containers/demo-rest:0.1.0
command: ["-cerbos=unix:/sock/cerbos-grpc.sock", "-listen=:9999"]
ports:
- 9999:9999
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ module github.com/cerbos/demo-rest
go 1.16

require (
github.com/cerbos/cerbos v0.0.2
github.com/cerbos/cerbos v0.3.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
354 changes: 254 additions & 100 deletions go.sum

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2021 Zenauth Ltd.
// SPDX-License-Identifier: Apache-2.0

package main

43 changes: 24 additions & 19 deletions service/service.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2021 Zenauth Ltd.
// SPDX-License-Identifier: Apache-2.0

package service

@@ -18,9 +19,14 @@ import (
"golang.org/x/crypto/bcrypt"
)

type principalKeyType struct{}
type authCtxKeyType struct{}

var principalKey = principalKeyType{}
var authCtxKey = authCtxKeyType{}

type authContext struct {
username string
principal *cerbos.Principal
}

const (
inventoryResource = "inventory"
@@ -91,13 +97,13 @@ func authenticationMiddleware(next http.Handler) http.Handler {
// Get the basic auth credentials from the request.
user, password, ok := r.BasicAuth()
if ok {
// check the password and retrieve the user record.
principal, err := retrievePrincipal(user, password, r)
// check the password and retrieve the auth context.
authCtx, err := buildAuthContext(user, password, r)
if err != nil {
log.Printf("Failed to authenticate user [%s]: %v", user, err)
} else {
// Add the retrieved principal to the context.
ctx := context.WithValue(r.Context(), principalKey, principal)
ctx := context.WithValue(r.Context(), authCtxKey, authCtx)
next.ServeHTTP(w, r.WithContext(ctx))

return
@@ -110,8 +116,8 @@ func authenticationMiddleware(next http.Handler) http.Handler {
})
}

// retrievePrincipal verifies the username and password and returns a new principal object.
func retrievePrincipal(username, password string, r *http.Request) (*cerbos.Principal, error) {
// buildAuthContext verifies the username and password and returns a new authContext object.
func buildAuthContext(username, password string, r *http.Request) (*authContext, error) {
// Lookup the user from the database.
record, err := db.LookupUser(r.Context(), username)
if err != nil {
@@ -129,17 +135,17 @@ func retrievePrincipal(username, password string, r *http.Request) (*cerbos.Prin
WithAttr("aisles", record.Aisles).
WithAttr("ipAddress", r.RemoteAddr)

return principal, nil
return &authContext{username: username, principal: principal}, nil
}

// isAllowed is a utility function to check each action against a Cerbos policy.
func (s *Service) isAllowed(ctx context.Context, resource *cerbos.Resource, action string) bool {
principal := getPrincipal(ctx)
if principal == nil {
authCtx := getAuthContext(ctx)
if authCtx == nil {
return false
}

allowed, err := s.cerbos.IsAllowed(ctx, principal, resource, action)
allowed, err := s.cerbos.IsAllowed(ctx, authCtx.principal, resource, action)
if err != nil {
log.Printf("ERROR: %v", err)
return false
@@ -148,14 +154,14 @@ func (s *Service) isAllowed(ctx context.Context, resource *cerbos.Resource, acti
return allowed
}

// getPrincipal retrieves the principal stored in the context by the authentication middleware.
func getPrincipal(ctx context.Context) *cerbos.Principal {
p := ctx.Value(principalKey)
if p == nil {
// getAuthContext retrieves the principal stored in the context by the authentication middleware.
func getAuthContext(ctx context.Context) *authContext {
ac := ctx.Value(authCtxKey)
if ac == nil {
return nil
}

return p.(*cerbos.Principal)
return ac.(*authContext)
}

func (s *Service) handleOrderCreate(w http.ResponseWriter, r *http.Request) {
@@ -174,8 +180,8 @@ func (s *Service) handleOrderCreate(w http.ResponseWriter, r *http.Request) {
return
}

principal := getPrincipal(r.Context())
orderID := s.orders.Create(principal.Id, order)
authCtx := getAuthContext(r.Context())
orderID := s.orders.Create(authCtx.username, order)

writeJSON(w, http.StatusCreated, struct {
OrderID uint64 `json:"orderID"`
@@ -414,7 +420,6 @@ func (s *Service) handleInventoryPick(w http.ResponseWriter, r *http.Request) {
}

resource := toInventoryResource(record).WithAttr("pickQuantity", pickQty)
fmt.Printf("%+v\n", resource)
if !s.isAllowed(r.Context(), resource, "PICK") {
writeMessage(w, http.StatusForbidden, "Operation not allowed")
return

0 comments on commit a1649de

Please sign in to comment.