README.md in authz-0.0.1 vs README.md in authz-0.0.2

- old
+ new

@@ -19,34 +19,34 @@ Get a feel for **Authz** with this [live demo](https://authzcasestudy.herokuapp.com/). <!--- TODO: Change link to video ---> ## Table of Content - [Is Authz A Good Match For My Needs?](#is-authz-a-good-match-for-my-needs) +- [Requirements](#requirements) - [Installation and Initial Setup](#installation-and-initial-setup) - [Usage](#usage) * [How Authorization Rules Work in Authz](#how-authorization-rules-work-in-authz) + [Permissions](#permissions) + [Scoping Rules](#scoping-rules) * [Usage for Authorization Admins](#usage-for-authorization-admins) - + [Cold-start](#cold-start) - + [Managing the System](#managing-the-system) - + [Keeping the System Healthy](#keeping-the-system-healthy) + + [Cold-start Configuration](#cold-start-configuration) + + [Business as Usual](#business-as-usual) + + [Maintenance](#maintenance) * [Usage for Developers](#usage-for-developers) + [Scopables](#scopables) + [Controllers](#controllers) - [`authorize`](#authorize) - [`apply_authz_scopes`](#apply_authz_scopes) + [Views](#views) - [`authorized_path?`](#authorized_path) - [`authz_link_to`](#authz_link_to) + + [Programmatic Interaction with Authz](#programmatic-interaction-with-authz) - [Performance and Caching](#performance-and-caching) * [In-request caching](#in-request-caching) * [Cross-request caching](#cross-request-caching) * [Fragment and Russian Doll caching](#fragment-and-russian-doll-caching) -- [Authorization Good and Bad Practices](#authorization-good-and-bad-practices) - * [Good Practices](#good-practices) - * [Bad Practices](#bad-practices) +- [Common Problems and Solutions](#common-problems-and-solutions) - [License](#license) ## Is Authz A Good Match For My Needs? Authz was built for applications that need a particular type of authorization which is very common and relatively simple. However, there are many other types of authorization rules that are out of @@ -72,11 +72,11 @@ - Examples: - The *general director* **role** must be able to *index/show/new/create/...* the **reports** from **all cities** and **all departments**. - The *regional director* **role** must be able to *index/show/new/create/...* the **reports** from **their city** and **all departments**. - - The *regional auditor* **role** must only be able to *index/show* the **reports** of the **their city** + - The *regional auditor* **role** must only be able to *index/show* the **reports** from **their city** and **all departments**. - The *writer* **role** must be able to *index/show/new/create/...* the **reports** from **their city** and **their department.** - *John Doe* can simultaneously be a *regional auditor* in San Francisco and a *writer* in New York for the 'Sports' department. @@ -85,10 +85,15 @@ **Roles** are authorized to perform those actions on a **certain subest of those resources**, like the reports that belong to New York and the Sports Department. [Back to table of content](#table-of-content) +## Requirements +- Rails >= 5.0 using `ActiveRecord` on a relational database. +- Your app already has an authentication mechanism like [Devise](https://github.com/plataformatec/devise). +- Optional: Your preference of caching technology through `ActiveSupport::Cache`. + ## Installation and Initial Setup Add this line to your application's Gemfile: ```ruby gem 'authz' @@ -109,11 +114,11 @@ Seed Authz's tables with the data required to control access to the Authorization Admin. This will create a [Business Process](#permissions) in the `Authz::BusinessProcesses` table. Any role that is granted that business process will get full access to the Authorization Admin. - Later, you will also probably want to run this in production to configure it. -See [Cold Start](#cold-start) for more details. +See [Cold Start Configuration](#cold-start-configuration) for more details. ```bash $ rails authz:seed_admin ``` Go to `config/initializer/authz.rb` and configure: @@ -272,13 +277,19 @@ However, despite all these models, only the information about the `City` and the `Department` is relevant to the authorization rules. We call these classes **Scoping Classes** as they define the scope of the permissions granted to a role. For the case of the "New York Sports Editor" role, the permissions to _"publish reports"_ and to _"moderate comments"_ are scoped -down to `Reports` and `Comments` that belong to "New York" `City` and the "Sports" `Department`. We refer to these -as the **Scoping Rules**. The next figure illustrates how everything fits together. +down to `Reports` and `Comments` that belong to "New York" `City` and the "Sports" `Department`. +In Authz terminology, we say that a “New York Sports Editor” is authorized to do any of his granted **permissions** +only on resources that are within his scope, which is defined by his configured **Scoping Rules**: +- `ScopableByCity = 'New York` +- `ScopableByDepartment = Sports` +The next figure illustrates how everything fits together. + + <div align="center"> <center> <img src="/readme_images/how_everything_fits_together.png" width="800"/> </center> </div> @@ -287,29 +298,79 @@ repetition..." take a look at the [Scopables](#scopables) section. It is simple to DRY this up, it's just easier to explain the concepts this way._ These **Scoping Classes** typically exists inside the application's domain logic or are easy enough to implement and fit nicely into the domain. -**If you can't express you authorization rules in terms of Scoping Classes then Authz is probably +**If you can't express your authorization rules in terms of Scoping Classes then Authz is probably not for you.** [Back to table of content](#table-of-content) ### Usage for Authorization Admins -TODO: we are working on this... stay tuned -- 3 activities that admins do -#### Cold-start +Authz comes with a built-in authorization GUI from which admins can configure everything related to authorization. +The GUI can be accessed on a URL/path configured by the developers. -#### Managing the System -#### Keeping the System Healthy +There are 3 types of activities that admins do through the GUI: _cold-start configuration, +business as usual and maintenance._ + +#### Cold-start Configuration +Cold-start makes reference to configuring all the permissions, scoping rules and roles on a brand new Authz installation. + +For teams that are integrating Authz into an existing live project we recommend doing the cold-start configuration +using a rake task, since they probably can’t afford to have all users locked out while the +admin manually configures everything through the GUI. More information on how to do this on: +[Programmatic Interaction with Authz](#programmatic-interaction-with-authz). + +Teams can also opt to use the GUI to manually configure everything. Authz uses its own permission system to authorize +the access to the GUI, so the “first ever” admin needs to be granted permission to access the GUI by a developer +through the console. Authz provides a the `rails authz:seed_admin` rake task to automatically seed everything +needed to access the GUI (more information on the [Installation Section](#installation-and-initial-setup)). + [Back to table of content](#table-of-content) +#### Business as Usual +During _“business as usual”_ an admin will make changes to the authorization configuration to keep up with business’ +needs, like granting, creating and revoking roles. + +The GUI provides the admins full control of the authorization system without requiring any code modifications and +re-deployments. Through the GUI admins can: +- Create, view, update and delete controller actions, business processes, scoping rules and roles. +- Grant and revoke roles to users. + +The admin also makes it very easy to answer questions like _“who can cancel orders?”_ or _“what can John do?”_. + +<div align="center"> + <center> + <img src="/readme_images/admin_high_visibility.png" width="800"/> + </center> +</div> + +[Back to table of content](#table-of-content) + +#### Maintenance +The authorization configuration will need maintenance as developers make changes to the codebase. +In particular, maintenance is needed when developers add/remove **controller actions** from the application +or add/remove/change the **scopables** or **keywords** for the **scoping rules**. + +The GUI’s main dashboard detects differences from the current in-database configuration and the codebase, +and suggests the adjustments that need to be done. However, nothing replaces good communication between the developers +and the authorization admins. + +<div align="center"> + <center> + <img src="/readme_images/admin_dashboard.png" width="800"/> + </center> + </div> + + +[Back to table of content](#table-of-content) + ### Usage for Developers The authorization logic bits inside your app typically live in 3 places: [Scopables](#scopables), -[Controllers](#controllers) and [Views](#views). +[Controllers](#controllers) and [Views](#views). You may also interact with the Authz models directly. #### Scopables This is the first thing to do if you have just installed the gem. Start by identifying which are the [Scoping Classes](#scoping-rules) inside your app that you need to meet your @@ -436,11 +497,11 @@ # if not authorized end end ``` -Authz will check if the current user has any role that allows him to do the action `Reports#show` on the instance +Authz will check if the current user has any role that allows him to perform the action `Reports#show` on the instance `@report` taking into account the role's *Scoping Rules*. Note that the controller and action name are automatically inferred. <!--- TODO: If we relax the authorize method to allow for passing custom actions, put that here ---> @@ -574,10 +635,31 @@ # Note that we are explictly wrapping the 3rd argument in {} to avoid ambiguity in the params. # If you get an 'unknown keyword: class' error, it's caused by this. ``` [Back to table of content](#table-of-content) +#### Programmatic interaction with Authz +Everything that can be done through the admin GUI can also be done programmatically. +You can interact with Authz’s models in the exact same way as you would interact with any `ActiveRecord` class. + +The models you probably want to interact with are: `Authz::ConrollerAction`, `Authz::BusinessProcess`, +`Authz::Role`, `Authz::RoleGrant` and `Authz::ScopingRule`. + +You can also call `user.roles` to get the roles associated to a user. + +If for some reason you want to reference a specific `BusinessProces` or `Role` from your code, +you can do so through the `code` attribute. `code` is automatically set as the snake case + of the `name` upon creation (unless you specify otherwise) and can't be changed from the GUI + (so your code does not break if the name is changed). + +```ruby +Authz::Role.find_by(code: 'foo') +Authz::BusinessProcess.find_by(code: 'bar') +``` +[Back to table of content](#table-of-content) + + ## Performance and Caching Dynamic views based on the `current_user`'s authorization privileges will add some calls to your database as part of the authorization resolution process. The effect of these additional calls can be significant in applications with highly dynamic views and requires special attention. @@ -690,51 +772,9 @@ a consequence, we need to include information about the departments in addition to `#roles_cache_key`in the fragment keys. [Back to table of content](#table-of-content) -## Authorization Good and Bad Practices -A non exhaustive list of generally accepted authorization wisdom and things we've learned from using Authz ourselves: - -### Good Practices -- **Principle of Least Privilege**: Users should have the minimal set of permissions required to perform -their duties on the application. -- **Closed by Default / Fail-Safe Systems**: deny access to resources unless otherwise stated. -“Blacklisting” type of permissions should only be used as an optimisation on top of a fail-safe system. This is why -`around_action :verify_authorized` is a good idea. -- **Visibility Is Important**: Strive to design a permission and scoping structure that is easily understandable -by all human beings managing the authorization system. Answering questions like _'who can edit reports'_ and _'what -can user 1234 do'_ should be straightforward. Security holes are often related to misconfiguration due to complexity. -- **Know the Trade-offs**: As with most of things in engineering, there is no "best" solution for all authorization -problems; it's up to you to choose the right tool for YOUR job. In our opinion the most important trade-off is driven -by the _"rule resolution time"_ of your authorization strategy/library. The extreme points of this continuum are -pure **Runtime resolution** and pure **Static resolution**, with most systems falling somewhere in the middle. - - **Runtime resolution** means that when a _user_ performs a request to perform an _action_ over a _resource_ - given a certain _context_, the system executes some code at runtime to decide whether the user can be granted access. - - **Static Resolution** means that the user access is fully pre-defined and once a user request arrives it can be - resolved by simply “reading” from the defined permissions (e.g. querying a database). - - For reference **Authz** takes a hybrid approach in which permissions are **statically defined**, and the degree - of **runtime resolution** for scoping rules can be chosen by developers according to their needs. We believe this - is a happy medium that is able to support many use cases while keeping the benefits of visibility and configurability. - -<div align="center"> - <center> - <img src="/readme_images/rule_resolution_time_tradeoffs.png" width="700"/> - </center> - </div> - -### Bad Practices -- **Client-Side Authorization** does not exist (period). If anything, it can be included -as a usability improvement. -- **Security Through Obscurity**: Any authorisation system that relies on the user not knowing certain information to -guarantee security is not viable. For example, using obfuscated URL links to make it harder for attackers to guess -a customer’s invoice number. -- **Leave Authorization for Later**: _'let's develop the whole thing and we will think about authorization later'_. -This sucks but it's the truth. Almost everything you code requires some type of authorization and therefore authorization -code quickly gets everywhere. If you just 'wing it' with a couple of boolean flags in the `User` model you are -almost guaranteed to have a painful re-write. - -[Back to table of content](#table-of-content) ## Common Problems and Solutions - When linking from your app into the Authz Admin, make sure you use the `root_url` helper and NOT the `root_path`. If you don't, you will get an `ActionController::RoutingError No route matches...`. In other words, if you mounted Authz `as: authz` in your router, use: \ No newline at end of file