thecore_auth_commons 0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +32 -0
  5. data/app/models/ability.rb +49 -0
  6. data/app/models/action.rb +3 -0
  7. data/app/models/permission.rb +20 -0
  8. data/app/models/permission_role.rb +4 -0
  9. data/app/models/predicate.rb +3 -0
  10. data/app/models/role.rb +13 -0
  11. data/app/models/role_user.rb +4 -0
  12. data/app/models/target.rb +3 -0
  13. data/app/models/user.rb +48 -0
  14. data/config/initializers/after_initialize_thecore_auth_commons.rb +13 -0
  15. data/config/initializers/devise.rb +299 -0
  16. data/config/locales/en.activerecord.yml +11 -0
  17. data/config/locales/it.activerecord.yml +36 -0
  18. data/config/locales/it.permissions.yml +10 -0
  19. data/config/routes.rb +5 -0
  20. data/db/migrate/20200306143408_create_users.rb +51 -0
  21. data/db/migrate/20200306151046_add_admin_field_to_user.rb +5 -0
  22. data/db/migrate/20200306151541_add_first_admin_user.rb +60 -0
  23. data/db/migrate/20200306152740_create_roles.rb +10 -0
  24. data/db/migrate/20200306152816_create_role_users.rb +10 -0
  25. data/db/migrate/20200306153125_add_lock_version_to_user.rb +5 -0
  26. data/db/migrate/20200306153136_add_lock_version_to_role.rb +5 -0
  27. data/db/migrate/20200516215346_add_locked_to_user.rb +5 -0
  28. data/db/migrate/20200518082821_create_permissions.rb +48 -0
  29. data/lib/abilities/thecore_auth_commons.rb +20 -0
  30. data/lib/tasks/thecore_auth_commons_tasks.rake +4 -0
  31. data/lib/thecore_auth_commons.rb +10 -0
  32. data/lib/thecore_auth_commons/engine.rb +12 -0
  33. data/lib/thecore_auth_commons/version.rb +3 -0
  34. data/lib/thecore_auth_commons_actioncontroller_concerns.rb +7 -0
  35. metadata +154 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e94769e7d262d850b7c5531f9bebe433fe00dd934d9b593264605bd92c153d0d
