chaltron 1.0.10 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72571e485760e7bf439a08e096643fb70f0ded45ac64095534fb9b2ae5f3efb5
4
- data.tar.gz: 5e00168651cb647adada5c19187a71364344e0ad609f2a186d3efe9169701fbc
3
+ metadata.gz: c977d4e104c86ab2f4fd73705bff5c85fc86fb47090e10cab3d0b9ad8ed16b7c
4
+ data.tar.gz: ea483a22e0bc4a3d917072fef88c35cc6312c3aefdefc32757efddb04675d45c
5
5
  SHA512:
6
- metadata.gz: 18005274020f0076645423b10692f9b189da42f3efbe3234e376b1f0ba9fafd97c787a4f9ba477fe467d33fc260a568647af3bf7dc77aa17c1f259b9b0bbf4cb
7
- data.tar.gz: c5b3e8b1670d2eea5db6e551d0950781e5d942f16616a28777ab1b687064f73b694303b2dbe9b28f6dc0d3d92096d528696e7626acf14eb92ed1f8f6195c083b
6
+ metadata.gz: f723fdbd928eb379f9d289543b708d4de2dfb6581b57140e47ebc9e2b69121261ecf90f75812038ae8b3ef83c0cd1ef1499c6d9264e3638ab57162584c1db84f
7
+ data.tar.gz: 037d308844d5a4cf8c30ce9be50ca1ebb0ad294ba81bfecb6fa3bf2433c4d4fcf58c194e7630acb9e3f276847a4d017af9f7071bea9a394f3430bf59e71d0794
@@ -15,11 +15,11 @@ class Chaltron::LdapController < ApplicationController
15
15
  userid = params[:userid]
16
16
  if userid.present?
17
17
  entry = Chaltron::LDAP::Person.find_by_uid(userid)
18
- @entries << entry unless entry.nil?
18
+ @entries << entry
19
19
  else
20
- res = Chaltron::LDAP::Person.find_by_fields(find_options)
21
- @entries = res
20
+ @entries = Chaltron::LDAP::Person.find_by_fields(find_options)
22
21
  end
22
+ @entries.compact!
23
23
  end
24
24
 
25
25
  def multi_create
@@ -41,12 +41,12 @@ class Chaltron::LdapController < ApplicationController
41
41
  private
42
42
  def find_options
43
43
  department = params[:department]
44
- name = params[:fullname]
44
+ name = params[:lastname]
45
45
  limit = params[:limit].to_i
46
46
 
47
47
  ret = {}
48
48
  ret[:department] = "*#{department}*" unless department.blank?
49
- ret[:cn] = "*#{name}*" unless name.blank?
49
+ ret[:last_name] = "*#{name}*" unless name.blank?
50
50
  ret[:limit] = limit.zero? ? default_limit : limit
51
51
  ret
52
52
  end
@@ -12,6 +12,7 @@ module Chaltron
12
12
  # We only find ourselves here
13
13
  # if the authentication to LDAP was successful.
14
14
  user = Chaltron::LDAP::User.find_or_create(oauth, Chaltron.ldap_allow_all)
15
+ user = Chaltron.ldap_after_authenticate.call(user, Chaltron::LDAP::Connection.new)
15
16
  if user.nil?
16
17
  redirect_to root_url, alert: I18n.t('chaltron.not_allowed_to_sign_in')
17
18
  else
@@ -1,3 +1,5 @@
1
+ require 'chaltron/ldap/connection'
2
+
1
3
  class Chaltron::SessionsController < Devise::SessionsController
2
4
  after_action :after_login, only: :create
3
5
  before_action :before_logout, only: :destroy
@@ -9,6 +11,7 @@ class Chaltron::SessionsController < Devise::SessionsController
9
11
  end
10
12
 
11
13
  def before_logout
14
+ Chaltron.ldap_before_logout.call(current_user, Chaltron::LDAP::Connection.new) if current_user.ldap_user?
12
15
  info I18n.t('chaltron.logs.logout', user: current_user.display_name)
