thecore_auth_commons 2.2.2 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9188aab4fb99d33cb6c49e95752406c7eac5f9e2096752f94c688ef447070079
4
- data.tar.gz: 53423ae9b99ffe34b078a04be259825bb023ce643e1487ad7848546dc338f86e
3
+ metadata.gz: 502bc9400ed04c0c637b9298e1997c8f310535d76edb06324bf495b044e32267
4
+ data.tar.gz: bc1d951e09c235f4907a6f67bf287fdb3d4703f47b4b32c4662e0d2b2b95c337
5
5
  SHA512:
6
- metadata.gz: 8c86e8003839386365889c8c1890d4c8fc1f61a65228259a854e00933198d00d385da75018c02885bca975d55e8afcfb2f2244902f045ce2017312405ca7a87a
7
- data.tar.gz: 13bb3dbae5695a7cf16e937d8d2fb881d1cae47ad59e22cf57efcd822c327ffb1024868a5efed179c9898cac7de5ed5c2c1297ca90eaac0e176460b7b78efe9d
6
+ metadata.gz: 4cad4cd8fb0864c65afb9b14dfcafe7f89209f8607d608de029ed8128f470f42c57c6dcdee1aa0bc2706e4f42a83119ce7c3e87dd50e47fdea316b90ae4a3729
7
+ data.tar.gz: 9de160e2037cae69e75dd41a520b6bad75ab7445136a337328288dd3bcd3e1e15cbd91e0bfd080a7232908f57958cf658df298068052bb9cb0164d267fbf87ab
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require 'abilities/thecore_auth_commons'
3
2
 
4
3
  class Ability
5
4
  include CanCan::Ability
@@ -41,5 +40,10 @@ class Ability
41
40
  self.merge const.new(user) if const.is_a? Class
42
41
  end
43
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 unless user.blank?
44
48
  end
45
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
data/app/models/role.rb CHANGED
@@ -4,8 +4,10 @@ class Role < ApplicationRecord
4
4
  # REFERENCES
5
5
  has_many :role_users, dependent: :destroy, inverse_of: :role
6
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
7
9
 
8
10
  def display_name
9
- I18n.t name.parameterize.underscore, default: name.titleize
11
+ (I18n.t name.parameterize.underscore, default: name.titleize rescue nil)
10
12
  end
11
13
  end
@@ -0,0 +1,3 @@
1
+ class Target < ApplicationRecord
2
+ has_many :permissions, dependent: :destroy, inverse_of: :target
3
+ end
data/app/models/user.rb CHANGED
@@ -23,17 +23,25 @@ class User < ApplicationRecord
23
23
  # Don't want admin == false if the current user is the only admin
24
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
25
  end
26
-
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
+
27
31
  def display_name
28
32
  email
29
33
  end
30
-
34
+
31
35
  def has_role? role
32
- roles.include? role
36
+ roles.include? role.to_s
37
+ end
38
+
39
+ def authenticate password
40
+ self&.valid_password?(password) ? self : nil
33
41
  end
34
-
42
+
35
43
  protected
36
-
44
+
37
45
  def check_password_and_confirmation_equal
38
46
  errors.add(:password, I18n.t("validation.errors.password_and_confirm_must_be_the_same")) unless password == password_confirmation
39
47
  end
@@ -1,10 +1,10 @@
1
1
  require 'thecore_auth_commons_actioncontroller_concerns'
2
2
 
3
+ # App Config
3
4
  Rails.application.configure do
4
5
  config.after_initialize do
5
6
  # In development be sure to load all the namespaces
6
7
  # in order to have working reflection and meta-programming.
7
- #
8
8
  if Rails.env.development?
9
9
  Rails.configuration.eager_load_namespaces.each(&:eager_load!) if Rails.version.to_i == 5 #Rails 5
10
10
  Zeitwerk::Loader.eager_load_all if Rails.version.to_i >= 6 #Rails 6
@@ -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
@@ -34,7 +34,7 @@ class CreateUsers < ActiveRecord::Migration[6.0]
34
34
 
35
35
 
36
36
  # Uncomment below if timestamps were not included in your original model.
37
- # t.timestamps null: false
37
+ t.timestamps null: false
38
38
  end
39
39
 
40
40
  add_index :users, :email, unique: true
@@ -1,4 +1,43 @@
1
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
+
2
41
  def up
3
42
  email = "admin@example.com"
4
43
  User.reset_column_information
@@ -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
@@ -1,6 +1,7 @@
1
1
  require 'devise'
2
2
  require 'cancancan'
3
3
  require 'kaminari'
4
+ require 'abilities/thecore_auth_commons'
4
5
 
5
6
  require "thecore_auth_commons/engine"
6
7
 
@@ -1,3 +1,3 @@
1
1
  module ThecoreAuthCommons
2
- VERSION = '2.2.2'.freeze
2
+ VERSION = "#{`git describe --tags $(git rev-list --tags --max-count=1)`.chomp}"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thecore_auth_commons
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriele Tassoni
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-15 00:00:00.000000000 Z
11
+ date: 2021-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -98,14 +98,19 @@ files:
98
98
  - README.md
99
99
  - Rakefile