4
+ data.tar.gz: e37f8a876f911fb9cf40723d5bda436125829de14fc85b91816e12dc56be2768
5
+ SHA512:
6
+ metadata.gz: f28ae34cec6fbe45be080c1b1da3d2b346bafd5c4cc1bc2d33830351e94d0207d34e710768405ad97c36f3653c457d2d214fee32a451d93bd59054d97bd7fcf4
7
+ data.tar.gz: e6fea2a4ddcd36efaae3e0867ad2d673d9412e71df1fd1088ec1cb4161dcbc1d034d6b326baf43ac33e4f908d986d082e7bc934e124775a972ce65284046a2d8
@@ -0,0 +1,20 @@
1
+ Copyright 2020
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,28 @@
1
+ # ThecoreAuth
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'thecore_auth_commons'
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install thecore_auth_commons
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,32 @@
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 = 'ThecoreAuthCommons'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Ability
4
+ include CanCan::Ability
5
+
6
+ def initialize(user)
7
+ # Define abilities for the passed in user here. For example:
8
+ #
9
+ # user ||= User.new # guest user (not logged in)
10
+ # if user.admin?
11
+ # can :manage, :all
12
+ # else
13
+ # can :read, :all
14
+ # end
15
+ #
16
+ # The first argument to `can` is the action you are giving the user
17
+ # permission to do.
18
+ # If you pass :manage it will apply to every action. Other common actions
19
+ # here are :read, :create, :update and :destroy.
20
+ #
21
+ # The second argument is the resource the user can perform the action on.
22
+ # If you pass :all it will apply to every resource. Otherwise pass a Ruby
23
+ # class of the resource.
24
+ #
25
+ # The third argument is an optional hash of conditions to further filter the
26
+ # objects.
27
+ # For example, here the user can only update published articles.
28
+ #
29
+ # can :update, Article, :published => true
30
+ #
31
+ # See the wiki for details:
32
+ # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
33
+
34
+ # This will always be the first Ability, since the abilities are "last wins"
35
+ self.merge Abilities::ThecoreAuthCommons.new user
36
+ # Other Abilities
37
+ Abilities.constants(false).each do |ability|
38
+ unless ability.to_s == "ThecoreAuthCommons"
39
+ const = Abilities.const_get(ability)
40
+ self.merge const.new(user) if const.is_a? Class
41
+ end
42
+ end
43
+ # Overrides from the database defined permissions
44
+ ::Permission.joins(roles: :users).where(users: {id: user.id}).order(:id).each do |permission|
45
+ # E.g. can :manage, :all
46
+ self.send(permission.predicate.name.to_sym, permission.action.name.to_sym, (permission.target.name.classify.constantize rescue permission.target.name.to_sym))
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ class Action < ApplicationRecord
2
+ has_many :permissions, dependent: :destroy, inverse_of: :action
3
+ end
@@ -0,0 +1,20 @@
1
+ class Permission < ApplicationRecord
2
+ # REFERENCES
3
+ has_many :permission_roles, dependent: :destroy, inverse_of: :permission
4
+ has_many :roles, through: :permission_roles, inverse_of: :permissions
5
+ belongs_to :predicate, inverse_of: :permissions
6
+ belongs_to :action, inverse_of: :permissions
7
+ belongs_to :target, inverse_of: :permissions
8
+
9
+ # VALIDATIONS
10
+ validates :predicate_id, presence: true, uniqueness: {scope: [:action_id, :target_id]}
11
+ validates :action_id, presence: true
12
+ validates :target_id, presence: true
13
+
14
+ def display_name
15
+ p = (I18n.t "permissions.predicates.#{predicate.name}", default: predicate.name.titleize rescue nil)
16
+ a = (I18n.t "permissions.actions.#{action.name}", default: action.name.titleize rescue nil)
17
+ m = (I18n.t "activerecord.models.#{target.name}", default: target.name.titleize rescue nil)
18
+ [ p, a, m ].join(" ")
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ class PermissionRole < ApplicationRecord
2
+ belongs_to :role, inverse_of: :permission_roles
3
+ belongs_to :permission, inverse_of: :permission_roles
4
+ end
@@ -0,0 +1,3 @@
1
+ class Predicate < ApplicationRecord
2
+ has_many :permissions, dependent: :destroy, inverse_of: :predicate
3
+ end
@@ -0,0 +1,13 @@
1
+ class Role < ApplicationRecord
2
+ # VALIDATIONS
3
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
4
+ # REFERENCES
5
+ has_many :role_users, dependent: :destroy, inverse_of: :role
6
+ has_many :users, through: :role_users, inverse_of: :roles
7
+ has_many :permission_roles, dependent: :destroy, inverse_of: :role
8
+ has_many :permissions, through: :permission_roles, inverse_of: :roles
9
+
10
+ def display_name
11
+ (I18n.t name.parameterize.underscore, default: name.titleize rescue nil)
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ class RoleUser < ApplicationRecord
2
+ belongs_to :user, inverse_of: :role_users
3
+ belongs_to :role, inverse_of: :role_users
4
+ end
@@ -0,0 +1,3 @@
1
+ class Target < ApplicationRecord
2
+ has_many :permissions, dependent: :destroy, inverse_of: :target
3
+ end
@@ -0,0 +1,48 @@
1
+ class User < ApplicationRecord
2
+ # Include default devise modules. Others available are:
3
+ # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
4
+ devise :database_authenticatable
5
+ devise :trackable
6
+ devise :validatable
7
+ # TODO: If it works, these must be added to another gem one which deal
8
+ # more with sessions
9
+ # devise :database_authenticatable
10
+ # devise :rememberable
11
+ # devise :trackable
12
+ # devise :validatable
13
+ # devise :timeoutable, timeout_in: 30.minutes
14
+ # REFERENCES
15
+ has_many :role_users, dependent: :destroy, inverse_of: :user
16
+ has_many :roles, through: :role_users, inverse_of: :users
17
+ # VALIDATIONS
18
+ validates :email, uniqueness: { case_sensitive: false }, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }
19
+ validates :password, presence: true, on: :create
20
+ validates :password_confirmation, presence: true, on: :create
21
+ validate :check_password_and_confirmation_equal
22
+ validates_each :admin do |record, attr, value|
23
+ # Don't want admin == false if the current user is the only admin
24
+ record.errors.add(attr, I18n.t("validation.errors.cannot_unadmin_last_admin")) if record.admin_changed? && record.admin_was == true && User.where(admin: true).count == 1
25
+ end
26
+ validates_each :locked do |record, attr, value|
27
+ # Don't want locked == true if the current user is the only admin
28
+ record.errors.add(attr, I18n.t("validation.errors.cannot_lock_last_admin")) if record.locked_changed? && record.locked_was == false && User.where(locked: false).count == 1
29
+ end
30
+
31
+ def display_name
32
+ email
33
+ end
34
+
35
+ def has_role? role
36
+ roles.include? role.to_s
37
+ end
38
+
39
+ def authenticate password
40
+ self&.valid_password?(password) ? self : nil
41
+ end
42
+
43
+ protected
44
+
45
+ def check_password_and_confirmation_equal
46
+ errors.add(:password, I18n.t("validation.errors.password_and_confirm_must_be_the_same")) unless password == password_confirmation
47
+ end
48
+ end
@@ -0,0 +1,13 @@
1
+ require 'thecore_auth_commons_actioncontroller_concerns'
2
+
3
+ # App Config
4
+ Rails.application.configure do
5
+ config.after_initialize do
6
+ # In development be sure to load all the namespaces
7
+ # in order to have working reflection and meta-programming.
8
+ if Rails.env.development?
9
+ Rails.configuration.eager_load_namespaces.each(&:eager_load!) if Rails.version.to_i == 5 #Rails 5
10
+ Zeitwerk::Loader.eager_load_all if Rails.version.to_i >= 6 #Rails 6
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,299 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Use this hook to configure devise mailer, warden hooks and so forth.
4
+ # Many of these configuration options can be set straight in your model.
5
+ Devise.setup do |config|
6
+ # The secret key used by Devise. Devise uses this key to generate
7
+ # random tokens. Changing this key will render invalid all existing
8
+ # confirmation, reset password and unlock tokens in the database.
9
+ # Devise will use the `secret_key_base` as its `secret_key`
10
+ # by default. You can change it below and use your own secret key.
11
+ # config.secret_key = '3b97afd4baabfd5eb8c118ee25efe06017a8319dd5da4f39b287d20948ff844facb0b9c8daff13b7b437b92868aae71797686dcae3704e45e92d3b37094c9d3d'
12
+
13
+ # ==> Controller configuration
14
+ # Configure the parent class to the devise controllers.
15
+ # config.parent_controller = 'DeviseController'
16
+
17
+ # ==> Mailer Configuration
18
+ # Configure the e-mail address which will be shown in Devise::Mailer,
19
+ # note that it will be overwritten if you use your own mailer class
20
+ # with default "from" parameter.
21
+ config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
22
+
23
+ # Configure the class responsible to send e-mails.
24
+ # config.mailer = 'Devise::Mailer'
25
+
26
+ # Configure the parent class responsible to send e-mails.
27
+ # config.parent_mailer = 'ActionMailer::Base'
28
+
29
+ # ==> ORM configuration
30
+ # Load and configure the ORM. Supports :active_record (default) and
31
+ # :mongoid (bson_ext recommended) by default. Other ORMs may be
32
+ # available as additional gems.
33
+ require 'devise/orm/active_record'
34
+
35
+ # ==> Configuration for any authentication mechanism
36
+ # Configure which keys are used when authenticating a user. The default is
37
+ # just :email. You can configure it to use [:username, :subdomain], so for
38
+ # authenticating a user, both parameters are required. Remember that those
39
+ # parameters are used only when authenticating and not when retrieving from
40
+ # session. If you need permissions, you should implement that in a before filter.
41
+ # You can also supply a hash where the value is a boolean determining whether
42
+ # or not authentication should be aborted when the value is not present.
43
+ # config.authentication_keys = [:email]
44
+
45
+ # Configure parameters from the request object used for authentication. Each entry
46
+ # given should be a request method and it will automatically be passed to the
47
+ # find_for_authentication method and considered in your model lookup. For instance,
48
+ # if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
49
+ # The same considerations mentioned for authentication_keys also apply to request_keys.
50
+ # config.request_keys = []
51
+
52
+ # Configure which authentication keys should be case-insensitive.
53
+ # These keys will be downcased upon creating or modifying a user and when used
54
+ # to authenticate or find a user. Default is :email.
55
+ config.case_insensitive_keys = [:email]
56
+
57
+ # Configure which authentication keys should have whitespace stripped.
58
+ # These keys will have whitespace before and after removed upon creating or
59
+ # modifying a user and when used to authenticate or find a user. Default is :email.
60
+ config.strip_whitespace_keys = [:email]
61
+
62
+ # Tell if authentication through request.params is enabled. True by default.
63
+ # It can be set to an array that will enable params authentication only for the
64
+ # given strategies, for example, `config.params_authenticatable = [:database]` will
65
+ # enable it only for database (email + password) authentication.
66
+ # config.params_authenticatable = true
67
+
68
+ # Tell if authentication through HTTP Auth is enabled. False by default.
69
+ # It can be set to an array that will enable http authentication only for the
70
+ # given strategies, for example, `config.http_authenticatable = [:database]` will
71
+ # enable it only for database authentication. The supported strategies are:
72
+ # :database = Support basic authentication with authentication key + password
73
+ # config.http_authenticatable = false
74
+
75
+ # If 401 status code should be returned for AJAX requests. True by default.
76
+ # config.http_authenticatable_on_xhr = true
77
+
78
+ # The realm used in Http Basic Authentication. 'Application' by default.
79
+ # config.http_authentication_realm = 'Application'
80
+
81
+ # It will change confirmation, password recovery and other workflows
82
+ # to behave the same regardless if the e-mail provided was right or wrong.
83
+ # Does not affect registerable.
84
+ # config.paranoid = true
85
+
86
+ # By default Devise will store the user in session. You can skip storage for
87
+ # particular strategies by setting this option.
88
+ # Notice that if you are skipping storage for all authentication paths, you
89
+ # may want to disable generating routes to Devise's sessions controller by
90
+ # passing skip: :sessions to `devise_for` in your config/routes.rb
91
+ config.skip_session_storage = [:http_auth]
92
+
93
+ # By default, Devise cleans up the CSRF token on authentication to
94
+ # avoid CSRF token fixation attacks. This means that, when using AJAX
95
+ # requests for sign in and sign up, you need to get a new CSRF token
96
+ # from the server. You can disable this option at your own risk.
97
+ # config.clean_up_csrf_token_on_authentication = true
98
+
99
+ # When false, Devise will not attempt to reload routes on eager load.
100
+ # This can reduce the time taken to boot the app but if your application
101
+ # requires the Devise mappings to be loaded during boot time the application
102
+ # won't boot properly.
103
+ # config.reload_routes = true
104
+
105
+ # ==> Configuration for :database_authenticatable
106
+ # For bcrypt, this is the cost for hashing the password and defaults to 11. If
107
+ # using other algorithms, it sets how many times you want the password to be hashed.
108
+ #
109
+ # Limiting the stretches to just one in testing will increase the performance of
110
+ # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
111
+ # a value less than 10 in other environments. Note that, for bcrypt (the default
112
+ # algorithm), the cost increases exponentially with the number of stretches (e.g.
113
+ # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
114
+ config.stretches = Rails.env.test? ? 1 : 11
115
+
116
+ # Set up a pepper to generate the hashed password.
117
+ # config.pepper = 'ec0d64f5b4e32673fdc396433677d4f6b61aaf2ef6081b99b2cc8612f3c24556361eeea86ab2799772618f30e417c965491737a553b03d3e558db85256569971'
118
+
119
+ # Send a notification to the original email when the user's email is changed.
120
+ # config.send_email_changed_notification = false
121
+
122
+ # Send a notification email when the user's password is changed.
123
+ # config.send_password_change_notification = false
124
+
125
+ # ==> Configuration for :confirmable
126
+ # A period that the user is allowed to access the website even without
127
+ # confirming their account. For instance, if set to 2.days, the user will be
128
+ # able to access the website for two days without confirming their account,
129
+ # access will be blocked just in the third day.
130
+ # You can also set it to nil, which will allow the user to access the website
131
+ # without confirming their account.
132
+ # Default is 0.days, meaning the user cannot access the website without
133
+ # confirming their account.
134
+ # config.allow_unconfirmed_access_for = 2.days
135
+
136
+ # A period that the user is allowed to confirm their account before their
137
+ # token becomes invalid. For example, if set to 3.days, the user can confirm
138
+ # their account within 3 days after the mail was sent, but on the fourth day
139
+ # their account can't be confirmed with the token any more.
140
+ # Default is nil, meaning there is no restriction on how long a user can take
141
+ # before confirming their account.
142
+ # config.confirm_within = 3.days
143
+
144
+ # If true, requires any email changes to be confirmed (exactly the same way as
145
+ # initial account confirmation) to be applied. Requires additional unconfirmed_email
146
+ # db field (see migrations). Until confirmed, new email is stored in
147
+ # unconfirmed_email column, and copied to email column on successful confirmation.
148
+ config.reconfirmable = true
149
+
150
+ # Defines which key will be used when confirming an account
151
+ # config.confirmation_keys = [:email]
152
+
153
+ # ==> Configuration for :rememberable
154
+ # The time the user will be remembered without asking for credentials again.
155
+ # config.remember_for = 2.weeks
156
+
157
+ # Invalidates all the remember me tokens when the user signs out.
158
+ config.expire_all_remember_me_on_sign_out = true
159
+
160
+ # If true, extends the user's remember period when remembered via cookie.
161
+ # config.extend_remember_period = false
162
+
163
+ # Options to be passed to the created cookie. For instance, you can set
164
+ # secure: true in order to force SSL only cookies.
165
+ # config.rememberable_options = {}
166
+
167
+ # ==> Configuration for :validatable
168
+ # Range for password length.
169
+ config.password_length = 6..128
170
+
171
+ # Email regex used to validate email formats. It simply asserts that
172
+ # one (and only one) @ exists in the given string. This is mainly
173
+ # to give user feedback and not to assert the e-mail validity.
174
+ config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
175
+
176
+ # ==> Configuration for :timeoutable
177
+ # The time you want to timeout the user session without activity. After this
178
+ # time the user will be asked for credentials again. Default is 30 minutes.
179
+ # config.timeout_in = 30.minutes
180
+
181
+ # ==> Configuration for :lockable
182
+ # Defines which strategy will be used to lock an account.
183
+ # :failed_attempts = Locks an account after a number of failed attempts to sign in.
184
+ # :none = No lock strategy. You should handle locking by yourself.
185
+ # config.lock_strategy = :failed_attempts
186
+
187
+ # Defines which key will be used when locking and unlocking an account
188
+ # config.unlock_keys = [:email]
189
+
190
+ # Defines which strategy will be used to unlock an account.
191
+ # :email = Sends an unlock link to the user email
192
+ # :time = Re-enables login after a certain amount of time (see :unlock_in below)
193
+ # :both = Enables both strategies
194
+ # :none = No unlock strategy. You should handle unlocking by yourself.
195
+ # config.unlock_strategy = :both
196
+
197
+ # Number of authentication tries before locking an account if lock_strategy
198
+ # is failed attempts.
199
+ # config.maximum_attempts = 20
200
+
201
+ # Time interval to unlock the account if :time is enabled as unlock_strategy.
202
+ # config.unlock_in = 1.hour
203
+
204
+ # Warn on the last attempt before the account is locked.
205
+ # config.last_attempt_warning = true
206
+
207
+ # ==> Configuration for :recoverable
208
+ #
209
+ # Defines which key will be used when recovering the password for an account
210
+ # config.reset_password_keys = [:email]
211
+
212
+ # Time interval you can reset your password with a reset password key.
213
+ # Don't put a too small interval or your users won't have the time to
214
+ # change their passwords.
215
+ config.reset_password_within = 6.hours
216
+
217
+ # When set to false, does not sign a user in automatically after their password is
218
+ # reset. Defaults to true, so a user is signed in automatically after a reset.
219
+ # config.sign_in_after_reset_password = true
220
+
221
+ # ==> Configuration for :encryptable
222
+ # Allow you to use another hashing or encryption algorithm besides bcrypt (default).
223
+ # You can use :sha1, :sha512 or algorithms from others authentication tools as
224
+ # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20
225
+ # for default behavior) and :restful_authentication_sha1 (then you should set
226
+ # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).
227
+ #
228
+ # Require the `devise-encryptable` gem when using anything other than bcrypt
229
+ # config.encryptor = :sha512
230
+
231
+ # ==> Scopes configuration
232
+ # Turn scoped views on. Before rendering "sessions/new", it will first check for
233
+ # "users/sessions/new". It's turned off by default because it's slower if you
234
+ # are using only default views.
235
+ # config.scoped_views = false
236
+
237
+ # Configure the default scope given to Warden. By default it's the first
238
+ # devise role declared in your routes (usually :user).
239
+ # config.default_scope = :user
240
+
241
+ # Set this configuration to false if you want /users/sign_out to sign out
242
+ # only the current scope. By default, Devise signs out all scopes.
243
+ # config.sign_out_all_scopes = true
244
+
245
+ # ==> Navigation configuration
246
+ # Lists the formats that should be treated as navigational. Formats like
247
+ # :html, should redirect to the sign in page when the user does not have
248
+ # access, but formats like :xml or :json, should return 401.
249
+ #
250
+ # If you have any extra navigational formats, like :iphone or :mobile, you
251
+ # should add them to the navigational formats lists.
252
+ #
253
+ # The "*/*" below is required to match Internet Explorer requests.
254
+ # config.navigational_formats = ['*/*', :html]
255
+
256
+ # The default HTTP method used to sign out a resource. Default is :delete.
257
+ config.sign_out_via = :delete
258
+
259
+ # ==> OmniAuth
260
+ # Add a new OmniAuth provider. Check the wiki for more information on setting
261
+ # up on your models and hooks.
262
+ # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
263
+
264
+ # ==> Warden configuration
265
+ # If you want to use other strategies, that are not supported by Devise, or
266
+ # change the failure app, you can configure them inside the config.warden block.
267
+ #
268
+ # config.warden do |manager|
269
+ # manager.intercept_401 = false
270
+ # manager.default_strategies(scope: :user).unshift :some_external_strategy
271
+ # end
272
+
273
+ # ==> Mountable engine configurations
274
+ # When using Devise inside an engine, let's call it `MyEngine`, and this engine
275
+ # is mountable, there are some extra configurations to be taken into account.
276
+ # The following options are available, assuming the engine is mounted as:
277
+ #
278
+ # mount MyEngine, at: '/my_engine'
279
+ #
280
+ # The router that invoked `devise_for`, in the example above, would be:
281
+ # config.router_name = :my_engine
282
+ #
283
+ # When using OmniAuth, Devise cannot automatically set OmniAuth path,
284
+ # so you need to do it manually. For the users scope, it would be:
285
+ # config.omniauth_path_prefix = '/my_engine/users/auth'
286
+
287
+ # ==> Turbolinks configuration
288
+ # If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly:
289
+ #
290
+ # ActiveSupport.on_load(:devise_failure_app) do
291
+ # include Turbolinks::Controller
292
+ # end
293
+
294
+ # ==> Configuration for :registerable
295
+
296
+ # When set to false, does not sign a user in automatically after their password is
297
+ # changed. Defaults to true, so a user is signed in automatically after changing a password.
298
+ # config.sign_in_after_change_password = true
299
+ end
@@ -0,0 +1,11 @@
1
+ en:
2
+ activerecord:
3
+ models:
4
+ user:
5
+ one: User
6
+ other: Users
7
+ descriptions:
8
+ user: Section to manage users.
9
+ role: Section to manage Roles
10
+ permission: Section to manage Permissions
11
+
@@ -0,0 +1,36 @@
1
+ it:
2
+ activerecord:
3
+ models:
4
+ user:
5
+ one: Utente
6
+ other: Utenti
7
+ role:
8
+ one: Ruolo
9
+ other: Ruoli
10
+ permission:
11
+ one: Permesso
12
+ other: Permessi
13
+ attributes:
14
+ user:
15
+ email: E-Mail
16
+ username: Nome Utente
17
+ code: Codice
18
+ roles: Ruoli
19
+ admin: Amministratore?
20
+ created_at: Data di Creazione
21
+ locked: Bloccato?
22
+ third_party: Ente Terzo?
23
+ password: Password
24
+ password_confirmation: Conferma Password
25
+ role:
26
+ users: Utenti
27
+ name: Nome
28
+ permissions: Permessi
29
+ permission:
30
+ predicate: Predicato
31
+ action: Azione
32
+ model: Modello
33
+ descriptions:
34
+ user: In questa sezione dell'applicazione potete cercare nella lista degli utenti in diversi modi usando i filtri o ordinare la lista secondo diversi campi.
35
+ role: In questa sezione si possono creare dei ruoli da usare nell'RBAC gestito dai file abilities, per definire le autorizzazioni CRUD e non solo.
36
+ permission: Il predicato definisce se è un permesso di poter fare o non fare, l'azione è il tipo definisce cosa si possa fare o non fare, mentre il modello definisce su chi.
@@ -0,0 +1,10 @@
1
+ it:
2
+ permissions:
3
+ predicates:
4
+ can: Può
5
+ cannot: Non può
6
+ actions:
7
+ manage: Gestire
8
+ read: Leggere
9
+ update: Modificare
10
+ destroy: Eliminare
@@ -0,0 +1,5 @@
1
+ Rails.application.routes.draw do
2
+ devise_for :users
3
+ # Look at https://altalogy.com/blog/rails-6-user-accounts-with-3-types-of-roles/
4
+ # For controller
5
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateUsers < ActiveRecord::Migration[6.0]
4
+ def self.up
5
+ create_table :users do |t|
6
+ ## Database authenticatable
7
+ t.string :email, null: false, default: ""
8
+ t.string :encrypted_password, null: false, default: ""
9
+
10
+ ## Recoverable
11
+ # t.string :reset_password_token
12
+ # t.datetime :reset_password_sent_at
13
+
14
+ ## Rememberable
15
+ # t.datetime :remember_created_at
16
+
17
+ # Trackable
18
+ t.integer :sign_in_count, default: 0, null: false
19
+ t.datetime :current_sign_in_at
20
+ t.datetime :last_sign_in_at
21
+ t.string :current_sign_in_ip
22
+ t.string :last_sign_in_ip
23
+
24
+ ## Confirmable
25
+ # t.string :confirmation_token
26
+ # t.datetime :confirmed_at
27
+ # t.datetime :confirmation_sent_at
28
+ # t.string :unconfirmed_email # Only if using reconfirmable
29
+
30
+ ## Lockable
31
+ # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
32
+ # t.string :unlock_token # Only if unlock strategy is :email or :both
33
+ # t.datetime :locked_at
34
+
35
+
36
+ # Uncomment below if timestamps were not included in your original model.
37
+ t.timestamps null: false
38
+ end
39
+
40
+ add_index :users, :email, unique: true
41
+ # add_index :users, :reset_password_token, unique: true
42
+ # add_index :users, :confirmation_token, unique: true
43
+ # add_index :users, :unlock_token, unique: true
44
+ end
45
+
46
+ def self.down
47
+ # By default, we don't want to make any assumption about how to roll back a migration when your
48
+ # model already existed. Please edit below which fields you would like to remove in this migration.
49
+ raise ActiveRecord::IrreversibleMigration
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ class AddAdminFieldToUser < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :users, :admin, :boolean, null: false, default: false
4
+ end
5
+ end
@@ -0,0 +1,60 @@
1
+ class AddFirstAdminUser < ActiveRecord::Migration[6.0]
2
+ class User < ApplicationRecord
3
+ # Include default devise modules. Others available are:
4
+ # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
5
+ devise :database_authenticatable, :trackable, :validatable
6
+ # TODO: If it works, these must be added to another gem one which deal
7
+ # more with sessions
8
+ # devise :database_authenticatable
9
+ # devise :rememberable
10
+ # devise :trackable
11
+ # devise :validatable
12
+ # devise :timeoutable, timeout_in: 30.minutes
13
+ # REFERENCES
14
+ has_many :role_users, dependent: :destroy, inverse_of: :user
15
+ has_many :roles, through: :role_users, inverse_of: :users
16
+ # VALIDATIONS
17
+ validates :email, uniqueness: { case_sensitive: false }, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }
18
+ validates :password, presence: true, on: :create
19
+ validates :password_confirmation, presence: true, on: :create
20
+ validate :check_password_and_confirmation_equal
21
+ validates_each :admin do |record, attr, value|
22
+ # Don't want admin == false if the current user is the only admin
23
+ record.errors.add(attr, I18n.t("validation.errors.cannot_unadmin_last_admin")) if record.admin_changed? && record.admin_was == true && User.where(admin: true).count == 1
24
+ end
25
+
26
+ def display_name
27
+ email
28
+ end
29
+
30
+ def has_role? role
31
+ roles.include? role
32
+ end
33
+
34
+ protected
35
+
36
+ def check_password_and_confirmation_equal
37
+ errors.add(:password, I18n.t("validation.errors.password_and_confirm_must_be_the_same")) unless password == password_confirmation
38
+ end
39
+ end
40
+
41
+ def up
42
+ email = "admin@example.com"
43
+ User.reset_column_information
44
+ u=User.find_or_initialize_by(email: email)
45
+ psswd = SecureRandom.hex(5)
46
+ u.password = psswd
47
+ u.password_confirmation = psswd
48
+ u.admin = true
49
+ u.save!
50
+ puts "\nPlease find generated initial admin password in .passwords file."
51
+ File.open('.passwords', 'w') do |f|
52
+ f.write(psswd)
53
+ end
54
+ end
55
+
56
+ def down
57
+ email = "admin@example.com"
58
+ User.find_by(email: email).destroy
59
+ end
60
+ end
@@ -0,0 +1,10 @@
1
+ class CreateRoles < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :roles do |t|
4
+ t.string :name
5
+
6
+ t.timestamps
7
+ end
8
+ add_index :roles, :name
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class CreateRoleUsers < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :role_users do |t|
4
+ t.references :role, null: false, foreign_key: true
5
+ t.references :user, null: false, foreign_key: true
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class AddLockVersionToUser < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :users, :lock_version, :bigint
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddLockVersionToRole < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :roles, :lock_version, :bigint
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddLockedToUser < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :users, :locked, :boolean, null: false, default: false
4
+ end
5
+ end
@@ -0,0 +1,48 @@
1
+ class CreatePermissions < ActiveRecord::Migration[6.0]
2
+ def change
3
+ @values = {
4
+ predicates: %i[can cannot],
5
+ actions: %i[manage create read update destroy],
6
+ targets: ApplicationRecord.subclasses.map {|d| d.to_s.underscore}.to_a.unshift(:all)
7
+ }
8
+
9
+ def create_and_fill table
10
+ create_table table do |t|
11
+ t.string :name
12
+ t.bigint :lock_version
13
+
14
+ t.timestamps
15
+ end
16
+ add_index table, :name, unique: true
17
+ model = table.to_s.classify.constantize
18
+ model.reset_column_information
19
+ model.upsert_all @values[table].map { |p| {name: p, created_at: Time.now, updated_at: Time.now} }, unique_by: [:name]
20
+ end
21
+
22
+ # Predicates
23
+ create_and_fill :predicates
24
+
25
+ # Actions
26
+ create_and_fill :actions
27
+
28
+ # Targets
29
+ create_and_fill :targets
30
+
31
+ create_table :permissions do |t|
32
+ t.references :predicate, null: false, foreign_key: true
33
+ t.references :action, null: false, foreign_key: true
34
+ t.references :target, null: false, foreign_key: true
35
+ t.bigint :lock_version
36
+
37
+ t.timestamps
38
+ end
39
+ # Association table
40
+ create_table :permission_roles do |t|
41
+ t.references :role, null: false, foreign_key: true
42
+ t.references :permission, null: false, foreign_key: true
43
+ t.bigint :lock_version
44
+
45
+ t.timestamps
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ module Abilities
2
+ class ThecoreAuthCommons
3
+ include CanCan::Ability
4
+ def initialize user
5
+ # Main abilities file for Thecore applications
6
+ if user.present?
7
+ # Users' abilities
8
+ # -
9
+ if user.admin?
10
+ # Admins' abiities
11
+ can :manage, :all # only allow admin users to access Rails Admin
12
+ cannot :destroy, User do |u|
13
+ # prevents killing himself
14
+ u.id == user.id
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :thecore_auth_commons do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,10 @@
1
+ require 'devise'
2
+ require 'cancancan'
3
+ require 'kaminari'
4
+ require 'abilities/thecore_auth_commons'
5
+
6
+ require "thecore_auth_commons/engine"
7
+
8
+ module ThecoreAuthCommons
9
+ # Your code goes here...
10
+ end
@@ -0,0 +1,12 @@
1
+ module ThecoreAuthCommons
2
+ class Engine < ::Rails::Engine
3
+ initializer 'thecore_auth_commons.add_to_migrations' do |app|
4
+ unless app.root.to_s.match root.to_s
5
+ # APPEND TO MAIN APP MIGRATIONS FROM THIS GEM
6
+ config.paths['db/migrate'].expanded.each do |expanded_path|
7
+ app.config.paths['db/migrate'] << expanded_path
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module ThecoreAuthCommons
2
+ VERSION = "#{`git describe --tags $(git rev-list --tags --max-count=1)`}"
3
+ end
@@ -0,0 +1,7 @@
1
+ module ThecoreAuthCommonsActioncontrollerConcerns
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include HttpAcceptLanguage::AutoLocale
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thecore_auth_commons
3
+ version: !ruby/object:Gem::Version
4
+ version: '0'
5
+ platform: ruby
6
+ authors:
7
+ - Gabriele Tassoni
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-12-16 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: devise
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '4.7'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '4.7'
47
+ - !ruby/object:Gem::Dependency
48
+ name: cancancan
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.1'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.1'
61
+ - !ruby/object:Gem::Dependency
62
+ name: kaminari
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.1'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.1'
75
+ - !ruby/object:Gem::Dependency
76
+ name: sqlite3
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.4'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.4'
89
+ description: Provides common User and Role models to attach Authentication and Authorization
90
+ via your preferred gem.
91
+ email:
92
+ - gabriele.tassoni@gmail.com
93
+ executables: []
94
+ extensions: []
95
+ extra_rdoc_files: []
96
+ files:
97
+ - MIT-LICENSE
98
+ - README.md
99
+ - Rakefile
100
+ - app/models/ability.rb
101
+ - app/models/action.rb
102
+ - app/models/permission.rb
103
+ - app/models/permission_role.rb
104
+ - app/models/predicate.rb
105
+ - app/models/role.rb
106
+ - app/models/role_user.rb
107
+ - app/models/target.rb
108
+ - app/models/user.rb
109
+ - config/initializers/after_initialize_thecore_auth_commons.rb
110
+ - config/initializers/devise.rb
111
+ - config/locales/en.activerecord.yml
112
+ - config/locales/it.activerecord.yml
113
+ - config/locales/it.permissions.yml
114
+ - config/routes.rb
115
+ - db/migrate/20200306143408_create_users.rb
116
+ - db/migrate/20200306151046_add_admin_field_to_user.rb
117
+ - db/migrate/20200306151541_add_first_admin_user.rb
118
+ - db/migrate/20200306152740_create_roles.rb
119
+ - db/migrate/20200306152816_create_role_users.rb
120
+ - db/migrate/20200306153125_add_lock_version_to_user.rb
121
+ - db/migrate/20200306153136_add_lock_version_to_role.rb
122
+ - db/migrate/20200516215346_add_locked_to_user.rb
123
+ - db/migrate/20200518082821_create_permissions.rb
124
+ - lib/abilities/thecore_auth_commons.rb
125
+ - lib/tasks/thecore_auth_commons_tasks.rake
126
+ - lib/thecore_auth_commons.rb
127
+ - lib/thecore_auth_commons/engine.rb
128
+ - lib/thecore_auth_commons/version.rb
129
+ - lib/thecore_auth_commons_actioncontroller_concerns.rb
130
+ homepage: https://github.com/gabrieletassoni/thecore_auth_commons
131
+ licenses:
132
+ - MIT
133
+ metadata:
134
+ allowed_push_host: https://rubygems.org
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubygems_version: 3.0.3
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: Common Auth methods and models to be used in thecore components.
154
+ test_files: []