13
16
  end
14
17
  end
@@ -1,4 +1,4 @@
1
- class Log < ActiveRecord::Base
1
+ class Log < ApplicationRecord
2
2
  Severities = %w( emerg alert crit err warning notice info debug )
3
3
 
4
4
  validates_presence_of :severity, :message
@@ -1,4 +1,4 @@
1
- class User < ActiveRecord::Base
1
+ class User < ApplicationRecord
2
2
  include Authorizable
3
3
  # Include default devise modules. Others available are:
4
4
  # :registerable, :confirmable, :lockable and :omniauthable
@@ -26,4 +26,8 @@ class User < ActiveRecord::Base
26
26
  end
27
27
  end
28
28
 
29
+ def ldap_user?
30
+ provider == 'ldap'
31
+ end
32
+
29
33
  end
@@ -5,7 +5,7 @@
5
5
  label_col: 'col-sm-2', control_col: 'col-sm-10') do |f| %>
6
6
 
7
7
  <%= f.text_field :userid, label: t('.name_label'), help: t('.name_help') %>
8
- <%= f.text_field :fullname, label: t('.fullname_label'), help: t('.fullname_help') %>
8
+ <%= f.text_field :lastname, label: t('.lastname_label'), help: t('.lastname_help') %>
9
9
  <%= f.text_field :department, label: t('.department_label'), help: t('.department_help') %>
10
10
  <%= f.text_field :limit, label: t('.limit_label'), help: t('.limit_help'), value: @limit %>
11
11
 
@@ -5,7 +5,7 @@
5
5
  <%= render partial: 'side_filters', locals: { filters: @filters } %>
6
6
  <hr>
7
7
 
8
- <% if ldap_enabled? %>
8
+ <% if ldap_enabled? and !Chaltron.ldap_allow_all %>
9
9
  <div class='dropdown pull-right'>
10
10
  <%= content_tag :button, type: 'button', class: 'btn btn-primary dropdown-toggle',
11
11
  data: {toggle: 'dropdown'}, aria: {expanded: false, haspopup: true}, id: 'new_user_button' do %>
@@ -66,11 +66,11 @@
66
66
  <div class='float-right'>
67
67
  <%= link_to edit_user_path(@user), class: 'btn btn-primary' do %>
68
68
  <%= icon :fas, :edit, t('.edit') %>
69
- <% end %>
69
+ <% end if can? :edit, @user %>
70
70
  <%= link_to @user, method: :delete, class: 'btn btn-danger',
71
71
  disabled: current_user == @user,
72
72
  data: { confirm: t('.destroy_confirm', user: @user.username) } do %>
73
73
  <%= icon :fas, :trash, t('.destroy') %>
74
- <% end %>
74
+ <% end if can? :destroy, @user%>
75
75
  </div>
76
76
  </div>
@@ -70,11 +70,11 @@ en:
70
70
  title: Search for LDAP sers
71
71
  submit_text: Search
72
72
  name_label: User
73
- name_help: Search for user-id (exact match)
73
+ name_help: Search by user-id (exact match)
74
74
  fullname_label: Fullname
75
- fullname_help: Search for first name or surname (also partial match)
75
+ fullname_help: Search by last name (also partial match)
76
76
  department_label: Department
77
- department_help: Search for department (also partial match)
77
+ department_help: Search by department (also partial match)
78
78
  limit_label: Limit
79
79
  limit_help: Max shown results
80
80
  multi_new:
@@ -71,8 +71,8 @@ it:
71
71
  submit_text: Cerca
72
72
  name_label: Utente
73
73
  name_help: Ricerca per user-id (match esatto)
74
- fullname_label: Nome
75
- fullname_help: Ricerca per nome o cognome (anche match parziale)
74
+ lastname_label: Nome
75
+ lastname_help: Ricerca per cognome (anche match parziale)
76
76
  department_label: Funzione
77
77
  department_help: Ricerca per sigla di funzione (anche match parziale)
78
78
  limit_label: Limite
