1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
# Trapperkeeper Authorization Service
[](https://travis-ci.org/puppetlabs/trapperkeeper-authorization)
This project provides an authorization service for use with the
[trapperkeeper service framework](https://github.com/puppetlabs/trapperkeeper).
It aims to port Puppet's
[`auth.conf`](https://docs.puppetlabs.com/puppet/latest/reference/config_file_auth.html)
feature to Clojure and the trapperkeeper framework, with a different way to
express authorization rules.
## Installation
To use this service in your trapperkeeper application, simply add this project
as a dependency in your leiningen project file:
[](http://clojars.org/puppetlabs/trapperkeeper-authorization)
Then add the authorization service to your
[`bootstrap.cfg`](https://github.com/puppetlabs/trapperkeeper/wiki/Bootstrapping)
file, via:
puppetlabs.trapperkeeper.services.authorization.authorization-service/authorization-service
The authorization service provides an implementation of the
`:AuthorizationService` interface.
The authorization service is configured via the
[trapperkeeper configuration service](https://github.com/puppetlabs/trapperkeeper/wiki/Built-in-Configuration-Service);
so, you can control the authorization logic by adding an `authorization` section
to one of your Trapperkeeper configuration files, and setting various
properties therein. For more info, see
[Configuring the Authorization Service](doc/authorization-config.md).
## Documentation
Trapperkeeper-authorization's docs are housed [in this repository](doc/index.md).
## Example code
One example, a Trapperkeeper service which wraps the authorization service
around a Ring handler, is included with this project
([source code](./examples/ring_app/README.md)).
## Service protocol
This is the protocol for the current implementation of the `:AuthorizationService`:
~~~~clj
(defprotocol AuthorizationService
(wrap-with-authorization-check [this handler])
(authorization-check [this request]))
~~~~
### `wrap-with-authorization-check`
`wrap-with-authorization-check` takes one argument - `handler`. The `handler`
argument is just a
[Ring handler](https://github.com/ring-clojure/ring/wiki/Concepts#handlers).
`wrap-with-authorization-check` will wrap logic for authorizing web requests
around the supplied `handler` argument and return the wrapped handler.
Here is an example of how a Trapperkeeper service can use the
`:AuthorizationService`:
~~~~clj
(defservice hello-service-using-tk-authz
[[:AuthorizationService wrap-with-authorization-check]
[:WebserverService add-ring-handler]]
(init [this context]
(add-ring-handler
(wrap-with-authorization-check
(fn [_]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello, World!"}))
"/hello")
context))
~~~~
See the
[Trapperkeeper web service](https://github.com/puppetlabs/trapperkeeper-webserver-jetty9)
project for more information on the `:WebserverService`.
For this example, if the web server receives a request to the "/hello"
endpoint, the middleware logic behind the `wrap-with-authorization-check`
function evaluates the request to see if it is "authorized". If the
request is determined to be "allowed", the request is handed on to
the `handler` passed into the original `wrap-with-authorization-check`
function call. If the request is determined to be "denied", a Ring response
with an HTTP status code of "403" and a message body with details about the
authorization failure is returned. In the latter case, the original
`handler` supplied to the `wrap-with-authorization-check` function is not
called.
For more information on the rule evaluation behavior (e.g., how a request is
determined to be "allowed" or "denied"), see
[Configuring the Authorization Service](doc/authorization-config.md).
Upon successful authorization, a key name of `authorization` is appended to
the Ring request map which is passed through to the `handler` function. The
value associated with the `authorization` key is a map containing the
following key/value pairs:
* `name` - CN (Common Name) extracted from the Distinguished Name in the
subject of the certificate presented with the request. When the
`allow-header-cert-info` configuration setting is `false`, the `name` value
is pulled from the CN attribute in the certificate provided by the client
during SSL session negotiation. When the `allow-header-cert-info`
configuration setting is `true`, the `name` value is pulled from the CN
attribute in the `X-Client-DN` HTTP header provided with the request.
If no certificate is available or a CN value cannot be retrieved from the
certificate, the `name` is set to an empty string.
* `authenticated` - A boolean value representing whether or not the client
request included an authenticated user. In any case where the `name` value
has an empty string, `authenticated` is `false`. If the
`allow-header-cert-info` configuration setting is `false` and the `name` value
is non-empty, `authenticated` is `true`. If the `allow-header-cert-info`
configuration setting is `true`, the `name` value is non-empty, and an HTTP
header named `X-Client-Cert` with a value of `SUCCESS` is provided,
`authenticated` is `true`; for a value other than `SUCCESS` for
`X-Client-Cert`, `authenticated` is `false`.
* `certificate` - An `java.security.cert.X509Certificate` object for the client's
certificate, if available for the request, else a value of `nil`. If the
`allow-header-cert-info` configuration setting is `false`, the value is just
reassigned from whatever is set for the `ssl-client-cert` key in the Ring
request map. If the `allow-header-cert-info` configuration setting is `true`,
the `X509Certificate` object is constructed by URL-decoding the string
value passed in for the request's `X-Client-Cert` HTTP header and parsing
the result as a PEM-formatted (Base-64 encoded) certificate. If the header
value cannot be URL-decoded and/or converted from a Base-64 encoded string, a
value of `nil` is set.
> **Note:** Apache's mod_proxy converts line breaks in PEM documents in HTTP
headers to spaces for some reason and trapperkeeper-authorization can't URL
decode the result. We're tracking this issue as
[SERVER-217](https://tickets.puppetlabs.com/browse/SERVER-217).
### `authorization-check`
A function for directly checking whether a request is authorized or not.
Useful if you'd like to take more control of the behavior than what
`wrap-authorization-check` allows for, such as if you've got a servlet request.
The result of this function contains the authorization boolean as well as a
user-friendly message if it's denied, and some meta-information like the
request in question. The request will be updated to include things like
destructured query parameters and authorization information.
See the [`wrap-with-authorization-check`](#wrap-with-authorization-check)
section for more information on the authorization information.
## Credits
The original work for this library, service, and the original REST authconfig
work in Ruby Puppet were all contributed by [Brice Figureau](https://github
.com/masterzen). This project has been graciously transferred to Puppet Labs for
further development and maintenance as it becomes a critical part of the
[Puppet Server](https://github.com/puppetlabs/puppet-server) security model as
authconfig became a critical part of Puppet's security model.
## Support
We use the
[Trapperkeeper project on JIRA](https://tickets.puppetlabs.com/browse/TK) for
tickets on the Trapperkeeper Authorization Service, although Github issues
are welcome too. Please note that the best method to get our attention on an
issue is via JIRA.
Tickets: https://tickets.puppetlabs.com/browse/TK
|