README.md in rails-auth-0.0.0 vs README.md in rails-auth-0.0.1
- old
+ new
@@ -1,11 +1,22 @@
# Rails::Auth
-Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rails/auth`. To experiment with that code, run `bin/console` for an interactive prompt.
+Modular resource-based authentication and authorization for Rails/Rack
-TODO: Delete this and the text above, and describe your gem
+## Description
+Rails::Auth is a flexible library designed for both authentication (AuthN) and
+authorization (AuthZ) using Rack Middleware. It splits the AuthN and AuthZ
+steps into separate middleware classes, using AuthN middleware to first verify
+request client identities, or "principals", then authorizing the request
+via separate AuthZ middleware that consumes these principals, e.g. access
+control lists (ACLs).
+
+Rails::Auth can be used to authenticate and authorize end users using browser
+cookies, service-to-service requests using X.509 client certificates, or any
+other clients with credentials that have proper authenticating middleware.
+
## Installation
Add this line to your application's Gemfile:
```ruby
@@ -20,17 +31,266 @@
$ gem install rails-auth
## Usage
-TODO: Write usage instructions here
+To use Rails::Auth you will need to configure the relevant AuthN and AuthZ
+middleware for your app.
-## Development
+Rails::Auth ships with the following middleware:
-After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
+* **AuthN**: `Rails::Auth::X509::Middleware`: support for authenticating
+ principals by their SSL/TLS client certificates.
+* **AuthZ**: `Rails::Auth::ACL::Middleware`: support for authorizing requests
+ using Access Control Lists (ACLs).
-To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
+Documentation of these middleware and how to use them is provided below.
+### Access Control Lists (ACLs)
+
+ACLs are the main tool Rails::Auth provides for AuthZ. ACLs use a set of
+route-by-route matchers to control access to particular resources.
+Unlike some Rails AuthZ frameworks, this gem grants/denies access to
+controller actions, rather than helping you provide different content to
+different roles or varying the parameters allowed in, say, an update action.
+
+Rails::Auth encourages the use of YAML files for storing ACL definitions,
+although the use of YAML is not mandatory and the corresponding object
+structure output from `YAML.load` can be passed in instead. The following is
+an example of an ACL definition in YAML:
+
+```yaml
+---
+- resources:
+ - method: ALL
+ path: /foo/bar/.*
+ allow_x509_subject:
+ ou: ponycopter
+ allow_claims:
+ groups: ["example"]
+- resources:
+ - method: ALL
+ path: /_admin/?.*
+ allow_claims:
+ groups: ["admins"]
+- resources:
+ - method: GET
+ path: /internal/frobnobs/.*
+ allow_x509_subject:
+ ou: frobnobber
+- resources:
+ - method: GET
+ path: /
+ allow_all: true
+```
+
+An ACL consists of a list of guard expressions, each of which contains a list
+of resources and a set of predicates which can authorize access to those
+resources. *Any* matching predicate will authorize access to any of the
+resources listed for a given expression.
+
+Resources are defined by the following constraints:
+
+* **method**: The requested HTTP method, or `"ALL"` to allow any method
+* **path**: A regular expression to match the path. `\A` and `\z` are added by
+ default to the beginning and end of the regex to ensure the entire path and
+ not a substring is matched.
+
+Once you've defined an ACL, you'll need to create a corresponding ACL object
+in Ruby and a middleware to authorize requests using that ACL. Add the
+following code anywhere you can modify the middleware chain (e.g. config.ru):
+
+```ruby
+app = MyRackApp.new
+
+acl = Rails::Auth::ACL.from_yaml(
+ File.read("/path/to/my/acl.yaml"),
+ matchers: { allow_claims: MyClaimsPredicate }
+)
+
+acl_auth = Rails::Auth::ACL::Middleware.new(app, acl: acl)
+
+run acl_auth
+```
+
+You'll need to pass in a hash of predicate matchers that correspond to the
+keys in the ACL. See the "X.509 Client Certificates" section below for how
+to configure the middleware for `allow_x509_subject`.
+
+The following predicate matchers are built-in and always available:
+
+* **allow_all**: (options: `true` or `false`) always allow requests to the
+ given resources (so long as `true` is passed as the option)
+
+Custom predicate matchers can be any Ruby class that responds to the `#match`
+method. The full Rack environment is passed to `#match`. The corresponding
+object from the ACL definition is passed to the class's `#initialize` method.
+Here is an example of a simple custom predicate matcher:
+
+```ruby
+class MyClaimsPredicate
+ def initialize(options)
+ @options = options
+ end
+
+ def match(env)
+ claims = Rails::Auth.principals(env)["claims"]
+ return false unless principal
+
+ @options["groups"].any? { |group| claims["groups"].include?(group) }
+ end
+end
+
+```
+
+### X.509 Client Certificates
+
+Add an `Rails::Auth::X509::Middleware` object to your Rack middleware chain to
+verify X.509 client certificates (in e.g. config.ru):
+
+```ruby
+app = MyRackApp.new
+
+acl = Rails::Auth::ACL.from_yaml(
+ File.read("/path/to/my/acl.yaml")
+ matchers: { allow_x509_subject: Rails::Auth::X509::Matcher }
+)
+
+acl_auth = Rails::Auth::ACL::Middleware.new(app, acl: acl)
+
+x509_auth = Rails::Auth::X509::Middleware.new(
+ acl_auth,
+ ca_file: "/path/to/my/cabundle.pem"
+ cert_filters: { 'X-SSL-Client-Cert' => :pem },
+ require_cert: true
+)
+
+run x509_auth
+```
+
+The constructor takes the following parameters:
+
+* **app**: the next Rack middleware in the chain. You'll likely want to use
+ an `Rails::Auth::ACL::Middleware` instance as the next middleware in the chain.
+* **ca_file**: Path to the certificate authority (CA) bundle with which to
+ authenticate clients. This will typically be the certificates for the
+ internal CA(s) you use to issue X.509 certificates to internal services, as
+ opposed to commercial CAs typically used by browsers. Client certificates
+ will be ignored unless they can be verified by one of the CAs in this bundle.
+* **cert_filters**: A `Hash` which configures how client certificates are
+ extracted from the Rack environment. You will need to configure your web
+ server to include the certificate in the Rack environment. See notes below
+ for more details.
+* **require_cert**: (default `false`) require a valid client cert in order for
+ the request to complete. This disallows access to your app from any clients
+ who do not have a valid client certificate. When enabled, the middleware
+ will raise the `Rails::Auth::X509::CertificateVerifyFailed` exception.
+
+When creating `Rails::Auth::ACL::Middleware`, make sure to pass in
+`matchers: { allow_x509_subject: Rails::Auth::X509::Matcher }` in order to use
+this predicate in your ACLs. This predicate matcher is not enabled by default.
+
+For client certs to work, you will need to configure your web server to include
+them in your Rack environment, and also configure `cert_filters` correctly to
+filter and process them from the Rack environment.
+
+For example, if you're using nginx + Passenger, you'll need to add something
+like the following to your nginx configuration:
+
+```
+passenger_set_cgi_param X-SSL-Client-Cert $ssl_client_raw_cert;
+```
+
+Once the client certificate is in the Rack environment in some form, you'll
+need to configure a filter object which can convert it from its Rack
+environment form into an `OpenSSL::X509::Certificate` instance. There are
+two built in filters you can reference as symbols to do this:
+
+* `:pem`: parses certificates from the Privacy Enhanced Mail format
+* `:java`: converts `sun.security.x509.X509CertImpl` object instances
+
+The `cert_filters` parameter is a mapping of Rack environment names to
+corresponding filters:
+
+```ruby
+cert_filters: { 'X-SSL-Client-Cert' => :pem }
+```
+
+In addition to these symbols, a filter can be any object that responds to the
+`#call` method, such as a `Proc`. The following filter will parse PEM
+certificates:
+
+```ruby
+cert_filters: { 'X-SSL-Client-Cert' => proc { |pem| OpenSSL::X509::Certificate.new(pem) } }
+```
+
+When certificates are recognized and verified, an `Rails::Auth::X509::Principal`
+object will be added to the Rack environment under `env["rails-auth.principals"]["x509"]`.
+This middleware will never add any certificate to the environment's principals
+that hasn't been verified against the configured CA bundle.
+
+## RSpec integration
+
+Rails::Auth includes built-in matchers that allow you to write tests for your
+ACLs to ensure they have the behavior you expect.
+
+To enable RSpec support, require the following:
+
+```ruby
+require "rails/auth/rspec"
+```
+
+Below is an example of how to write an ACL spec:
+
+```ruby
+RSpec.describe "example_acl.yml", acl_spec: true do
+ let(:example_principals) { x509_principal_hash(ou: "ponycopter") }
+
+ subject do
+ Rails::Auth::ACL.from_yaml(
+ File.read("/path/to/example_acl.yml"),
+ matchers: { allow_x509_subject: Rails::Auth::X509::Matcher }
+ )
+ end
+
+ describe "/path/to/resource" do
+ it { is_expected.to permit get_request(principals: example_principals) }
+ it { is_expected.not_to permit get_request) }
+ end
+end
+```
+
+The following helper methods are available:
+
+* `x509_principal`, `x509_principal_hash`: create instance doubles of Rails::Auth::X509::Principals
+* Request builders: The following methods build requests from the described path:
+ * `get_request`
+ * `head_request`
+ * `put_request`
+ * `post_request`
+ * `delete_request`
+ * `options_request`
+ * `path_request`
+ * `link_request`
+ * `unlink_request`
+
+The following matchers are available:
+
+* `allow_request`: allows a request with the given Rack environment, and optional principals
+
## Contributing
-Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rails-auth.
+Any contributors to the master *rails-auth* repository must sign the
+[Individual Contributor License Agreement (CLA)]. It's a short form that covers
+our bases and makes sure you're eligible to contribute.
+When you have a change you'd like to see in the master repository, send a
+[pull request]. Before we merge your request, we'll make sure you're in the list
+of people who have signed a CLA.
+
+[Individual Contributor License Agreement (CLA)]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1
+[pull request]: https://github.com/square/rails-auth/pulls
+
+## License
+
+Copyright (c) 2016 Square Inc. Distributed under the Apache 2.0 License.
+See LICENSE file for further details.