action_access 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/CONTRIBUTING.md +23 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +87 -0
- data/LICENSE.txt +20 -0
- data/README.md +283 -0
- data/Rakefile +28 -0
- data/action_access.gemspec +24 -0
- data/lib/action_access.rb +14 -0
- data/lib/action_access/controller_additions.rb +75 -0
- data/lib/action_access/keeper.rb +90 -0
- data/lib/action_access/model_additions.rb +14 -0
- data/lib/action_access/railtie.rb +19 -0
- data/lib/action_access/user_utilities.rb +39 -0
- data/lib/action_access/version.rb +3 -0
- data/test/action_access_test.rb +21 -0
- data/test/controllers/articles_controller_test.rb +41 -0
- data/test/controllers/secrets_controller_test.rb +10 -0
- data/test/controllers/static_controller_test.rb +15 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +15 -0
- data/test/dummy/app/controllers/articles_controller.rb +36 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/controllers/secrets_controller.rb +4 -0
- data/test/dummy/app/controllers/static_controller.rb +6 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/mailers/.keep +0 -0
- data/test/dummy/app/models/.keep +0 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/models/user.rb +7 -0
- data/test/dummy/app/views/articles/edit.html.erb +1 -0
- data/test/dummy/app/views/articles/index.html.erb +1 -0
- data/test/dummy/app/views/articles/new.html.erb +1 -0
- data/test/dummy/app/views/articles/show.html.erb +3 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/static/home.html.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +78 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/assets.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/migrate/20140926071026_create_users.rb +10 -0
- data/test/dummy/db/schema.rb +23 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/models/user_test.rb +22 -0
- data/test/support/compact_environment.rb +9 -0
- data/test/test_helper.rb +8 -0
- metadata +203 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c3a922a62dc47428db03349b5ab4381c32d669ea
|
4
|
+
data.tar.gz: 639f7cf2ce63e3783f7d33970ea874624f8c7981
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 10c2835e378462860b56b636431ebb26ac196acd179c82703a72bb77cb61745352b97ed668d28f2999dc88a4deec16f0ac32d3c10794a42be1489dd103b37d94
|
7
|
+
data.tar.gz: 8a1b64c08c10887c41e55a1e8f16d2ed8677cd68a7f7ef42f050f8e04a7d517727e2651e7841c0cc8bfa84b195768ae7c43d99c168eaa0f6a6d61e2b96bf797a
|
data/.gitignore
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
## Please read before contributing
|
2
|
+
|
3
|
+
1. If you have questions about Action Access, use the
|
4
|
+
[Mailing List](https://groups.google.com/d/forum/action_access) or
|
5
|
+
[Stack Overflow](https://stackoverflow.com/questions/tagged/action_access).
|
6
|
+
Do not post questions here.
|
7
|
+
|
8
|
+
2. If you find a security bug, **DO NOT** submit an issue here.
|
9
|
+
Please send an e-mail to [mgag.issues@gmail.com](mailto:mgag.issues@gmail.com)
|
10
|
+
instead.
|
11
|
+
|
12
|
+
3. Do a small search on the issues tracker before submitting your issue to
|
13
|
+
see if it was already reported or fixed. In case it was not, create your report
|
14
|
+
with a **clear description** of the issue and a **code sample** that
|
15
|
+
demonstrates it. Include Rails and Action Access versions, and as much relevant
|
16
|
+
information as possible. Tests or a sample application showing how the
|
17
|
+
expected behavior is not occurring will be much appreciated.
|
18
|
+
|
19
|
+
The more information you give, the easier it becomes to track the issue and fix
|
20
|
+
it. Your goal should be to make it easy for yourself and others to replicate
|
21
|
+
the bug and figure out a fix.
|
22
|
+
|
23
|
+
Thanks for your time and support!
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# Declare your gem's dependencies in action_access.gemspec.
|
4
|
+
# Bundler will treat runtime dependencies like base dependencies, and
|
5
|
+
# development dependencies will be added by default to the :development group.
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
# Declare any dependencies that are still in development here instead of in
|
9
|
+
# your gemspec. These might include edge Rails or gems from your path or
|
10
|
+
# Git. Remember to move these dependencies to your gemspec before releasing
|
11
|
+
# your gem to rubygems.org.
|
12
|
+
|
13
|
+
# To use debugger
|
14
|
+
# gem 'debugger'
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
action_access (0.0.1)
|
5
|
+
rails (~> 4.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actionmailer (4.1.6)
|
11
|
+
actionpack (= 4.1.6)
|
12
|
+
actionview (= 4.1.6)
|
13
|
+
mail (~> 2.5, >= 2.5.4)
|
14
|
+
actionpack (4.1.6)
|
15
|
+
actionview (= 4.1.6)
|
16
|
+
activesupport (= 4.1.6)
|
17
|
+
rack (~> 1.5.2)
|
18
|
+
rack-test (~> 0.6.2)
|
19
|
+
actionview (4.1.6)
|
20
|
+
activesupport (= 4.1.6)
|
21
|
+
builder (~> 3.1)
|
22
|
+
erubis (~> 2.7.0)
|
23
|
+
activemodel (4.1.6)
|
24
|
+
activesupport (= 4.1.6)
|
25
|
+
builder (~> 3.1)
|
26
|
+
activerecord (4.1.6)
|
27
|
+
activemodel (= 4.1.6)
|
28
|
+
activesupport (= 4.1.6)
|
29
|
+
arel (~> 5.0.0)
|
30
|
+
activesupport (4.1.6)
|
31
|
+
i18n (~> 0.6, >= 0.6.9)
|
32
|
+
json (~> 1.7, >= 1.7.7)
|
33
|
+
minitest (~> 5.1)
|
34
|
+
thread_safe (~> 0.1)
|
35
|
+
tzinfo (~> 1.1)
|
36
|
+
arel (5.0.1.20140414130214)
|
37
|
+
builder (3.2.2)
|
38
|
+
erubis (2.7.0)
|
39
|
+
hike (1.2.3)
|
40
|
+
i18n (0.6.11)
|
41
|
+
json (1.8.1)
|
42
|
+
mail (2.6.1)
|
43
|
+
mime-types (>= 1.16, < 3)
|
44
|
+
mime-types (2.3)
|
45
|
+
minitest (5.4.1)
|
46
|
+
multi_json (1.10.1)
|
47
|
+
rack (1.5.2)
|
48
|
+
rack-test (0.6.2)
|
49
|
+
rack (>= 1.0)
|
50
|
+
rails (4.1.6)
|
51
|
+
actionmailer (= 4.1.6)
|
52
|
+
actionpack (= 4.1.6)
|
53
|
+
actionview (= 4.1.6)
|
54
|
+
activemodel (= 4.1.6)
|
55
|
+
activerecord (= 4.1.6)
|
56
|
+
activesupport (= 4.1.6)
|
57
|
+
bundler (>= 1.3.0, < 2.0)
|
58
|
+
railties (= 4.1.6)
|
59
|
+
sprockets-rails (~> 2.0)
|
60
|
+
railties (4.1.6)
|
61
|
+
actionpack (= 4.1.6)
|
62
|
+
activesupport (= 4.1.6)
|
63
|
+
rake (>= 0.8.7)
|
64
|
+
thor (>= 0.18.1, < 2.0)
|
65
|
+
rake (10.3.2)
|
66
|
+
sprockets (2.12.2)
|
67
|
+
hike (~> 1.2)
|
68
|
+
multi_json (~> 1.0)
|
69
|
+
rack (~> 1.0)
|
70
|
+
tilt (~> 1.1, != 1.3.0)
|
71
|
+
sprockets-rails (2.1.4)
|
72
|
+
actionpack (>= 3.0)
|
73
|
+
activesupport (>= 3.0)
|
74
|
+
sprockets (~> 2.8)
|
75
|
+
sqlite3 (1.3.9)
|
76
|
+
thor (0.19.1)
|
77
|
+
thread_safe (0.3.4)
|
78
|
+
tilt (1.4.1)
|
79
|
+
tzinfo (1.2.2)
|
80
|
+
thread_safe (~> 0.1)
|
81
|
+
|
82
|
+
PLATFORMS
|
83
|
+
ruby
|
84
|
+
|
85
|
+
DEPENDENCIES
|
86
|
+
action_access!
|
87
|
+
sqlite3
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 Matías A. Gagliano
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,283 @@
|
|
1
|
+
# Action Access
|
2
|
+
|
3
|
+
Action Access is an access control system for Ruby on Rails. It provides a
|
4
|
+
modular and easy way to secure applications and handle permissions.
|
5
|
+
|
6
|
+
It works at controller level focusing on what **actions** are accessible for
|
7
|
+
the current user instead of handling models and their attributes.
|
8
|
+
|
9
|
+
It also provides utilities for thorough control and some useful view helpers.
|
10
|
+
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add `action_access` to the app's Gemfile, run the `bundle` command and restart
|
15
|
+
any running server.
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
# Gemfile
|
19
|
+
gem 'action_access'
|
20
|
+
```
|
21
|
+
|
22
|
+
|
23
|
+
## Basic configuration
|
24
|
+
|
25
|
+
The most important setting is the way to get the **clearance level** (role,
|
26
|
+
user group, etc.), other than that it works out of the box.
|
27
|
+
|
28
|
+
Action Access doesn't require users or authentication at all to function so
|
29
|
+
you can get creative with the way you set and identify clearance levels.
|
30
|
+
|
31
|
+
It only needs a `current_clearance_level` method that returns the proper
|
32
|
+
clearance level for the current request. It can be a string or symbol and
|
33
|
+
it doesn't matter if it's singular or plural, it'll be singularized.
|
34
|
+
|
35
|
+
* With `current_user`:
|
36
|
+
|
37
|
+
The default `current_clearance_level` method tests if it can get
|
38
|
+
`current_user.clearance_level` and defaults to `:guest` if not.
|
39
|
+
|
40
|
+
So, if you already have a `current_user` method you just need to add a
|
41
|
+
`clearance_level` method to the user. With a role based authorization you
|
42
|
+
may add the following to your `User` model:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class User < ActiveRecord::Base
|
46
|
+
belongs_to :role
|
47
|
+
|
48
|
+
def clearance_level
|
49
|
+
role.name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
* No `current_user`:
|
55
|
+
|
56
|
+
If there's no `current_user` you need to override `current_clearance_level`
|
57
|
+
with whatever logic that applies to your application.
|
58
|
+
|
59
|
+
Continuing with the role based example, you might do something like this:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
class ApplicationController < ActionController::Base
|
63
|
+
def current_clearance_level
|
64
|
+
session[:role] || :guest
|
65
|
+
end
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
|
70
|
+
## Setting permissions
|
71
|
+
|
72
|
+
Permissions are set through authorization statements using the **let** class
|
73
|
+
method available in every controller. The first parameter is the clearance
|
74
|
+
level (plural or singular) and the second is the action or list of actions.
|
75
|
+
|
76
|
+
As a simple example, to allow administrators (and only administrators in this
|
77
|
+
case) to delete articles you'd add the following to `ArticlesController`:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
class ArticlesController < ApplicationController
|
81
|
+
let :admins, :destroy
|
82
|
+
|
83
|
+
def destroy
|
84
|
+
# ...
|
85
|
+
end
|
86
|
+
|
87
|
+
# ...
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
This will automatically **lock** the controller and only allow administrators
|
92
|
+
accessing the destroy action. **Every other request** pointing to the controller
|
93
|
+
**will be rejected** and redirected with an alert.
|
94
|
+
|
95
|
+
### Real-life example:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
class ArticlesController < ApplicationController
|
99
|
+
let :admins, :all
|
100
|
+
let :editors, [:index, :show, :edit, :update]
|
101
|
+
let :all, [:index, :show]
|
102
|
+
|
103
|
+
def index
|
104
|
+
# ...
|
105
|
+
end
|
106
|
+
|
107
|
+
# ...
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
These statements lock the controller and set the following:
|
112
|
+
* _Administrators_ (admins) are authorized to access any action.
|
113
|
+
* _Editors_ can list, view and edit articles.
|
114
|
+
* _Anyone else_ can **only** list and view articles.
|
115
|
+
|
116
|
+
This case uses the special keyword `:all`, it means everyone if passed as the
|
117
|
+
first argument or every action if passed as the second one.
|
118
|
+
|
119
|
+
Again, any unauthorized request will be rejected and redirected with an alert.
|
120
|
+
|
121
|
+
### Note about clearance levels
|
122
|
+
|
123
|
+
Notice that in the previous examples we didn't need to define clearance levels
|
124
|
+
or roles anywhere else in the application. With the authorization statement you
|
125
|
+
both **define** them and **set their permissions**. The only requirement is
|
126
|
+
that the clearance levels from the authorizations match the one returned by
|
127
|
+
`current_clearance_level`.
|
128
|
+
|
129
|
+
|
130
|
+
## Advanced configuration
|
131
|
+
|
132
|
+
### Locked by default
|
133
|
+
|
134
|
+
The `lock_access` class method forces controllers to be locked even if no
|
135
|
+
permissions are defined, in such case every request will be redirected.
|
136
|
+
|
137
|
+
This allows to ensure that an entire application or scope (e.g. `Admin`) is
|
138
|
+
**locked by default**. Simply call `lock_access` inside `ApplicationController`
|
139
|
+
or from a scope's base controller.
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
class ApplicationController < ActionController::Base
|
143
|
+
lock_access
|
144
|
+
|
145
|
+
# ...
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
To **unlock** a single controller (to make it "public") add `let :all, :all`,
|
150
|
+
this will allow anyone to access any action in the controller.
|
151
|
+
|
152
|
+
### Redirection path
|
153
|
+
|
154
|
+
By default any unauthorized (or not explicitly authorized) access will be
|
155
|
+
redirected to the **root path**.
|
156
|
+
|
157
|
+
You can set or choose a different path by overriding the somewhat long but
|
158
|
+
very clear `unauthorized_access_redirection_path` method.
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
class ApplicationController < ActionController::Base
|
162
|
+
|
163
|
+
def unathorized_access_redirection_path
|
164
|
+
case current_user.clearance_level.to_sym
|
165
|
+
when :admin then admin_root_path
|
166
|
+
when :user then user_root_path
|
167
|
+
else root_path
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# ...
|
172
|
+
end
|
173
|
+
```
|
174
|
+
|
175
|
+
### Alert message
|
176
|
+
|
177
|
+
Redirections have a default alert message of "Not authorized.". To customize it
|
178
|
+
or use translations set `action_access.redirection_message` in your locales.
|
179
|
+
|
180
|
+
```yml
|
181
|
+
# config/locales/en.yml
|
182
|
+
en:
|
183
|
+
action_access:
|
184
|
+
redirection_message: "You are not allowed to do this!"
|
185
|
+
```
|
186
|
+
|
187
|
+
|
188
|
+
## Utilities
|
189
|
+
|
190
|
+
### Fine Grained Access Control
|
191
|
+
|
192
|
+
If further control is required, possibly because access depends on request
|
193
|
+
parameters or some result from the database, you can use the `not_authorized!`
|
194
|
+
method inside actions to reject the request and issue a redirection. It
|
195
|
+
optionally takes a redirection path and a custom alert message.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
class ProfilesController < ApplicationController
|
199
|
+
let :user, [:edit, :update]
|
200
|
+
|
201
|
+
def update
|
202
|
+
unless params[:id] == current_user.profile_id
|
203
|
+
not_authorized! path: profile_path, message: "That's not your profile!"
|
204
|
+
end
|
205
|
+
|
206
|
+
# ...
|
207
|
+
end
|
208
|
+
|
209
|
+
# ...
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
There are better ways to handle this particular case but it serves to outline
|
214
|
+
the use of `not_authorized!` inside actions.
|
215
|
+
|
216
|
+
### Model additions
|
217
|
+
|
218
|
+
Action Access is bundled with some model utilities too. By calling
|
219
|
+
`add_access_utilities` in any model it will extend it with a `can?` instance
|
220
|
+
method that checks if the entity (commonly a user) is authorized to perform a
|
221
|
+
given action on a resource.
|
222
|
+
|
223
|
+
`can?` takes two arguments, the action and the resource, and a namespace option
|
224
|
+
if needed. The resource can be a string, symbol, controller class or model
|
225
|
+
instance. Action Access will do the possible to get the right controller out
|
226
|
+
of the resource and the namespace (optional). In the end it returns a boolean.
|
227
|
+
|
228
|
+
**Some examples:**
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
@user.can? :edit, :articles, namespace: :admin
|
232
|
+
@user.can? :edit, @admin_article # Admin::Article instance
|
233
|
+
@user.can? :edit, Admin::ArticlesController
|
234
|
+
# True if the user's clearance level allows her to access 'admin/articles#edit'.
|
235
|
+
```
|
236
|
+
|
237
|
+
`can?` depends on a `clearance_level` method in the model so don't forget it.
|
238
|
+
Continuing with the `User` model from before:
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
class User < ActiveRecord::Base
|
242
|
+
add_access_utilities
|
243
|
+
|
244
|
+
belongs_to :role
|
245
|
+
|
246
|
+
def clearance_level
|
247
|
+
role.name
|
248
|
+
end
|
249
|
+
end
|
250
|
+
```
|
251
|
+
|
252
|
+
```erb
|
253
|
+
<% if current_user.can? :edit, :articles %>
|
254
|
+
<%= link_to 'Edit article', edit_article_path(@article) %>
|
255
|
+
<% end %>
|
256
|
+
```
|
257
|
+
|
258
|
+
### The keeper
|
259
|
+
|
260
|
+
The **keeper** is the core of Action Access, it's the one that registers
|
261
|
+
permissions and who decides if a clearance level grants access or not.
|
262
|
+
|
263
|
+
It's available as `keeper` within controllers and views and as
|
264
|
+
`ActionAccess::Keeper.instance` anywhere else. You can use it check permissions
|
265
|
+
with the `lets?` method that takes the clearance level as the first argument,
|
266
|
+
the rest are the same as `can?`.
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
# Filter a list of users to only those allowed to edit articles.
|
270
|
+
@users.select { |user| keeper.lets? user.role.name, :edit, :articles }
|
271
|
+
```
|
272
|
+
|
273
|
+
|
274
|
+
## License
|
275
|
+
|
276
|
+
Action Access is released under the [MIT License](http://opensource.org/licenses/MIT).
|
277
|
+
|
278
|
+
Copyright (c) 2014 Matías A. Gagliano.
|
279
|
+
|
280
|
+
|
281
|
+
## Contributing
|
282
|
+
|
283
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|