@@ -6,10 +6,10 @@ SimpleNavigation::Configuration.run do |navigation|
6
6
  if signed_in?
7
7
  primary.item :admin, I18n.t('chaltron.menu.admin'), '#', link_html: { icon: 'cogs' } do |admin|
8
8
  admin.item :users, I18n.t('chaltron.menu.users'), users_path, link_html: { icon: 'users' },
9
- highlights_on: /\/(users|ldap)(?!\/self_(show|edit|update))/ if can?(:manage, User)
9
+ highlights_on: /\/(users|ldap)(?!\/self_(show|edit|update))/ if can?(:read, User)
10
10
  admin.item :logs, I18n.t('chaltron.menu.logs'), logs_path, link_html: { icon: 'book' },
11
11
  highlights_on: /\/logs/ if can?(:read, Log)
12
- end if can?(:manage, User) or can?(:read, Log)
12
+ end if can?(:read, User) or can?(:read, Log)
13
13
  primary.item :logged, current_user.display_name.html_safe, '#',
14
14
  html: { class: 'dropdown-menu-right' } do |user|
15
15
  user.item :self_edit, I18n.t('chaltron.menu.self_show'), self_show_users_path,
@@ -15,8 +15,9 @@ Rails.application.routes.draw do
15
15
  resources :logs, controller: 'chaltron/logs', only: [:index, :show]
16
16
 
17
17
  # search and create LDAP users
18
- get 'ldap/search' => 'chaltron/ldap#search'
19
- post 'ldap/multi_new' => 'chaltron/ldap#multi_new'
20
- post 'ldap/multi_create' => 'chaltron/ldap#multi_create'
21
-
18
+ if Devise.omniauth_providers.include?(:ldap) and !Chaltron.ldap_allow_all
19
+ get 'ldap/search' => 'chaltron/ldap#search'
20
+ post 'ldap/multi_new' => 'chaltron/ldap#multi_new'
21
+ post 'ldap/multi_create' => 'chaltron/ldap#multi_create'
22
+ end
22
23
  end
@@ -14,7 +14,7 @@ module Chaltron
14
14
  @@default_roles = []
15
15
 
16
16
  mattr_accessor :ldap_allow_all
17
- @@ldap_allow_all = false
17
+ @@ldap_allow_all = true
18
18
 
19
19
  mattr_accessor :enable_syslog
20
20
  @@enable_syslog = false
@@ -22,6 +22,29 @@ module Chaltron
22
22
  mattr_accessor :syslog_facility
23
23
  @@syslog_facility = Syslog::LOG_SYSLOG
24
24
 
25
+ mattr_accessor :ldap_field_mappings
26
+ @@ldap_field_mappings = {
27
+ first_name: 'givenname',
28
+ last_name: 'cn',
29
+ department: 'department',
30
+ email: 'mail'
31
+ }
32
+
33
+ mattr_accessor :ldap_group_base
34
+ @@ldap_group_base = nil
35
+
36
+ mattr_accessor :ldap_group_member_filter
37
+ @@ldap_group_member_filter = -> (entry) { "uniquemember=#{entry.dn}" }
38
+
39
+ mattr_accessor :ldap_role_mappings
40
+ @@ldap_role_mappings = {}
41
+
42
+ mattr_accessor :ldap_after_authenticate
43
+ @@ldap_after_authenticate = -> (user, ldap) { user }
44
+
45
+ mattr_accessor :ldap_before_logout
46
+ @@ldap_before_logout = -> (user, ldap) { }
47
+
25
48
  def self.setup
26
49
  yield self
27
50
  end
@@ -4,6 +4,12 @@ require 'chaltron/ldap/person'
4
4
  module Chaltron
5
5
  module LDAP
6
6
  class Connection
7
+ NET_LDAP_ENCRYPTION_METHOD = {
8
+ simple_tls: :simple_tls,
9
+ start_tls: :start_tls,
10
+ plain: nil
11
+ }.freeze
12
+
7
13
  attr_reader :ldap
8
14
 
9
15
  def initialize(params = {})
