thecore_auth_commons 3.3.3 → 3.4.0
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 +4 -4
- data/app/controllers/users/sessions_controller.rb +35 -0
- data/app/jobs/background_ldap_import_job.rb +7 -0
- data/app/models/concerns/api/ldap_server.rb +19 -0
- data/app/models/concerns/endpoints/ldap_server.rb +78 -0
- data/app/models/concerns/rails_admin/ldap_server.rb +10 -0
- data/app/models/ldap_server.rb +46 -0
- data/app/services/ldap/authenticator.rb +59 -0
- data/config/locales/it.thecore_auth_commons.yml +20 -0
- data/db/migrate/20250516074016_create_ldap_servers.rb +21 -0
- data/db/migrate/20250516075204_add_auth_source_to_user.rb +17 -0
- data/lib/tasks/ldap.rake +7 -0
- data/lib/thecore_auth_commons/version.rb +1 -1
- data/lib/thecore_auth_commons.rb +69 -0
- metadata +25 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5ff5d59febf329c917067988bc9a854f77222f310d07a34b98adbf500b92abd
|
4
|
+
data.tar.gz: ece767087f4e4bde62d0500c8e7b9840b0482043401d3df2052009b32cdfe639
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc4901ac970bc086ae02863f9d82bfb72028055193d3bb6190d0b2396e8c86adf261b8ac6eb101b5de971729f734da6b8a1692454654c9731f3c868d07aed9d9
|
7
|
+
data.tar.gz: 7cf606e7d94bf1ef8c404b7bf859130c9d33c06a4d20cc4228dad1d1f035db5b1a99746504a5d92280fbdf818cfe13716ca2c99a21535445d712912063653705
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# app/controllers/users/sessions_controller.rb
|
2
|
+
class Users::SessionsController < Devise::SessionsController
|
3
|
+
def create
|
4
|
+
self.resource = warden.authenticate(auth_options)
|
5
|
+
|
6
|
+
if resource
|
7
|
+
sign_in_and_redirect(resource)
|
8
|
+
else
|
9
|
+
user = Ldap::Authenticator.new(
|
10
|
+
email: params[:user][:email],
|
11
|
+
password: params[:user][:password]
|
12
|
+
).authenticate
|
13
|
+
|
14
|
+
if user
|
15
|
+
flash[:notice] = "Autenticato via LDAP"
|
16
|
+
sign_in(:user, user)
|
17
|
+
redirect_to after_sign_in_path_for(user)
|
18
|
+
else
|
19
|
+
flash.now[:alert] = "Email o password non validi"
|
20
|
+
self.resource = resource_class.new(sign_in_params)
|
21
|
+
clean_up_passwords(resource)
|
22
|
+
respond_with_navigational(resource) { render :new, status: :unauthorized }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def sign_in_and_redirect(resource)
|
30
|
+
set_flash_message!(:notice, :signed_in)
|
31
|
+
sign_in(resource_name, resource)
|
32
|
+
yield resource if block_given?
|
33
|
+
respond_with resource, location: after_sign_in_path_for(resource)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Api::LdapServer
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
# Use self.json_attrs to drive json rendering for
|
6
|
+
# API model responses (index, show and update ones).
|
7
|
+
# For reference:
|
8
|
+
# https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
|
9
|
+
# The object passed accepts only these keys:
|
10
|
+
# - only: list [] of model fields to be shown in JSON serialization
|
11
|
+
# - except: exclude these fields from the JSON serialization, is a list []
|
12
|
+
# - methods: include the result of some method defined in the model
|
13
|
+
# - include: include associated models, it's an object {} which also accepts the keys described here
|
14
|
+
cattr_accessor :json_attrs
|
15
|
+
self.json_attrs = ::ModelDrivenApi.smart_merge (json_attrs || {}), {}
|
16
|
+
|
17
|
+
# Custom action callable by the API must be defined in /app/models/concerns/endpoints/
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class Endpoints::LdapServer < NonCrudEndpoints
|
2
|
+
# self.desc 'LdapServer', :test, {
|
3
|
+
# # Define the action name using openapi swagger format
|
4
|
+
# get: {
|
5
|
+
# summary: "Test API Custom Action",
|
6
|
+
# description: "This is a test API custom action",
|
7
|
+
# operationId: "test",
|
8
|
+
# tags: ["Test"],
|
9
|
+
# parameters: [
|
10
|
+
# {
|
11
|
+
# name: "explain",
|
12
|
+
# in: "query",
|
13
|
+
# description: "Explain the action by returning this openapi schema",
|
14
|
+
# required: true,
|
15
|
+
# schema: {
|
16
|
+
# type: "boolean"
|
17
|
+
# }
|
18
|
+
# }
|
19
|
+
# ],
|
20
|
+
# responses: {
|
21
|
+
# 200 => {
|
22
|
+
# description: "The openAPI json schema for this action",
|
23
|
+
# content: {
|
24
|
+
# "application/json": {
|
25
|
+
# schema: {
|
26
|
+
# type: "object",
|
27
|
+
# additionalProperties: true
|
28
|
+
# }
|
29
|
+
# }
|
30
|
+
# }
|
31
|
+
# },
|
32
|
+
# 501 => {
|
33
|
+
# error: :string,
|
34
|
+
# }
|
35
|
+
# }
|
36
|
+
# },
|
37
|
+
# post: {
|
38
|
+
# summary: "Test API Custom Action",
|
39
|
+
# description: "This is a test API custom action",
|
40
|
+
# operationId: "test",
|
41
|
+
# tags: ["Test"],
|
42
|
+
# requestBody: {
|
43
|
+
# required: true,
|
44
|
+
# content: {
|
45
|
+
# "application/json": {}
|
46
|
+
# }
|
47
|
+
# },
|
48
|
+
# responses: {
|
49
|
+
# 200 => {
|
50
|
+
# description: "The openAPI json schema for this action",
|
51
|
+
# # This will return the object with a message string and a params object
|
52
|
+
# content: {
|
53
|
+
# "application/json": {
|
54
|
+
# schema: {
|
55
|
+
# type: "object",
|
56
|
+
# properties: {
|
57
|
+
# message: {
|
58
|
+
# type: "string"
|
59
|
+
# },
|
60
|
+
# params: {
|
61
|
+
# type: "object",
|
62
|
+
# additionalProperties: true
|
63
|
+
# }
|
64
|
+
# }
|
65
|
+
# }
|
66
|
+
# }
|
67
|
+
# }
|
68
|
+
# },
|
69
|
+
# 501 => {
|
70
|
+
# error: :string,
|
71
|
+
# }
|
72
|
+
# }
|
73
|
+
# }
|
74
|
+
# }
|
75
|
+
# def test(params)
|
76
|
+
# return { message: "Hello World From Test API Custom Action called test", params: params }, 200
|
77
|
+
# end
|
78
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class LdapServer < ApplicationRecord
|
2
|
+
default_scope { order(priority: :asc) }
|
3
|
+
|
4
|
+
# Associations
|
5
|
+
include Api::LdapServer
|
6
|
+
include RailsAdmin::LdapServer
|
7
|
+
|
8
|
+
# Validations
|
9
|
+
validates :host, presence: true
|
10
|
+
validates :base_dn, presence: true
|
11
|
+
|
12
|
+
# Callbacks
|
13
|
+
# After the record is actually deleted from the DB, remove all associated users which have the same auth_source with value "ldap #{id}"
|
14
|
+
after_destroy :remove_users_with_auth_source
|
15
|
+
|
16
|
+
def test_connection
|
17
|
+
# Test the connection to the LDAP server
|
18
|
+
ldap = Net::LDAP.new(
|
19
|
+
host: host,
|
20
|
+
port: port,
|
21
|
+
encryption: use_ssl ? :simple_tls : nil,
|
22
|
+
auth: {
|
23
|
+
method: :simple,
|
24
|
+
username: admin_user,
|
25
|
+
password: admin_password
|
26
|
+
}
|
27
|
+
)
|
28
|
+
# Perform a simple bind to check the connection
|
29
|
+
if ldap.bind
|
30
|
+
# Connection successful
|
31
|
+
Rails.logger.info "Connection to LDAP server #{host} successful."
|
32
|
+
else
|
33
|
+
# Connection failed
|
34
|
+
Rails.logger.info "Connection to LDAP server #{host} failed: #{ldap.get_operation_result.message}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# This method is called after the record is destroyed
|
41
|
+
def remove_users_with_auth_source
|
42
|
+
User.where(auth_source: "ldap #{id}").find_each do |user|
|
43
|
+
user.destroy
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# app/services/ldap/authenticator.rb
|
2
|
+
module Ldap
|
3
|
+
class Authenticator
|
4
|
+
def initialize(email:, password:)
|
5
|
+
@password = password
|
6
|
+
@email = email
|
7
|
+
end
|
8
|
+
|
9
|
+
def authenticate
|
10
|
+
return nil if @password.blank?
|
11
|
+
|
12
|
+
LdapServer.all.each do |server|
|
13
|
+
ldap = Net::LDAP.new(
|
14
|
+
host: server.host,
|
15
|
+
port: server.port,
|
16
|
+
encryption: server.use_ssl ? :simple_tls : nil,
|
17
|
+
auth: {
|
18
|
+
method: :simple,
|
19
|
+
username: server.admin_user,
|
20
|
+
password: server.admin_password
|
21
|
+
}
|
22
|
+
)
|
23
|
+
|
24
|
+
filter = Net::LDAP::Filter.eq(server.auth_field, email) # server.auth_field
|
25
|
+
treebase = server.base_dn
|
26
|
+
|
27
|
+
ldap.search(base: treebase, filter: filter) do |entry|
|
28
|
+
user_dn = entry.dn
|
29
|
+
|
30
|
+
# Prova autenticazione utente
|
31
|
+
user_ldap = Net::LDAP.new(
|
32
|
+
host: server.host,
|
33
|
+
port: server.port,
|
34
|
+
encryption: server.use_ssl ? :simple_tls : nil,
|
35
|
+
auth: {
|
36
|
+
method: :simple,
|
37
|
+
username: user_dn,
|
38
|
+
password: password
|
39
|
+
}
|
40
|
+
)
|
41
|
+
|
42
|
+
if user_ldap.bind
|
43
|
+
return find_or_create_user(entry, server.id)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :email, :password
|
54
|
+
|
55
|
+
def find_or_create_user(entry, server_id)
|
56
|
+
ThecoreAuthCommons.align_user email, entry, server_id
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -13,6 +13,9 @@ it:
|
|
13
13
|
cannot_delete_last_role: "deve esistere almeno un ruolo"
|
14
14
|
activerecord:
|
15
15
|
models:
|
16
|
+
ldap_server:
|
17
|
+
one: Server LDAP
|
18
|
+
other: Server LDAP
|
16
19
|
user:
|
17
20
|
one: Utente
|
18
21
|
other: Utenti
|
@@ -23,6 +26,23 @@ it:
|
|
23
26
|
one: Permesso
|
24
27
|
other: Permessi
|
25
28
|
attributes:
|
29
|
+
ldap_server:
|
30
|
+
# t.string :host, null: false
|
31
|
+
# t.integer :port, default: 389
|
32
|
+
# t.string :base_dn, null: false
|
33
|
+
# t.string :admin_user
|
34
|
+
# t.string :admin_password
|
35
|
+
# t.integer :priority, default: 1
|
36
|
+
# t.boolean :use_ssl, default: false
|
37
|
+
# t.string :auth_field, default: "userPrincipalName"
|
38
|
+
host: Host
|
39
|
+
port: Porta
|
40
|
+
base_dn: Base DN
|
41
|
+
admin_user: Utente Amministratore
|
42
|
+
admin_password: Password Amministratore
|
43
|
+
priority: Priorità
|
44
|
+
use_ssl: Usa SSL?
|
45
|
+
auth_field: Campo di Autenticazione
|
26
46
|
user:
|
27
47
|
email: E-Mail
|
28
48
|
code: Codice
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateLdapServers < ActiveRecord::Migration[7.2]
|
2
|
+
def change
|
3
|
+
create_table :ldap_servers do |t|
|
4
|
+
t.string :host, null: false
|
5
|
+
t.integer :port, default: 389
|
6
|
+
t.string :base_dn, null: false
|
7
|
+
t.string :admin_user
|
8
|
+
t.string :admin_password
|
9
|
+
t.integer :priority, default: 1
|
10
|
+
t.boolean :use_ssl, default: false
|
11
|
+
t.string :auth_field, default: "userPrincipalName"
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
add_index :ldap_servers, :host
|
16
|
+
add_index :ldap_servers, :base_dn
|
17
|
+
add_index :ldap_servers, :admin_user
|
18
|
+
add_index :ldap_servers, :admin_password
|
19
|
+
add_index :ldap_servers, :auth_field
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class AddAuthSourceToUser < ActiveRecord::Migration[7.2]
|
2
|
+
def change
|
3
|
+
add_column :users, :auth_source, :string, null: false, default: 'local'
|
4
|
+
add_index :users, :auth_source
|
5
|
+
|
6
|
+
# Fill the new column with the default value for existing users
|
7
|
+
reversible do |dir|
|
8
|
+
dir.up do
|
9
|
+
execute <<-SQL.squish
|
10
|
+
UPDATE users
|
11
|
+
SET auth_source = 'local'
|
12
|
+
WHERE auth_source IS NULL
|
13
|
+
SQL
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/tasks/ldap.rake
ADDED
data/lib/thecore_auth_commons.rb
CHANGED
@@ -3,11 +3,80 @@ require 'cancancan'
|
|
3
3
|
require 'kaminari'
|
4
4
|
require 'activerecord-nulldb-adapter'
|
5
5
|
require "thecore_settings"
|
6
|
+
require "net/ldap"
|
6
7
|
|
7
8
|
require "thecore_auth_commons/engine"
|
8
9
|
|
9
10
|
require "thecore/seed"
|
10
11
|
|
11
12
|
module ThecoreAuthCommons
|
13
|
+
|
14
|
+
def self.import_ldap_users_task
|
15
|
+
puts "== Avvio sincronizzazione utenti da LDAP =="
|
16
|
+
|
17
|
+
imported_count = 0
|
18
|
+
|
19
|
+
LdapServer.all.each do |server|
|
20
|
+
puts "Contatto server LDAP: #{server.host} (priorità: #{server.priority})"
|
21
|
+
|
22
|
+
ldap = Net::LDAP.new(
|
23
|
+
host: server.host,
|
24
|
+
port: server.port,
|
25
|
+
encryption: server.use_ssl ? :simple_tls : nil,
|
26
|
+
auth: {
|
27
|
+
method: :simple,
|
28
|
+
username: server.admin_user,
|
29
|
+
password: server.admin_password
|
30
|
+
}
|
31
|
+
)
|
32
|
+
|
33
|
+
unless ldap.bind
|
34
|
+
puts "❌ Connessione fallita a #{server.host}"
|
35
|
+
next
|
36
|
+
end
|
37
|
+
|
38
|
+
filter = Net::LDAP::Filter.present(server.auth_field)
|
39
|
+
treebase = server.base_dn
|
40
|
+
|
41
|
+
ldap.search(base: treebase, filter: filter) do |entry|
|
42
|
+
email = entry[server.auth_field]&.first
|
43
|
+
next unless email
|
44
|
+
|
45
|
+
puts "Importando utente: #{email}"
|
46
|
+
|
47
|
+
# Password must contain at least one uppercase letter, one lowercase letter, one number and one special character
|
48
|
+
ThecoreAuthCommons.align_user email, entry, server.id
|
49
|
+
imported_count += 1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
puts "== Completato. Utenti importati: #{imported_count} =="
|
54
|
+
end
|
12
55
|
# Your code goes here...
|
56
|
+
def self.align_user email, entry, server_id
|
57
|
+
user = User.find_or_initialize_by(email: email)
|
58
|
+
user.auth_source = "ldap #{server_id}"
|
59
|
+
|
60
|
+
# Password don't need to be changed, just created, otherwise it will invalidate the current user session if it's logged in
|
61
|
+
user.password = user.password_confirmation = Devise.friendly_token[0, 20] if user.new_record?
|
62
|
+
|
63
|
+
# Eventuale mapping LDAP -> campi User
|
64
|
+
user.name = entry[:givenname]&.first if user.respond_to?(:name)
|
65
|
+
|
66
|
+
# Recupera dala entry i gruppi di cui fa parte l'utente e crea i relativi record in Role assegnandoli all'utente corrente
|
67
|
+
is_admin = false
|
68
|
+
entry[:memberOf].each do |group|
|
69
|
+
group_name = group.split(",").first.split("=").last
|
70
|
+
# Se il gruppo è un admin, assegna il ruolo admin
|
71
|
+
is_admin = true if [ "Administrators" "Domain Admins", "Schema Admins", "Enterprise Admins", "admins", "administrators" ].include?(group_name)
|
72
|
+
|
73
|
+
role = Role.find_or_create_by(name: group_name)
|
74
|
+
user.roles << role unless user.roles.include?(role)
|
75
|
+
end
|
76
|
+
|
77
|
+
user.admin = is_admin if user.respond_to?(:admin)
|
78
|
+
# Se l'utente è nuovo o ha cambiato qualcosa, salvalo
|
79
|
+
puts "Cannot save user #{email} with errors: #{user.errors.full_messages.join(", ")}" unless user.save # if user.new_record? || user.changed? || user.roles_changed?
|
80
|
+
user
|
81
|
+
end
|
13
82
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thecore_auth_commons
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriele Tassoni
|
@@ -9,6 +9,20 @@ bindir: bin
|
|
9
9
|
cert_chain: []
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: net-ldap
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
12
26
|
- !ruby/object:Gem::Dependency
|
13
27
|
name: devise
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,7 +193,13 @@ extra_rdoc_files: []
|
|
179
193
|
files:
|
180
194
|
- README.md
|
181
195
|
- Rakefile
|
196
|
+
- app/controllers/users/sessions_controller.rb
|
197
|
+
- app/jobs/background_ldap_import_job.rb
|
182
198
|
- app/models/action.rb
|
199
|
+
- app/models/concerns/api/ldap_server.rb
|
200
|
+
- app/models/concerns/endpoints/ldap_server.rb
|
201
|
+
- app/models/concerns/rails_admin/ldap_server.rb
|
202
|
+
- app/models/ldap_server.rb
|
183
203
|
- app/models/permission.rb
|
184
204
|
- app/models/permission_role.rb
|
185
205
|
- app/models/predicate.rb
|
@@ -187,6 +207,7 @@ files:
|
|
187
207
|
- app/models/role_user.rb
|
188
208
|
- app/models/target.rb
|
189
209
|
- app/models/user.rb
|
210
|
+
- app/services/ldap/authenticator.rb
|
190
211
|
- config/initializers/abilities.rb
|
191
212
|
- config/initializers/add_to_db_migrations.rb
|
192
213
|
- config/initializers/after_initialize.rb
|
@@ -203,7 +224,10 @@ files:
|
|
203
224
|
- db/migrate/20160209153811_create_roles.rb
|
204
225
|
- db/migrate/20160209153813_create_role_users.rb
|
205
226
|
- db/migrate/20160209153816_create_permissions_chain.rb
|
227
|
+
- db/migrate/20250516074016_create_ldap_servers.rb
|
228
|
+
- db/migrate/20250516075204_add_auth_source_to_user.rb
|
206
229
|
- db/seeds.rb
|
230
|
+
- lib/tasks/ldap.rake
|
207
231
|
- lib/tasks/thecore_auth_commons_tasks.rake
|
208
232
|
- lib/thecore/seed.rb
|
209
233
|
- lib/thecore_auth_commons.rb
|