rails-action-authorization 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a8d8a18e02c7cba715ff6d0369b2bc1463545d45062e58408ec8384d1dcbd316
4
+ data.tar.gz: 04ecdf2bae106d9cee8d4601f8693a7634a311a5cd12b905631954ed7e4d2be4
5
+ SHA512:
6
+ metadata.gz: 81053e97cbf237ac0cf489a370d2f3c13b057838c99fecbed411e814e7d884d2f18faf73a8cd2705e2e39fa1d5bcb8555f7626dbabe5933b3fc0a8e878912709
7
+ data.tar.gz: 6d02f17442c5276e29de19ce8d6ba4599841847ee52b39660cdadafdbc96400801878daa4b02ca842021d3c6c6be8e0fc6cd9d351d5025b3fd5f27653e8ba41a
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Andrew Luchuk
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,145 @@
1
+ [![Build Status](https://travis-ci.org/speratus/rails-action-authorization.svg?branch=master)](https://travis-ci.org/speratus/rails-action-authorization)
2
+ [![codecov](https://codecov.io/gh/speratus/rails-action-authorization/branch/master/graph/badge.svg)](https://codecov.io/gh/speratus/rails-action-authorization)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/588fbef9d8a7c39bb071/maintainability)](https://codeclimate.com/github/speratus/authorizer/maintainability)
4
+
5
+ # Rails Action Authorization
6
+ Rails Action Authorization is a rails plugin that gives developers a lightweight authorization framework.
7
+
8
+ While there are lots of rails plugins designed to do authorization, Rails Action Authorization strives to
9
+ be the most intuitive while simultaneously allowing developers to write the smallest possible amount of code to have a functioning authorization system.
10
+
11
+ ## Usage
12
+ There are two parts to using `rails-action-authorization`. The first part is defining rules.
13
+ Rules are defined on models, and they specify how actions determine how to authorize users (or other models).
14
+
15
+ The second parrt of using `rails-action-authorization` is on the controller side. In any action in which authorization is required, run the `check_authorization` method.
16
+
17
+ ### Step One, defining rules
18
+ Suppose we are writing the proverbial blogging application, and we need some way to determine whether a user is permitted to edit a post.
19
+
20
+ The first step is to define a rule for the action that will need authorization. In our case, we'll need authorization for the `edit` and `update` actions. To define a rule, we'll have to edit our `Post` model.
21
+ ```ruby
22
+ # models/Post.rb
23
+
24
+ class Post < ApplicationRecord
25
+ ...
26
+ end
27
+ ```
28
+
29
+ Rules are defined using the `define_rule` method. `define_rule` takes at least one argument that is a string or a symbol that represents the action that needs authorization in the format `controller#action`. In our case, we need to authorize the `edit` and `update` actions like so:
30
+ ```ruby
31
+ # models/Post.rb
32
+ class Post < ApplicationRecord
33
+ ...
34
+ define_rule 'posts#edit', 'posts#update' do |post, user|
35
+ # Authorization code here
36
+ end
37
+ end
38
+ ```
39
+
40
+ `define_rule` can take as many arguments as desired, enabling developers to define authorization rules for multiple actions simultaneously.
41
+
42
+ The block must return `true` or `false`, `true` if the user is permitted to edit the post
43
+ and `false` if the user is not permitted. The block itself will always yield the resource
44
+ that is being authorized as the first argument and the actor requesting authorization as the second argument. The first argument should always be the type as the class in which `define_rule` is called (if you ever encounter a case where it is not, then please report it as a bug). The second argument could technically be any model, but will probably most often be a `User` instance.
45
+
46
+ Since we only want a post's author to be able to edit a post, we'll define our rule as follows:
47
+ ```ruby
48
+ # models/Post.rb
49
+ class Post < ApplicationRecord
50
+ ...
51
+ define_rule 'posts#edit', 'posts#update' do |post, user|
52
+ post.author == user
53
+ end
54
+ end
55
+ ```
56
+
57
+ Next, we have to check the authorization of the user in each controller. The only thing required
58
+ to do this is to invoke `check_authorization` somewhere in each action that requries authorization.
59
+ ```ruby
60
+ # controllers/posts_controller.rb
61
+ class PostsController < ApplicationController
62
+ before_action :authorize_user, only: [:edit, :update]
63
+
64
+ ...
65
+
66
+ def edit
67
+ ...
68
+ #You will be able to reference @post in your views as usuals.
69
+ end
70
+
71
+ def update
72
+ ...
73
+ end
74
+
75
+ private
76
+
77
+ def authorize_user
78
+ @post = check_authorization(Post.find_by(id: params[:id]), current_user) # Here, use your own method for getting the current user.
79
+
80
+ end
81
+ end
82
+ ```
83
+ Notice that we are able to call check_authorization in a `before_action`. This is because `check_authorization`
84
+ knows how to identify the action it is called in without requiring developers to specify it themselves.
85
+
86
+ This will work great when the owner tries to edit his own posts, but if another user attempts to edit
87
+ a post, the server will raise an error. `rails-action-authorization` raises a `ForbiddenError` if a
88
+ user fails to be authorized in order to eliminate potential ambiguity of returning nil or some other
89
+ value.
90
+
91
+ In order to handle authorization failures, we'll have to adapt our controller slightly:
92
+ ```ruby
93
+ # controllers/posts_controller.rb
94
+ class PostsController < ApplicationController
95
+ before_action :authorize_user, only: [:edit, :update]
96
+ rescue_from ActionAuthorization::ForbiddenError, with:
97
+ :handle_forbidden
98
+
99
+ ...
100
+
101
+ def edit
102
+ ...
103
+ #You will be able to reference @post in your views as usuals.
104
+ end
105
+
106
+ def update
107
+ ...
108
+ end
109
+
110
+ private
111
+
112
+ def authorize_user
113
+ @post = check_authorization(Post.find_by(id: params[:id]), current_user) # Here, use your own method for getting the current user.
114
+ end
115
+
116
+ def handle_forbidden
117
+ render '403', status: 403
118
+ end
119
+ end
120
+ ```
121
+ This will cause the execution of any action to cease immediately when a user fails to authorize, and
122
+ instead run the code in `handle_forbidden`.
123
+
124
+ Of course, the above example assumes that you have a template for a `403` error, but you can run
125
+ whatever code is necessary in your case.
126
+
127
+ ## Installation
128
+ Add this line to your application's Gemfile:
129
+
130
+ ```ruby
131
+ gem 'rails-action-authorization'
132
+ ```
133
+
134
+ And then execute:
135
+ ```bash
136
+ $ bundle
137
+ ```
138
+
139
+ Or install it yourself as:
140
+ ```bash
141
+ $ gem install rails-action-authorization
142
+ ```
143
+
144
+ ## License
145
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Authorizer'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ require 'bundler/gem_tasks'
18
+
19
+ require 'rake/testtask'
20
+
21
+ Rake::TestTask.new(:test) do |t|
22
+ t.libs << 'test'
23
+ t.pattern = 'test/**/*_test.rb'
24
+ t.verbose = false
25
+ end
26
+
27
+ task default: :test
data/lib/authorizer.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "authorizer/railtie"
2
+
3
+ module ActionAuthorization
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,21 @@
1
+ module ActionAuthorization
2
+ OPTIONS = [:authorize_associated, :behavior]
3
+
4
+ POSSIBILITIES = [:allow_all, :deny_all, :filter]
5
+
6
+ class ActionController::Metal
7
+ def check_authorization(resource, authorizee, **options)
8
+ action = "#{params[:controller]}##{action_name}"
9
+
10
+ if resource.respond_to?(:length)
11
+ return resource if resource.length == 0
12
+ r = Resource.new(action, authorizee, *resource, **options)
13
+ result = r.get
14
+ else
15
+ result = resource.is_authorized(action, authorizee)
16
+ end
17
+
18
+ result
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ module ActionAuthorization
2
+ class ActiveRecord::Base
3
+ def self.get_perms
4
+ unless (self.class_variables.include?(:'@@perms'))
5
+ @@perms = {}
6
+ end
7
+ init_fallback_rule
8
+ return @@perms
9
+ end
10
+
11
+ def self.init_fallback_rule
12
+ @@fallback_rule = nil unless (self.class_variable_defined?(:@@fallback_rule))
13
+ end
14
+
15
+ def self.define_rule(*names, &block)
16
+ perms = self.get_perms
17
+ names.each {|name| perms[name.to_sym] = block}
18
+ end
19
+
20
+ def self.set_fallback_rule(&rule)
21
+ @@fallback_rule = rule
22
+ end
23
+
24
+ def is_authorized(action, authorizee)
25
+ symbol = action.to_sym
26
+ perms = self.class.get_perms
27
+
28
+ authorized = false
29
+ authorized = perms[symbol].(self, authorizee) if perms[symbol]
30
+ authorized = @@fallback_rule.(self, authorizee) if @@fallback_rule && !perms[symbol]
31
+
32
+ raise ForbiddenError.new(
33
+ "Actor #{authorizee} is not authorized to perform action #{action} on resource #{self}."
34
+ ) unless authorized
35
+
36
+ self
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,4 @@
1
+ require_relative './exceptions'
2
+ require_relative './active_record_patch'
3
+ require_relative './action_controller_patch'
4
+ require_relative './resource'
@@ -0,0 +1,9 @@
1
+ module ActionAuthorization
2
+ class AuthorizationError < ::StandardError
3
+
4
+ end
5
+
6
+ class ForbiddenError < AuthorizationError
7
+
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ require_relative './authorizer.rb'
2
+
3
+ module Authorizer
4
+ class Railtie < ::Rails::Railtie
5
+ # ActiveRecord::Base.include Authorizer::ModelClassMethods
6
+ # ActionController::API.include Authorizer::ControllerMethods
7
+ end
8
+ end
@@ -0,0 +1,49 @@
1
+ module ActionAuthorization
2
+ class Resource
3
+ attr_reader :action, :actor, :resources, :options
4
+
5
+ def initialize(action, actor, *resources, **options)
6
+ @action = action
7
+ @actor = actor
8
+ @resources = resources
9
+ @options = options
10
+ end
11
+
12
+ def get
13
+ return @resources if @resources.nil?
14
+ return @resources if @resources.length == 0
15
+
16
+ behavior = @options[:behavior]
17
+ if !behavior
18
+ behavior = :filter
19
+ end
20
+
21
+ case behavior
22
+ when :allow_all
23
+ collect_permitted(return_res: true) {|results| results.length > 0}
24
+ when :deny_all
25
+ collect_permitted {|results| results.length == @resources.length}
26
+ when :filter
27
+ collect_permitted {|results| results.length > 0}
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def collect_permitted(return_res: false)
34
+ results = @resources.filter do |r|
35
+ begin
36
+ r.is_authorized(@action, @actor) != nil
37
+ rescue
38
+ false
39
+ end
40
+ end
41
+
42
+ unless yield(results)
43
+ raise ForbiddenError
44
+ end
45
+ return @resources if return_res
46
+ results
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module ActionAuthorization
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :authorizer do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-action-authorization
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Luchuk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-04-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.2
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 6.0.2.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 6.0.2
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 6.0.2.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: sqlite3
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: codecov
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ description: Rails Action Authorization adds an authorization framework for controller
62
+ actions. Rails Action Authorization is designed to be extremely lightweight and
63
+ flexible, enabling developers to spend less time trying to build complex authorization
64
+ systems. It's unopinionated design makes it easy to define any kind of authorization
65
+ rules with minimal effort.
66
+ email:
67
+ - andrew.luchuk@outlook.com
68
+ executables: []
69
+ extensions: []
70
+ extra_rdoc_files: []
71
+ files:
72
+ - MIT-LICENSE
73
+ - README.md
74
+ - Rakefile
75
+ - lib/authorizer.rb
76
+ - lib/authorizer/action_controller_patch.rb
77
+ - lib/authorizer/active_record_patch.rb
78
+ - lib/authorizer/authorizer.rb
79
+ - lib/authorizer/exceptions.rb
80
+ - lib/authorizer/railtie.rb
81
+ - lib/authorizer/resource.rb
82
+ - lib/authorizer/version.rb
83
+ - lib/tasks/authorizer_tasks.rake
84
+ homepage: https://github.com/speratus/rails-action-authorization
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubygems_version: 3.0.6
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Rails Action Authorization adds an authorization framework for controller
107
+ actions.
108
+ test_files: []