@@ -16,7 +22,9 @@ module Chaltron
16
22
  end
17
23
 
18
24
  def find_by_uid(id)
19
- find_user(uid: id)
25
+ opts = {}
26
+ opts[uid.to_sym] = id
27
+ ret = find_user(opts)
20
28
  end
21
29
 
22
30
  def find_user(*args)
@@ -47,35 +55,31 @@ module Chaltron
47
55
  scope: Net::LDAP::SearchScope_BaseObject
48
56
  }
49
57
  else
50
- filters = []
51
- fields.each do |field|
52
- filters << Net::LDAP::Filter.eq(field, args[field])
58
+ filters = fields.map do |field|
59
+ f = translate_field(field)
60
+ Net::LDAP::Filter.eq(f, args[field]) if f
53
61
  end
54
62
  options = {
55
63
  base: base,
56
64
  filter: filters.inject { |sum, n| Net::LDAP::Filter.join(sum, n) }
57
65
  }
58
66
  end
67
+ options.merge!(size: limit) unless limit.nil?
68
+ ldap_search(options).map do |entry|
69
+ Chaltron::LDAP::Person.new(entry, uid) if entry.respond_to? uid
70
+ end.compact
71
+ end
59
72
 
60
- # if config.user_filter.present?
61
- # user_filter = Net::LDAP::Filter.construct(config.user_filter)
62
-
63
- # options[:filter] = if options[:filter]
64
- # Net::LDAP::Filter.join(options[:filter], user_filter)
65
- # else
66
- # user_filter
67
- # end
68
- # end
69
-
70
- options.merge!(size: limit) if limit.present?
71
-
72
- entries = ldap_search(options).select do |entry|
73
- entry.respond_to? uid
74
- end
73
+ def find_groups_by_member(entry)
74
+ options = {
75
+ base: Chaltron.ldap_group_base || base,
76
+ filter: Chaltron.ldap_group_member_filter.call(entry)
77
+ }
78
+ ldap_search(options)
79
+ end
75
80
 
76
- entries.map do |entry|
77
- Chaltron::LDAP::Person.new(entry, uid)
78
- end
81
+ def update_attributes(dn, args)
82
+ ldap.modify dn: dn, operations: args.map { |k,v| [:replace, k, v] }
79
83
  end
80
84
 
81
85
  private
@@ -84,15 +88,21 @@ module Chaltron
84
88
  Devise.omniauth_configs[:ldap].options
85
89
  end
86
90
 
91
+ def translate_field field
92
+ return uid if field.to_sym == :uid
93
+ return Chaltron.ldap_field_mappings[field.to_sym] unless Chaltron.ldap_field_mappings[field.to_sym].nil?
94
+ field
95
+ end
96
+
87
97
  def adapter_options
88
- {
98
+ opts = {
89
99
  host: options[:host],
90
100
  port: options[:port],
91
- encryption: encryption,
101
+ encryption: encryption_options,
92
102
  verbose: true
93
- }.tap do |options|
94
- options.merge!(auth_options) if has_auth?
95
- end
103
+ }
104
+ opts.merge!(auth_options) if has_auth?
105
+ opts
96
106
  end
97
107
 
98
108
  def base
@@ -103,15 +113,64 @@ module Chaltron
103
113
  options[:uid]
104
114
  end
105
115
 