100
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
101
105
  - app/models/role.rb
102
106
  - app/models/role_user.rb
107
+ - app/models/target.rb
103
108
  - app/models/user.rb
104
109
  - config/initializers/after_initialize_thecore_auth_commons.rb
105
110
  - config/initializers/devise.rb
106
- - config/locales/devise.en.yml
107
- - config/locales/en.devise.custom.yml
108
- - config/locales/it.devise.custom.yml
111
+ - config/locales/en.activerecord.yml
112
+ - config/locales/it.activerecord.yml
113
+ - config/locales/it.permissions.yml
109
114
  - config/routes.rb
110
115
  - db/migrate/20200306143408_create_users.rb
111
116
  - db/migrate/20200306151046_add_admin_field_to_user.rb
@@ -114,6 +119,8 @@ files:
114
119
  - db/migrate/20200306152816_create_role_users.rb
115
120
  - db/migrate/20200306153125_add_lock_version_to_user.rb
116
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
117
124
  - lib/abilities/thecore_auth_commons.rb
118
125
  - lib/tasks/thecore_auth_commons_tasks.rake
119
126
  - lib/thecore_auth_commons.rb
@@ -1,65 +0,0 @@
1
- # Additional translations at https://github.com/plataformatec/devise/wiki/I18n
2
-
3
- en:
4
- devise:
5
- confirmations:
6
- confirmed: "Your email address has been successfully confirmed."
7
- send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
8
- send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
9
- failure:
10
- already_authenticated: "You are already signed in."
11
- inactive: "Your account is not activated yet."
12
- invalid: "Invalid %{authentication_keys} or password."
13
- locked: "Your account is locked."
14
- last_attempt: "You have one more attempt before your account is locked."
15
- not_found_in_database: "Invalid %{authentication_keys} or password."
16
- timeout: "Your session expired. Please sign in again to continue."
17
- unauthenticated: "You need to sign in or sign up before continuing."
18
- unconfirmed: "You have to confirm your email address before continuing."
19
- mailer:
20
- confirmation_instructions:
21
- subject: "Confirmation instructions"
22
- reset_password_instructions:
23
- subject: "Reset password instructions"
24
- unlock_instructions:
25
- subject: "Unlock instructions"
26
- email_changed:
27
- subject: "Email Changed"
28
- password_change:
29
- subject: "Password Changed"
30
- omniauth_callbacks:
31
- failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
32
- success: "Successfully authenticated from %{kind} account."
33
- passwords:
34
- no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
35
- send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
36
- send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
37
- updated: "Your password has been changed successfully. You are now signed in."
38
- updated_not_active: "Your password has been changed successfully."
39
- registrations:
40
- destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
41
- signed_up: "Welcome! You have signed up successfully."
42
- signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
43
- signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
44
- signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
45
- update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address."
46
- updated: "Your account has been updated successfully."
47
- updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again"
48
- sessions:
49
- signed_in: "Signed in successfully."
50
- signed_out: "Signed out successfully."
51
- already_signed_out: "Signed out successfully."
52
- unlocks:
53
- send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
54
- send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
55
- unlocked: "Your account has been unlocked successfully. Please sign in to continue."
56
- errors:
57
- messages:
58
- already_confirmed: "was already confirmed, please try signing in"
59
- confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
60
- expired: "has expired, please request a new one"
61
- not_found: "not found"
62
- not_locked: "was not locked"
63
- not_saved:
64
- one: "1 error prohibited this %{resource} from being saved:"
65
- other: "%{count} errors prohibited this %{resource} from being saved:"
@@ -1,19 +0,0 @@
1
- en:
2
- devise:
3
- failure:
4
- user:
5
- invalid: Incorrect login credentials.
6
- not_found_in_database: Incorrect login credentials.
7
- mailer:
8
- password_changed_instructions:
9
- greeting: Welcome %{recipient}!
10
- instruction: We sent you this email to inform about password change.
11
- instruction_2: If you didn't changed password, we ask you to contact our customer service, please.
12
- shared:
13
- links:
14
- sign_out: Log out
15
- descriptions:
16
- current_password_needed: (we need your current password to confirm your changes)
17
- leave_blank: (leave blank if you don't want to change it)
18
- validations:
19
- minimum_length: "%{length} characters minimum"
@@ -1,19 +0,0 @@
1
- it:
2
- devise:
3
- failure:
4
- user:
5
- invalid: Credenziali di accesso errate.
6
- not_found_in_database: Credenziali di accesso errate.
7
- mailer:
8
- password_changed_instructions:
9
- greeting: Benvenuto %{recipient}!
10
- instruction: Le abbiamo inviato questa email per notificarle il fatto che la sua password è stata cambiata.
11
- instruction_2: Se non è stato lei a richiedere la modifica della password, la preghiamo di contattare il servizio clienti.
12
- shared:
13
- links:
14
- sign_out: Esci
15
- descriptions:
16
- current_password_needed: (è necessario inserire la password corrente per autorizzare la modifica)
17
- leave_blank: (lasciare vuota se non la si vuole modificare)
18
- validations:
19
- minimum_length: la lunghezza minima è di %{length} caratteri