api_authorization 0.1.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 94acc2d7dd25207a51d40cfb954a679042f15a0b9d9b68c4fd36f75cfea3fbd9
4
+ data.tar.gz: cdef5a4d61e6f6d83f8787016281ff959bd2e0e820fd45d9eb4c7030239dac98
5
+ SHA512:
6
+ metadata.gz: ecf08b501bf862dcef4b1bb603c2255aee2c5db3d724a3aeafc171829d1cf303582cca2568aae82a85519e682a0809c8345880a2e6a62729663fea2ed06b8be4
7
+ data.tar.gz: 46fd8d2142abd9038bbffd6c232a9a68df34c1beca042f3c0adfd07de2587f6b7675a71dc11957c1500621d9fcc289f9ebb4f2096ebac804f38c1b9f294ba368
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Azdren Ymeri
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.
@@ -0,0 +1,51 @@
1
+ # Api Authorization (The gem is in development)
2
+ A multiple role-based authorization, based on controller actions.
3
+
4
+ ![Tests](https://github.com/montedelgallo/api-authorization/workflows/Ruby/badge.svg?branch=master)
5
+ ![Ruby Gem](https://github.com/montedelgallo/api-authorization/workflows/Ruby%20Gem/badge.svg?branch=master)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ##### Database Model
9
+ ![db_model](model.jpg)
10
+ ## Installation
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'api_authorization'
15
+ ```
16
+
17
+ And then execute:
18
+ ```bash
19
+ $ bundle
20
+ ```
21
+
22
+ Or install it yourself as:
23
+ ```bash
24
+ $ gem install api_authorization
25
+ ```
26
+
27
+ ## Usage
28
+ 1. After you have created your users_table(through devise or manually) next run
29
+ ```bash
30
+ $ rails api_auth:initialize
31
+ ```
32
+ 2. Next populate permissions table with your controllers and actions run:
33
+ ```bash
34
+ $ rails api_auth:create_permissions
35
+ ```
36
+ 3. Include the Authorization module on your `ApplicationController` :
37
+ ```ruby
38
+ include ActionController::Helpers
39
+ include ApiAuthorization
40
+ enable_role_authorization
41
+ ```
42
+ 4. DONE
43
+
44
+ ##### More CLI commands will be published soon
45
+
46
+ ## Contributing
47
+ Feel free to suggest a feature or report a bug.
48
+ #### [Code Of Conduct](CODE_OF_CONDUCT.md)
49
+
50
+ ## License
51
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'ApiAuthorization'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.md')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ require 'bundler/gem_tasks'
20
+
21
+ require 'rake/testtask'
22
+
23
+ Rake::TestTask.new(:test) do |_t|
24
+ # t.libs << 'spec'
25
+ # t.pattern = 'test/**/*_test.rb'
26
+ # t.verbose = true
27
+ sh 'rspec --format documentation'
28
+ end
29
+
30
+ task default: :test
31
+ Rake.add_rakelib 'lib/tasks'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ rake api_auth: init
@@ -0,0 +1,5 @@
1
+ require "api_authorization/railtie"
2
+
3
+ module ApiAuthorization
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiAuthorization
4
+ extend ActiveSupport::Concern
5
+
6
+ included do |_mod|
7
+ helper_method :check_role
8
+
9
+ def self.enable_role_authorization
10
+ before_action :check_role
11
+ end
12
+
13
+ # Filter the request with by controller and action to know if a user
14
+ # can execute that action in that controller
15
+ #
16
+ # @return when the user is allowed
17
+ def check_role
18
+ puts 'AUTHORIZATION: Checking user roles' if Rails.env == 'development'
19
+ roles = current_user.try(:roles)
20
+ return if roles.find { |role| role.try(:name).try(:downcase) == 'superadmin' }
21
+
22
+ return render json: { error: 'You are not authorized' }, status: 403 if current_user.roles.empty?
23
+
24
+ current_user.roles.each do |role|
25
+ return if role.permissions.where(controller: params['controller'], action: params['action']).count.positive?
26
+ end
27
+
28
+ render json: { error: 'You are not authorized' }, status: 403
29
+ rescue StandardError => e
30
+ render json: { error: 'You are not authorized' }, status: 403
31
+ end
32
+
33
+ # Filter the request params to know wether or not a user has
34
+ # the right to push a certain value as a parameter.
35
+ #
36
+ # example:
37
+ #
38
+ # allowed_params = { anno: [2019] }
39
+ # will remove from the params if the params hash contain a key :anno
40
+ # with a value which is not 2019
41
+ #
42
+ # @params [Action::Parameters]
43
+ # @return [Action::Parameters] without the disallowed key/values.
44
+ def check_allowed_params(params, controller, action)
45
+ puts 'AUTHORIZATION: Checking the request params' if Rails.env == 'development'
46
+
47
+ roles = current_user.try(:permissions)
48
+ return params if roles.find { |role| role.try(:name).try(:downcase) == 'superadmin' }
49
+
50
+ rules = roles.where('controller = ? AND action = ? AND allowed_params IS NOT NULL', controller, action)
51
+
52
+ return params if rules.nil? || rules.empty?
53
+
54
+ rules.each do |rule|
55
+ next if rule.allowed_params.nil?
56
+
57
+ # puts "####### ruleS => #{rule.allowed_params.inspect}"
58
+ rule.allowed_params.each do |k, v|
59
+ puts " key: #{k}, value: #{v}"
60
+ params.delete(k) unless Array(v).include?(params[k]) || v == params[k]
61
+ end
62
+ end
63
+
64
+ params
65
+ end
66
+ end
67
+
68
+ class Railtie < ::Rails::Railtie
69
+ # exportin rake tasks
70
+ rake_tasks do
71
+ load 'tasks/initialize.rake'
72
+ load 'tasks/user_tasks.rake'
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,3 @@
1
+ module ApiAuthorization
2
+ VERSION = '0.1.2'
3
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppRoutes
4
+ class ApplicationRoutes
5
+ def initialize
6
+ @routes = []
7
+
8
+ Rails.application.routes.routes.map do |r|
9
+ route = {
10
+ url: r.defaults[:controller],
11
+ controller: r.defaults[:controller],
12
+ action: r.defaults[:action],
13
+ method: r.verb
14
+ }
15
+ # rotta_finale = {
16
+ # controller: r.defaults[:controller],
17
+ # action: r.defaults[:action]
18
+ # }
19
+ # check = rotta[:controller]
20
+ # verb = rotta[:method]
21
+ # action = rotta[:action]
22
+ # if check && verb != 'PATCH' && action != 'new' && action != 'edit'
23
+ # @routes.push(rotta_finale) if check.include? 'api/v1'
24
+ # end
25
+
26
+ @routes.push(route)
27
+ end
28
+ generate_routes
29
+ end
30
+
31
+ def call
32
+ @routes.group_by { |n| n[:controller] }.each { |_k, v| v.uniq! }
33
+ end
34
+
35
+ def generate_routes
36
+ puts 'Populating permissions_table with routes of the application' if Rails.env == 'development'
37
+ Permission.destroy_all
38
+ @routes.each do |k|
39
+ Permission.create!(controller: k[:controller], action: k[:action])
40
+ end
41
+ puts 'Done!' if Rails.env == 'development'
42
+ end
43
+
44
+ private
45
+
46
+ attr_accessor :routes
47
+ end
48
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './../app_routes/application_routes.rb'
4
+
5
+ namespace :api_auth do
6
+ desc 'Create the initial tables and populate the permissions table'
7
+ task :init, %i[tenant single_role] => [:environment] do |_t, args|
8
+ Apartment::Tenant.switch! args[:tenant] if args[:tenant]
9
+
10
+ puts 'Initializing'
11
+ # TODO: before creating the migration make sure to check that there is
12
+ # no roles tables or permissions tables
13
+ if ActiveRecord::Base.connection.table_exists? 'users'
14
+
15
+ if args[:single_role].nil?
16
+ # creating roles table
17
+ sh 'rails g model role name:string description:text'
18
+ Rake::Task['db:migrate'].invoke
19
+
20
+ # creating a join table between users and roles
21
+ sh 'rails g migration CreateJoinTableUserRole user role'
22
+ sh 'rails db:migrate'
23
+
24
+ else
25
+ sh 'rails g model role name:string description:text user:references'
26
+ sh 'rails db:migrate'
27
+ end
28
+
29
+ # creating permissions table
30
+ sh 'rails g model permission controller:string action:string allowed_params:json'
31
+ sh 'rails db:migrate'
32
+
33
+ # creating a join table between roles and permissions
34
+ sh 'rails g migration CreateJoinTableRolePermission role permission'
35
+ sh 'rails db:migrate'
36
+
37
+ else
38
+ puts 'users table does not exist !'
39
+ next
40
+ end
41
+ puts 'Done !'
42
+ end
43
+
44
+ desc 'Populate the permissions table with all the controllers and actions of the application'
45
+ task :create_permissions, %i[tenant] => [:environment] do |_t, args|
46
+ Apartment::Tenant.switch! args[:tenant] if args[:tenant]
47
+ AppRoutes::ApplicationRoutes.new
48
+ end
49
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :api_auth do
4
+ desc 'Create a default user and make it superadmin'
5
+ task :create_superadmin, [:tenant] => [:environment] do |_t, args|
6
+ Apartment::Tenant.switch args[:tenant] do
7
+ user = User.find_by_email('giovanni@montedelgallo.com')
8
+ role = Role.find_by_name('superadmin')
9
+ if user
10
+ puts 'User giovanni@montedelgallo.com already exists'
11
+ else
12
+ user = User.create!(email: 'giovanni@montedelgallo.com', password: '123456')
13
+ puts "User created with email: 'giovanni@montedelgallo.com', password: '123456'"
14
+ end
15
+ role ||= Role.create!(name: 'superadmin')
16
+ user.roles << role
17
+ end
18
+ puts '###################################################'
19
+ puts '##'
20
+ puts '## USER CREATED:'
21
+ puts '##'
22
+ puts '## email: giovanni@montedelgallo.com'
23
+ puts '## password: 123456'
24
+ puts "## tenant: #{args[:tenant]}"
25
+ puts '##'
26
+ puts '###################################################'
27
+ end
28
+
29
+ desc 'create custom user'
30
+ task :create_user, %i[tenant email password] => [:environment] do |_t, args|
31
+ Apartment::Tenant.switch args[:tenant] do
32
+ User.create!(email: args[:email], password: args[:password])
33
+ puts '###################################################'
34
+ puts '##'
35
+ puts '## USER CREATED:'
36
+ puts '##'
37
+ puts "## email: #{args[:email]}"
38
+ puts "## password: #{args[:password]}"
39
+ puts "## tenant: #{args[:tenant]}"
40
+ puts '##'
41
+ puts '###################################################'
42
+ end
43
+ end
44
+
45
+ desc 'Reset user password'
46
+ task :reset_password, %i[tenant email new_password] => [:environment] do |_t, args|
47
+ # rails "auth:reset_password[uno, giovanni@montedelgallo.com, password]"
48
+ Apartment::Tenant.switch args[:tenant] do
49
+ user = User.find_by_email(args[:email])
50
+ user.password = args[:new_password]
51
+ user.save
52
+ end
53
+
54
+ puts '###################################################'
55
+ puts '##'
56
+ puts '## USER PASSWORD RESET:'
57
+ puts '##'
58
+ puts "## email: #{args[:email]}"
59
+ puts "## password: #{args[:new_password]}"
60
+ puts "## tenant: #{args[:tenant]}"
61
+ puts '##'
62
+ puts '###################################################'
63
+ end
64
+
65
+ desc 'Make a user superadmin'
66
+ task :promote_superadmin, %i[tenant email] => [:environment] do |_t, args|
67
+ # rails "auth:reset_password[uno, giovanni@montedelgallo.com, password]"
68
+ Apartment::Tenant.switch args[:tenant] do
69
+ user = User.find_by_email(args[:email])
70
+ role = Role.find_by_name('superadmin')
71
+ role ||= Role.create!(name: 'superadmin')
72
+ user.roles << role
73
+ end
74
+
75
+ puts '###################################################'
76
+ puts '##'
77
+ puts '## USER PROMOTED TO SUPERADMIN:'
78
+ puts '##'
79
+ puts "## email: #{args[:email]}"
80
+ puts "## tenant: #{args[:tenant]}"
81
+ puts '##'
82
+ puts '###################################################'
83
+ end
84
+
85
+ desc 'create admin'
86
+ task :create_admin, %i[email password] => [:environment] do |_t, args|
87
+ Admin.create!(email: args[:email], password: args[:password])
88
+ puts '###################################################'
89
+ puts '##'
90
+ puts '## ADMIN CREATED:'
91
+ puts '##'
92
+ puts "## email: #{args[:email]}"
93
+ puts "## password: #{args[:password]}"
94
+ puts '##'
95
+ puts '###################################################'
96
+ end
97
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: api_authorization
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Giovanni Panasiti
8
+ - Azdren Ymeri
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2020-08-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 6.0.3
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 6.0.3.2
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: 6.0.3
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 6.0.3.2
34
+ - !ruby/object:Gem::Dependency
35
+ name: sqlite3
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ type: :development
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: byebug
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Highly flexible authorization where a user can have more than 1 role
63
+ with differen permissions.
64
+ email:
65
+ - giovanni@montedelgallo.com
66
+ - azdren.ymeri@montedelgallo.com
67
+ executables:
68
+ - api_auth
69
+ extensions: []
70
+ extra_rdoc_files: []
71
+ files:
72
+ - MIT-LICENSE
73
+ - README.md
74
+ - Rakefile
75
+ - bin/api_auth
76
+ - lib/api_authorization.rb
77
+ - lib/api_authorization/railtie.rb
78
+ - lib/api_authorization/version.rb
79
+ - lib/app_routes/application_routes.rb
80
+ - lib/tasks/initialize.rake
81
+ - lib/tasks/user_tasks.rake
82
+ homepage: https://montedelgallo.com/
83
+ licenses:
84
+ - MIT
85
+ metadata:
86
+ allowed_push_host: https://rubygems.org/
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubygems_version: 3.0.3
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: A multi role bassed authorization
106
+ test_files: []