106
- def encryption
107
- case options[:method].to_s
108
- when 'ssl'
109
- :simple_tls
110
- when 'tls'
111
- :start_tls
112
- else
113
- nil
116
+ def encryption_options
117
+ method = translate_method
118
+ return unless method
119
+ {
120
+ method: method,
121
+ tls_options: tls_options
122
+ }
123
+ end
124
+
125
+ def translate_method
126
+ NET_LDAP_ENCRYPTION_METHOD[options[:encryption]&.to_sym]
127
+ end
128
+
129
+ def tls_options
130
+ return @tls_options if defined?(@tls_options)
131
+
132
+ method = translate_method
133
+ return unless method
134
+
135
+ opts = if options[:verify_certificates] && method != 'plain'
136
+ # Dup so we don't accidentally overwrite the constant
137
+ OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.dup
138
+ else
139
+ # It is important to explicitly set verify_mode for two reasons:
140
+ # 1. The behavior of OpenSSL is undefined when verify_mode is not set.
141
+ # 2. The net-ldap gem implementation verifies the certificate hostname
142
+ # unless verify_mode is set to VERIFY_NONE.
143
+ { verify_mode: OpenSSL::SSL::VERIFY_NONE }
144
+ end
145
+
146
+ opts.merge!(custom_tls_options)
147
+
148
+ @tls_options = opts
149
+ end
150
+
151
+ def custom_tls_options
152
+ return {} unless options['tls_options']
153
+
154
+ # Dup so we don't overwrite the original value
155
+ custom_options = options['tls_options'].dup.delete_if { |_, value| value.nil? || value.blank? }
156
+ custom_options.symbolize_keys!
157
+
158
+ if custom_options[:cert]
159
+ begin
160
+ custom_options[:cert] = OpenSSL::X509::Certificate.new(custom_options[:cert])
161
+ rescue OpenSSL::X509::CertificateError => e
162
+ Rails.logger.error "LDAP TLS Options 'cert' is invalid for provider #{provider}: #{e.message}"
163
+ end
164
+ end
165
+
166
+ if custom_options[:key]
167
+ begin
168
+ custom_options[:key] = OpenSSL::PKey.read(custom_options[:key])
169
+ rescue OpenSSL::PKey::PKeyError => e
170
+ Rails.logger.error "LDAP TLS Options 'key' is invalid for provider #{provider}: #{e.message}"
171
+ end
114
172
  end
173
+ custom_options
115
174
  end
116
175
 
117
176
  def auth_options
@@ -44,11 +44,17 @@ module Chaltron
44
44
  end
45
45
 
46
46
  def department
47
- entry.department.first rescue nil
47
+ entry.send(Chaltron.ldap_field_mappings[:department]).first rescue nil
48
48
  end
49
49
 
50
50
  def name
51
- entry.cn.first
51
+ if Chaltron.ldap_field_mappings[:full_name].nil?
52
+ first_name = entry.send(Chaltron.ldap_field_mappings[:first_name]).first
53
+ last_name = entry.send(Chaltron.ldap_field_mappings[:last_name]).first
54
+ "#{first_name} #{last_name}"
55
+ else
56
+ entry.send(Chaltron.ldap_field_mappings[:full_name]).first
57
+ end
52
58
  end
53
59
 
54
60
  def uid
@@ -60,7 +66,7 @@ module Chaltron
60
66
  end
61
67
 
62
68
  def email
63
- entry.mail.first rescue nil
69
+ entry.send(Chaltron.ldap_field_mappings[:email]).first rescue nil
64
70
  end
65
71
 
66
72
  def dn
@@ -71,6 +77,10 @@ module Chaltron
71
77
  'ldap'
72
78
  end
73
79
 
80
+ def ldap_groups
81
+ self.class.ldap.find_groups_by_member(self)
82
+ end
83
+
74
84
  private
75
85
 
76
86
  def self.ldap
@@ -20,7 +20,11 @@ module Chaltron
20
20
  entry = Chaltron::LDAP::Person.find_by_uid(username)
21
21
  if user.nil? and create
22
22
  # create user
23
- user = entry.create_user Chaltron.default_roles
23
+ roles = Chaltron.default_roles
24
+ roles = entry.ldap_groups.map do |e|
25
+ Chaltron.ldap_role_mappings[e.dn]
26
+ end.compact if !Chaltron.ldap_role_mappings.blank?
27
+ user = entry.create_user(roles)
24
28
  end
25
29
  update_ldap_attributes(user, entry) unless user.nil?
26
30
  user
@@ -1,3 +1,3 @@
1
1
  module Chaltron
2
- VERSION = '1.0.10'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
@@ -31,6 +31,10 @@ class Ability
31
31
  user ||= User.new
