This example demonstrates how to incorporate the trapperkeeper-authorization
service into a simple Ring app. This is based loosely upon the
[ring_app example] (https://github.com/puppetlabs/trapperkeeper-webserver-jetty9/tree/master/examples/ring_app)
in the trapperkeeper-webserver-jetty9 project. See that example for more
information on the use of the jetty9-service
and the Jetty web server
integration with Ring.
This example includes a simple set of default authorization rules which can be
used to protect access to the /hello
endpoint that is handled by the service.
All code needed to execute this example is located in ./src/examples/ring_app
.
The Clojure code is contained in the ring_app.clj
file.
To start up trapperkeeper and launch the sample application, use the following lein command while in the trapperkeeper-authorization home directory:
lein trampoline run --config ./examples/ring_app/ring-example.conf \
--bootstrap-config ./examples/ring_app/bootstrap.cfg
For convenience, the application could also be run instead via the
ring-example
alias:
lein ring-example
The bootstrap config file contains a list of services that trapperkeeper will load up and make available. They are listed as fully-qualified Clojure namespaces and service names. For this example, the bootstrap.cfg looks like this:
puppetlabs.trapperkeeper.services.webserver.jetty9-service/jetty9-service
puppetlabs.trapperkeeper.services.authorization.authorization-service/authorization-service
examples.ring-app.ring-app/hello-service
This configuration indicates that the jetty9 WebserverService
, authorization
service, and new "hello" service, defined in the ring_app.clj
file, are to be
loaded.
For the application configuration, a file called ring-example.conf
provides
a fairly minimal configuration for the hello service:
global: {
logging-config: ./examples/ring_app/logback.xml
}
webserver: {
port: 8080
ssl-port: 8081
ssl-cert: "./examples/ring_app/ssl/certs/localhost.pem"
ssl-ca-cert: "./examples/ring_app/ssl/certs/ca.pem"
ssl-key: "./examples/ring_app/ssl/private_keys/localhost.pem"
}
authorization: {
version: 1
rules: [
{
match-request: {
path: "/hello/all-allowed"
type: "path"
}
allow-unauthenticated: true
name: "all users allowed"
sort-order: 500
},
{
match-request: {
path: "/hello/user-allowed/([^/]+)$"
type: "regex"
}
allow: "$1"
name: "users allowed by backreference"
sort-order: 500
}
]
}
This configuration sets up two different web server ports - one unencrypted
(8080) and one encrypted (8081) - and some simple authorization rules. Some
pre-generated certificates and keys for use with SSL have been provided in the
./examples/ring_app/ssl
directory.
The effect that the rules have can be seen by making requests to the /hello
web service endpoint. Here are some sample requests, using
cURL. For each request that should be "allowed",
the result of the call should be a response payload with Hello, World!
in the
text and an HTTP status code of 200 OK
. For each request that should be
"denied", the result of the call should be a response payload with
Forbidden request: ... denied by rule <rule-name>
and an HTTP status code of
403 Forbidden
.
curl "http://localhost:8080/hello/all-allowed"
Should be "allowed". The "all users allowed" rule, by virtue of its
allow-unauthenticated
setting being set to true
, should allow requests
which supply no client certificate.
curl "http://localhost:8080/hello/user-allowed/localhost"
Should be "denied". The "users allowed by backreference" rule only allows access to users whose name is contained in the URL. Since no client certificate was provided, no user name could be matched to the URL.
curl "https://localhost:8081/hello/user-allowed/localhost" \
--cacert ./examples/ring_app/ssl/certs/ca.pem \
--cert ./examples/ring_app/ssl/certs/localhost.pem \
--key ./examples/ring_app/ssl/private_keys/localhost.pem
Should be "allowed" since the Common Name (CN) on the user's client
certificate has a value of "localhost" and the request matches the
/hello/user-allowed/([^/]+)$
regular expression which, when substituted with
the CN from the client certificate via the backreference, would be
/hello/user-allowed/localhost
.
curl "https://localhost:8081/hello/user-allowed/not-localhost" \
--cacert ./examples/ring_app/ssl/certs/ca.pem \
--cert ./examples/ring_app/ssl/certs/localhost.pem \
--key ./examples/ring_app/ssl/private_keys/localhost.pem
Should be "denied" since the Common Name (CN) on the user's client
certificate has a value of "localhost" and the request does not match the
/hello/user-allowed/([^/]+)$
regular expression after substitution of the
backreference.
curl "https://localhost:8081/hello" \
--cacert ./examples/ring_app/ssl/certs/ca.pem \
--cert ./examples/ring_app/ssl/certs/localhost.pem \
--key ./examples/ring_app/ssl/private_keys/localhost.pem
Should be "denied" since no rule matches the request. In this case, the
response message would contain global deny all - no rules matched
.