chaltron 0.2.11 → 0.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: f0db5689d36867b5dbe81aed50248c93189c3ed321fab99e007ad1003b40b506
4
- data.tar.gz: 29853e9008306cd04372a6e5c2fd332136b6e94e2749bf4c454ee04919552be6
3
+ metadata.gz: 7b464d3bc81862d834f059858174adf7461d64906a4e4ca665be9e1b7a41808f
4
+ data.tar.gz: c9c441c67ffa67a0710572821f7080b2865c31b8e70e8a1ab7ec33d566822bec
5
5
  SHA512:
6
- metadata.gz: 3c68397cbd1118f2ecc124d79146bffffe4428f40cc8a7a257068511d80975d7d8635f41c12cd2f3cafd14015dbcee7ba9083ad5c272cf91a6a6ae93ce2d9c8d
7
- data.tar.gz: c6120a6ba5a149bcd84d2f253754a3df5de558f19370b7fe23088815f5400dc57d2639069f23206381dd6c46c7ca4fa44fef39362c4da182188283d94def2765
6
+ metadata.gz: a2bec8d1c227de443791a47dace2291c4ff18e4302b1f3214ede923660570ffd32d4b00355d042e2743e53a20aacdb75f8a14f0e95ca7ee2fb114f3331ca9039
7
+ data.tar.gz: 669ccd46e0c625ce65c3695b57afe6a76aef174766613242f63d1be222f9af97f70d7e9bcff1b92c02a35fd47fdaa6ddbb956edac76efa607e64f3b2c836ba84
@@ -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
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
data/app/models/log.rb CHANGED
@@ -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
data/app/models/user.rb CHANGED
@@ -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
@@ -4,7 +4,7 @@
4
4
  <%= bootstrap_form_tag(url: ldap_multi_new_path, method: :post, layout: :horizontal) do |f| %>
5
5
 
6
6
  <%= f.text_field :userid, label: t('.name_label'), help: t('.name_help') %>
7
- <%= f.text_field :fullname, label: t('.fullname_label'), help: t('.fullname_help') %>
7
+ <%= f.text_field :lastname, label: t('.lastname_label'), help: t('.lastname_help') %>
8
8
  <%= f.text_field :department, label: t('.department_label'), help: t('.department_help') %>
9
9
  <%= f.text_field :limit, label: t('.limit_label'), help: t('.limit_help'), value: @limit %>
10
10
 
@@ -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='btn-group'>
10
10
  <%= content_tag :button, type: 'button', class: 'btn btn-primary dropdown-toggle',
11
11
  data: {toggle: 'dropdown'}, aria: {expanded: false} do %>
@@ -66,11 +66,11 @@
66
66
  <div class='pull-right'>
67
67
  <%= link_to edit_user_path(@user), class: 'btn btn-default' do %>
68
68
  <%= icon :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 :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)
74
- fullname_label: Fullname
75
- fullname_help: Search for first name or surname (also partial match)
73
+ name_help: Search by user-id (exact match)
74
+ lastname_label: Last name
75
+ lastname_help: Search by last name or surname (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
@@ -11,12 +11,12 @@ SimpleNavigation::Configuration.run do |navigation|
11
11
  admin.item :users,
12
12
  { icon: 'fa fa-fw fa-users',
13
13
  text: I18n.t('chaltron.menu.users')
14
- }, users_path, highlights_on: /\/(users|ldap)(?!\/self_(show|edit|update))/ if can?(:manage, User)
14
+ }, users_path, highlights_on: /\/(users|ldap)(?!\/self_(show|edit|update))/ if can?(:read, User)
15
15
  admin.item :logs,
16
16
  { icon: 'fa fa-fw fa-book',
17
17
  text: I18n.t('chaltron.menu.logs')
18
18
  }, logs_path, highlights_on: /\/logs/ if can?(:read, Log)
19
- end if can?(:manage, User) or can?(:read, Log)
19
+ end if can?(:read, User) or can?(:read, Log)
20
20
  primary.item :logged, current_user.display_name.html_safe, nil do |user|
21
21
  user.dom_class = 'dropdown-menu-right'
22
22
  user.item :self_edit, { icon: 'fa fa-fw fa-user',
data/config/routes.rb CHANGED
@@ -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
data/lib/chaltron.rb CHANGED
@@ -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 = '0.2.11'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
@@ -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: 0.2.11
4
+ version: 0.3.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-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -405,6 +405,7 @@ files:
405
405
  - app/helpers/chaltron/logs_helper.rb
406
406
  - app/helpers/chaltron/users_helper.rb
407
407
  - app/helpers/chaltron_helper.rb
408
+ - app/models/application_record.rb
408
409
  - app/models/authorizable.rb
409
410
  - app/models/locales/en.yml
410
411
  - app/models/locales/it.yml