This project provides an authorization service for use with the
trapperkeeper service framework.
It aims to port Puppet's
auth.conf
feature to Clojure and the trapperkeeper framework, with a different way to
express authorization rules.
To use this service in your trapperkeeper application, simply add this project as a dependency in your leiningen project file:
Then add the authorization service to your
bootstrap.cfg
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;
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.
Trapperkeeper-authorization's docs are housed in this repository.
One example, a Trapperkeeper service which wraps the authorization service around a Ring handler, is included with this project (source code).
This is the protocol for the current implementation of the :AuthorizationService
:
(defprotocol AuthorizationService
(wrap-with-authorization-check [this handler])
(authorization-check [this request]))
wrap-with-authorization-check
takes one argument - handler
. The handler
argument is just a
Ring handler.
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
:
(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
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.
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 theallow-header-cert-info
configuration setting isfalse
, thename
value is pulled from the CN attribute in the certificate provided by the client during SSL session negotiation. When theallow-header-cert-info
configuration setting istrue
, thename
value is pulled from the CN attribute in theX-Client-DN
HTTP header provided with the request. If no certificate is available or a CN value cannot be retrieved from the certificate, thename
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 thename
value has an empty string,authenticated
isfalse
. If theallow-header-cert-info
configuration setting isfalse
and thename
value is non-empty,authenticated
istrue
. If theallow-header-cert-info
configuration setting istrue
, thename
value is non-empty, and an HTTP header namedX-Client-Cert
with a value ofSUCCESS
is provided,authenticated
istrue
; for a value other thanSUCCESS
forX-Client-Cert
,authenticated
isfalse
. -
certificate
- Anjava.security.cert.X509Certificate
object for the client's certificate, if available for the request, else a value ofnil
. If theallow-header-cert-info
configuration setting isfalse
, the value is just reassigned from whatever is set for thessl-client-cert
key in the Ring request map. If theallow-header-cert-info
configuration setting istrue
, theX509Certificate
object is constructed by URL-decoding the string value passed in for the request'sX-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 ofnil
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.
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
section for more information on the authorization information.
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 security model as authconfig became a critical part of Puppet's security model.
We use the Trapperkeeper project on JIRA 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.