api_guardian 0.1.0.pre
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/MIT-LICENSE +20 -0
- data/README.md +125 -0
- data/Rakefile +30 -0
- data/app/controllers/api_guardian/api_controller.rb +112 -0
- data/app/controllers/api_guardian/application_controller.rb +11 -0
- data/app/controllers/api_guardian/permissions_controller.rb +7 -0
- data/app/controllers/api_guardian/registration_controller.rb +38 -0
- data/app/controllers/api_guardian/roles_controller.rb +19 -0
- data/app/controllers/api_guardian/users_controller.rb +20 -0
- data/app/models/api_guardian/permission.rb +14 -0
- data/app/models/api_guardian/role.rb +97 -0
- data/app/models/api_guardian/role_permission.rb +8 -0
- data/app/models/api_guardian/user.rb +23 -0
- data/app/serializers/api_guardian/permission_serializer.rb +7 -0
- data/app/serializers/api_guardian/role_serializer.rb +7 -0
- data/app/serializers/api_guardian/user_serializer.rb +10 -0
- data/config/initializers/api_guardian.rb +10 -0
- data/config/initializers/doorkeeper.rb +143 -0
- data/config/routes.rb +20 -0
- data/db/migrate/20151117191338_api_guardian_enable_uuid_extension.rb +5 -0
- data/db/migrate/20151117191911_create_api_guardian_roles.rb +9 -0
- data/db/migrate/20151117195618_create_api_guardian_users.rb +25 -0
- data/db/migrate/20151117212826_create_api_guardian_permissions.rb +10 -0
- data/db/migrate/20151117213145_create_api_guardian_role_permissions.rb +11 -0
- data/db/migrate/20151117225238_create_doorkeeper_tables.rb +42 -0
- data/db/seeds.rb +32 -0
- data/lib/api_guardian.rb +80 -0
- data/lib/api_guardian/concerns/api_errors/handler.rb +145 -0
- data/lib/api_guardian/concerns/api_errors/renderer.rb +45 -0
- data/lib/api_guardian/concerns/api_request/validator.rb +66 -0
- data/lib/api_guardian/configuration.rb +171 -0
- data/lib/api_guardian/engine.rb +23 -0
- data/lib/api_guardian/errors/invalid_content_type_error.rb +6 -0
- data/lib/api_guardian/errors/invalid_permission_name_error.rb +6 -0
- data/lib/api_guardian/errors/invalid_request_body_error.rb +6 -0
- data/lib/api_guardian/errors/invalid_request_resource_id_error.rb +6 -0
- data/lib/api_guardian/errors/invalid_request_resource_type_error.rb +6 -0
- data/lib/api_guardian/errors/invalid_update_action_error.rb +6 -0
- data/lib/api_guardian/errors/reset_token_expired_error.rb +6 -0
- data/lib/api_guardian/errors/reset_token_user_mismatch_error.rb +6 -0
- data/lib/api_guardian/policies/application_policy.rb +65 -0
- data/lib/api_guardian/policies/permission_policy.rb +15 -0
- data/lib/api_guardian/policies/role_policy.rb +15 -0
- data/lib/api_guardian/policies/user_policy.rb +23 -0
- data/lib/api_guardian/stores/base.rb +53 -0
- data/lib/api_guardian/stores/permission_store.rb +6 -0
- data/lib/api_guardian/stores/role_store.rb +9 -0
- data/lib/api_guardian/stores/user_store.rb +86 -0
- data/lib/api_guardian/version.rb +3 -0
- data/lib/generators/api_guardian/install/USAGE +8 -0
- data/lib/generators/api_guardian/install/install_generator.rb +19 -0
- data/lib/generators/api_guardian/install/templates/README +1 -0
- data/lib/generators/api_guardian/install/templates/api_guardian.rb +5 -0
- data/lib/tasks/api_guardian_tasks.rake +4 -0
- data/spec/concerns/api_errors/handler_spec.rb +114 -0
- data/spec/concerns/api_request/validator_spec.rb +102 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +25 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +13 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/schema.rb +104 -0
- data/spec/dummy/log/test.log +5031 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/permissions.rb +6 -0
- data/spec/factories/role_permissions.rb +6 -0
- data/spec/factories/roles.rb +24 -0
- data/spec/factories/users.rb +11 -0
- data/spec/models/permission_spec.rb +28 -0
- data/spec/models/role_permission_spec.rb +27 -0
- data/spec/models/role_spec.rb +209 -0
- data/spec/models/user_spec.rb +44 -0
- data/spec/policies/application_policy_spec.rb +118 -0
- data/spec/policies/permission_policy_spec.rb +28 -0
- data/spec/policies/role_policy_spec.rb +28 -0
- data/spec/policies/user_policy_spec.rb +29 -0
- data/spec/requests/permissions_controller_spec.rb +19 -0
- data/spec/requests/registration_controller_spec.rb +151 -0
- data/spec/requests/roles_controller_spec.rb +75 -0
- data/spec/requests/users_controller_spec.rb +75 -0
- data/spec/spec_helper.rb +138 -0
- data/spec/stores/base_spec.rb +113 -0
- data/spec/stores/permission_store_spec.rb +2 -0
- data/spec/stores/role_store_spec.rb +12 -0
- data/spec/stores/user_store_spec.rb +144 -0
- data/spec/support/controller_concern_test_helpers.rb +21 -0
- data/spec/support/matchers.rb +37 -0
- data/spec/support/request_helpers.rb +111 -0
- metadata +508 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: b3cb31c1deac4f774db0a75388fdc08c6b490f95
|
|
4
|
+
data.tar.gz: 44fc98728a248791eeee42aaeb18191db8652afb
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: d5a0bb945c2c13444138979d60703de6c47ef2239542a73cd3c607652faef4a8ba65613b5e0a4a79464dcb85b4bda6fe44b0a96e251bc9ce2c97175a5686426b
|
|
7
|
+
data.tar.gz: 510271fa761b9fc922fce424fe94da0c47e7594b8aa24aa8aec8bdf8b8375395aa6177a2130cbbc0bea3941844e6a71c1499a8ff05e5e719fb20d3268a0d3a47
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2015 Travis Vignon
|
|
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,125 @@
|
|
|
1
|
+
# Api Guardian
|
|
2
|
+
|
|
3
|
+
Drop in authorization and authentication suite for Rails APIs.
|
|
4
|
+
|
|
5
|
+
## **\*\*This gem is in alpha stages and is not feature complete. It should not be used in production!\*\***
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
ApiGuardian includes the following features out of the box:
|
|
10
|
+
|
|
11
|
+
* User registration (email/pass)
|
|
12
|
+
* Password reset workflow
|
|
13
|
+
* Roles
|
|
14
|
+
* Permissions
|
|
15
|
+
* Stateless authentication using OAuth2 (via [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) and [Doorkeeper::JWT](https://github.com/chriswarren/doorkeeper-jwt))
|
|
16
|
+
* Policy enforcement (via [Pundit](https://github.com/elabs/pundit))
|
|
17
|
+
* Serialization to [JSON API](http://jsonapi.org/) (via [AMS](https://github.com/rails-api/active_model_serializers))
|
|
18
|
+
* Two-factor auth (TODO)
|
|
19
|
+
* External Login (TODO)
|
|
20
|
+
|
|
21
|
+
What doesn't it include?
|
|
22
|
+
|
|
23
|
+
* Stateful session support (Cookies)
|
|
24
|
+
* HTML/CSS/JS or views of any kind.
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
* PostgreSQL >= 9.1 (uuid-ossp support)
|
|
29
|
+
|
|
30
|
+
**Note: For now, your app must use a PostgreSQL database.** This is because ApiGuardian is using UUID primary keys for all records.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
### First
|
|
35
|
+
|
|
36
|
+
Put this in your Gemfile:
|
|
37
|
+
|
|
38
|
+
```rb
|
|
39
|
+
gem 'api_guardian'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Second
|
|
43
|
+
|
|
44
|
+
Run this command:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
rake generate api_guardian:install
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This will add an initializer, mount the routes, and, copy the migrations/seed files.
|
|
51
|
+
You will need to follow this with:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
rake db:migrate
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Third
|
|
58
|
+
|
|
59
|
+
To Do
|
|
60
|
+
|
|
61
|
+
### Finally
|
|
62
|
+
|
|
63
|
+
To Do
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
### Roles
|
|
68
|
+
|
|
69
|
+
To Do
|
|
70
|
+
|
|
71
|
+
### Permissions
|
|
72
|
+
|
|
73
|
+
To Do
|
|
74
|
+
|
|
75
|
+
### Users
|
|
76
|
+
|
|
77
|
+
To Do
|
|
78
|
+
|
|
79
|
+
## Roadmap
|
|
80
|
+
|
|
81
|
+
* controller actions:
|
|
82
|
+
* Assign permissions to role by name
|
|
83
|
+
* validate user password
|
|
84
|
+
* config
|
|
85
|
+
* password settings (44:1?)
|
|
86
|
+
* devise_zxcvbn
|
|
87
|
+
* user lockouts
|
|
88
|
+
* 2fa settings
|
|
89
|
+
* ???
|
|
90
|
+
* Generators
|
|
91
|
+
* install (initializer, migrations, seed, routes)
|
|
92
|
+
* ???
|
|
93
|
+
* omniauth
|
|
94
|
+
* Request logging
|
|
95
|
+
* Sessions/Devices (attach to tokens)
|
|
96
|
+
* Activity/Events (User signed in, User authenticated at...)
|
|
97
|
+
* Email Service/SMS Service
|
|
98
|
+
* Account lockout
|
|
99
|
+
* SSO
|
|
100
|
+
* digits integration
|
|
101
|
+
* Multi-tenancy
|
|
102
|
+
* Account lockout (failed login attempts)
|
|
103
|
+
* 2FA
|
|
104
|
+
* http://blog.meldium.com/home/2013/8/23/screw-up-two-factor-authentication
|
|
105
|
+
* https://www.authy.com/product/
|
|
106
|
+
* https://github.com/heapsource/active_model_otp + Twilio
|
|
107
|
+
* Fix for JWT storage: https://github.com/doorkeeper-gem/doorkeeper/wiki/How-to-fix-PostgreSQL-error-on-index-row-size
|
|
108
|
+
* Cache
|
|
109
|
+
|
|
110
|
+
## Getting Help
|
|
111
|
+
|
|
112
|
+
If you find a bug, please report an [Issue](https://github.com/lookitsatravis/api_guardian/issues).
|
|
113
|
+
|
|
114
|
+
If you have a question, please post to [Stack Overflow](https://stackoverflow.com/questions/tagged/api_guardian).
|
|
115
|
+
|
|
116
|
+
Thanks!
|
|
117
|
+
|
|
118
|
+
## Contributing
|
|
119
|
+
|
|
120
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
ApiGuardian is copyright © 2015 Travis Vignon. It is free software, and may be
|
|
125
|
+
redistributed under the terms specified in the [`MIT-LICENSE`](MIT-LICENSE) file.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
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 = 'Guarantor'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
|
18
|
+
load 'rails/tasks/engine.rake'
|
|
19
|
+
|
|
20
|
+
load 'rails/tasks/statistics.rake'
|
|
21
|
+
|
|
22
|
+
Bundler::GemHelper.install_tasks
|
|
23
|
+
|
|
24
|
+
require 'rspec/core'
|
|
25
|
+
require 'rspec/core/rake_task'
|
|
26
|
+
|
|
27
|
+
desc 'Run all specs in spec directory (excluding plugin specs)'
|
|
28
|
+
RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
|
|
29
|
+
|
|
30
|
+
task default: :spec
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# TODO: User. should be moved to UserStore.
|
|
2
|
+
module ApiGuardian
|
|
3
|
+
class ApiController < ActionController::API
|
|
4
|
+
include ::Pundit
|
|
5
|
+
include ApiGuardian::Concerns::ApiErrors::Handler
|
|
6
|
+
include ApiGuardian::Concerns::ApiRequest::Validator
|
|
7
|
+
|
|
8
|
+
before_action :doorkeeper_authorize!, except: [:not_found]
|
|
9
|
+
before_action :set_current_user
|
|
10
|
+
before_action :prep_response
|
|
11
|
+
before_action :validate_api_request, except: [:not_found]
|
|
12
|
+
before_action :find_and_authorize_resource, except: [:index, :new, :create, :not_found]
|
|
13
|
+
after_action :verify_policy_scoped, only: :index
|
|
14
|
+
after_action :verify_authorized, except: [:index, :not_found]
|
|
15
|
+
|
|
16
|
+
rescue_from Exception, with: :api_error_handler
|
|
17
|
+
|
|
18
|
+
attr_reader :current_user
|
|
19
|
+
|
|
20
|
+
def index
|
|
21
|
+
@resources = resource_store.paginate(page_params[:number], page_params[:size])
|
|
22
|
+
render json: @resources, include: includes
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def show
|
|
26
|
+
render json: @resource, include: includes
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create
|
|
30
|
+
authorize resource_class
|
|
31
|
+
@resource = resource_store.create(create_resource_params)
|
|
32
|
+
render json: @resource, status: :created, include: includes
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def update
|
|
36
|
+
@resource = resource_store.update(@resource, update_resource_params)
|
|
37
|
+
render json: @resource, include: includes
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def destroy
|
|
41
|
+
@resource.destroy!
|
|
42
|
+
head :no_content
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def not_found
|
|
46
|
+
render_not_found
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
protected
|
|
50
|
+
|
|
51
|
+
def find_and_authorize_resource
|
|
52
|
+
@resource = resource_store.find(params[:id])
|
|
53
|
+
authorize @resource
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def resource_store
|
|
57
|
+
@resource_store ||= ('ApiGuardian::Stores::' + resource_name + 'Store').constantize.new(policy_scope(resource_class))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def resource_name
|
|
61
|
+
@resource_name ||= controller_name.classify
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def resource_class
|
|
65
|
+
@resource_class ||= ApiGuardian.send("#{resource_name.downcase}_class")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# :nocov:
|
|
69
|
+
def includes
|
|
70
|
+
fail 'This needs to be overriden by a child of the API ApplicationController.'
|
|
71
|
+
end
|
|
72
|
+
# :nocov:
|
|
73
|
+
|
|
74
|
+
def create_resource_params
|
|
75
|
+
params.require(:data).require(:attributes).permit(create_params)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def update_resource_params
|
|
79
|
+
params.require(:data).require(:attributes).permit(update_params)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# :nocov:
|
|
83
|
+
def create_params
|
|
84
|
+
fail 'This needs to be overriden by a child of the API ApplicationController.'
|
|
85
|
+
end
|
|
86
|
+
# :nocov:
|
|
87
|
+
|
|
88
|
+
def page_params
|
|
89
|
+
params.fetch(:page, number: 1, size: 25)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# :nocov:
|
|
93
|
+
def update_params
|
|
94
|
+
fail 'This needs to be overriden by a child of the API ApplicationController.'
|
|
95
|
+
end
|
|
96
|
+
# :nocov:
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def set_current_user
|
|
101
|
+
@current_user = ApiGuardian.user_class.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def current_resource_owner
|
|
105
|
+
ApiGuardian.user_class.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def prep_response
|
|
109
|
+
response.headers['Content-Type'] = 'application/vnd.api+json'
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module ApiGuardian
|
|
2
|
+
class RegistrationController < ApiGuardian::ApplicationController
|
|
3
|
+
def create
|
|
4
|
+
@user = ApiGuardian::Stores::UserStore.register(register_params)
|
|
5
|
+
render json: @user, status: :created, include: ['role']
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def reset_password
|
|
9
|
+
if ApiGuardian::Stores::UserStore.reset_password(reset_password_params)
|
|
10
|
+
head :no_content
|
|
11
|
+
else
|
|
12
|
+
render_not_found
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def complete_reset_password
|
|
17
|
+
if ApiGuardian::Stores::UserStore.complete_reset_password(complete_reset_password_params)
|
|
18
|
+
head :no_content
|
|
19
|
+
else
|
|
20
|
+
render_not_found
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected
|
|
25
|
+
|
|
26
|
+
def register_params
|
|
27
|
+
params.permit(:email, :password, :password_confirmation)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def reset_password_params
|
|
31
|
+
params.fetch(:email)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def complete_reset_password_params
|
|
35
|
+
params.permit(:token, :email, :password, :password_confirmation)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ApiGuardian
|
|
2
|
+
class UsersController < ApiController
|
|
3
|
+
protected
|
|
4
|
+
|
|
5
|
+
def includes
|
|
6
|
+
['role']
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def create_params
|
|
10
|
+
[
|
|
11
|
+
:first_name, :last_name, :email, :phone_number, :role_id, :password,
|
|
12
|
+
:password_confirmation
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def update_params
|
|
17
|
+
create_params
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module ApiGuardian
|
|
2
|
+
class Permission < ActiveRecord::Base
|
|
3
|
+
has_many :role_permissions, class_name: ApiGuardian.role_permission_class.to_s
|
|
4
|
+
has_many :roles, through: :role_permissions, class_name: ApiGuardian.role_class.to_s
|
|
5
|
+
|
|
6
|
+
validates :name, uniqueness: true
|
|
7
|
+
validates :name, :desc, presence: true
|
|
8
|
+
|
|
9
|
+
# Class Methods
|
|
10
|
+
def self.policy_class
|
|
11
|
+
ApiGuardian::Policies::PermissionPolicy
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# TODO: Permission. should be moved to PermissionStore.
|
|
2
|
+
module ApiGuardian
|
|
3
|
+
class Role < ActiveRecord::Base
|
|
4
|
+
has_many :users, class_name: ApiGuardian.user_class.to_s
|
|
5
|
+
has_many :role_permissions, class_name: ApiGuardian.role_permission_class.to_s
|
|
6
|
+
|
|
7
|
+
validates :name, uniqueness: true, presence: true
|
|
8
|
+
validates :default, uniqueness: true, if: proc { |r| r.default? }
|
|
9
|
+
|
|
10
|
+
scope :default, -> { where(default: true) }
|
|
11
|
+
|
|
12
|
+
# Class Methods
|
|
13
|
+
def self.default_role
|
|
14
|
+
default.first
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.policy_class
|
|
18
|
+
ApiGuardian::Policies::RolePolicy
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Instance Methods
|
|
22
|
+
def can?(action)
|
|
23
|
+
if action.is_a?(Array)
|
|
24
|
+
grants = []
|
|
25
|
+
action.each do |a|
|
|
26
|
+
perm = Permission.find_by_name(action)
|
|
27
|
+
fail ApiGuardian::Errors::InvalidPermissionNameError, "Permission '#{a}' is not valid." unless perm
|
|
28
|
+
|
|
29
|
+
role_permissions.includes(:permission).find_each do |rp|
|
|
30
|
+
grants.push rp.granted if rp.permission.name == a
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
return grants.include? true if grants.count > 0 # otherwise this permission wasn't found at all
|
|
34
|
+
else
|
|
35
|
+
perm = Permission.find_by_name(action)
|
|
36
|
+
fail ApiGuardian::Errors::InvalidPermissionNameError, "Permission '#{action}' is not valid." unless perm
|
|
37
|
+
|
|
38
|
+
role_permissions.includes(:permission).find_each do |rp|
|
|
39
|
+
return rp.granted if rp.permission.name == action
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def cannot?(action)
|
|
47
|
+
!can? action
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def permissions
|
|
51
|
+
arr = role_permissions.includes(:permission).map do |rp|
|
|
52
|
+
rp.permission.name if rp.granted
|
|
53
|
+
end.compact
|
|
54
|
+
|
|
55
|
+
# We want to simplify returned permissions when user has "manage" for a
|
|
56
|
+
# given resource by only returing that one instead of individual ones.
|
|
57
|
+
arr.map do |p|
|
|
58
|
+
val = nil
|
|
59
|
+
if p.include? ':manage'
|
|
60
|
+
val = p
|
|
61
|
+
else
|
|
62
|
+
resource = p.split(':')[0]
|
|
63
|
+
val = p unless arr.include? "#{resource}:manage"
|
|
64
|
+
end
|
|
65
|
+
val
|
|
66
|
+
end.compact
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def create_default_permissions(granted)
|
|
70
|
+
Permission.find_each do |p|
|
|
71
|
+
role_permissions.create(permission: p, granted: granted) unless role_permissions.include? p
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def add_permission(name)
|
|
76
|
+
perm = Permission.find_by_name(name)
|
|
77
|
+
fail ApiGuardian::Errors::InvalidPermissionNameError, "Permission '#{name}' is not valid." unless perm
|
|
78
|
+
|
|
79
|
+
role_permissions.each do |rp|
|
|
80
|
+
return rp.update_attribute(:granted, true) if rp.permission.name == name
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
role_permissions.create(permission: perm, granted: true)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def remove_permission(name, destroy = false)
|
|
87
|
+
role_permissions.each do |rp|
|
|
88
|
+
next unless rp.permission.name == name
|
|
89
|
+
if destroy
|
|
90
|
+
rp.destroy
|
|
91
|
+
else
|
|
92
|
+
rp.update_attribute(:granted, false)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|