32
32
  if user.is?(:user_admin)
33
33
  can :manage, User
34
+ if Chaltron.ldap_allow_all
35
+ cannot :edit, User, { provider: 'ldap' }
36
+ cannot :destroy, User, { provider: 'ldap' }
37
+ end
34
38
  can :read, Log, category: 'user_admin'
35
39
  end
36
40
  if user.is?(:admin)
@@ -2,13 +2,66 @@ Chaltron.setup do |config|
2
2
  # Add new roles to the right and NEVER change role order, or you'll break every role bitmask
3
3
  # config.roles = %w( admin user_admin )
4
4
 
5
- # If ldap enabled (see config/initializers/devise.rb), set this to true to
6
- # allow every ldap authenitcated users to access you application
7
- # config.ldap_allow_all = false
5
+ # If LDAP enabled (see config/initializers/devise.rb), chaltron must use
6
+ # email field and may use first_name, last_name, full_name, department.
7
+ # Here is the field mapping on you own LDAP server.
8
+ # Default values are the following:
9
+ # config.ldap_field_mappings = {
10
+ # first_name: 'givenname',
11
+ # last_name: 'cn',
12
+ # department: 'department',
13
+ # email: 'mail'
14
+ # }
8
15
 
9
- # Default roles granted to new users (if automatically created)
16
+ # If LDAP enabled, set this to true to allow every ldap authenitcated
17
+ # users to access you application
18
+ # config.ldap_allow_all = true
19
+
20
+ # You may set here default roles granted to new users (if automatically created)
10
21
  # config.default_roles = []
11
22
 
23
+ # Here you may specify a different base for your LDAP groups
24
+ # If not specified the :base parameter defined in Devise.omniauth_configs[:ldap] will be used
25
+ # config.ldap_group_base = 'ou=groups,dc=example,dc=com'
26
+
27
+ # Here you may specify a filter to retrieve LDAP group membership
28
+ # Accept entry (an instance of Chaltron::LDAP::Person) as parameter
29
+ # Default is
30
+ # config.ldap_group_member_filter = -> (entry) { "uniquemember=#{entry.dn}" }
31
+
32
+ # Roles granted to new users may be retrieved by LDAP group membership.
33
+ # config.ldap_role_mappings = {
34
+ # 'DN_of_LDAP_group1' => 'role1',
35
+ # 'DN_of_LDAP_group2' => 'role2'
36
+ # }
37
+
38
+ # The following callback is called after a successful LDAP authentication
39
+ # The callback may manipulate the user instance and
40
+ # must return user if ok, nil if not allowed do login
41
+ # Takes two parameters:
42
+ # - user, current instance of User
43
+ # - ldap, a new instance of Chaltron::LDAP::Connection
44
+ # Default is the following (it does nothing and return user)
45
+ # config.ldap_after_authenticate = -> (user, ldap) { user }
46
+ #
47
+ # Example:
48
+ # config.ldap_after_authenticate = -> (user, ldap) {
49
+ # ldap.find_by_uid(user.username).entry.enabled == ['true'] ? user : nil
50
+ # }
51
+
52
+ # The following callback is called before logout of an LDAP user
53
+ # Takes two parameters:
54
+ # - user, current instance of User
55
+ # - ldap, a new instance of Chaltron::LDAP::Connection
56
+ # Default is the following (does nothing)
57
+ # config.ldap_before_logout = -> (user, ldap) { }
58
+ #
59
+ # Example:
60
+ # config.ldap_before_logout = -> (user, ldap) {
61
+ # ldap.update_attributes(user.extern_uid, { lastLogout: Time.now.strftime('%Y%m%d%H%M%S%z') })
62
+ # }
63
+ #
64
+
12
65
  # If syslog enabled, all Log records will be available also in syslog flow
13
66
  # config.enable_syslog = false
14
67
  # config.syslog_facility = Syslog::LOG_SYSLOG
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chaltron
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.10
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - vicvega
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-15 00:00:00.000000000 Z
11
+ date: 2019-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails