diff --git a/Gemfile b/Gemfile index 444cdb21eb..431308dc21 100644 --- a/Gemfile +++ b/Gemfile @@ -9,7 +9,7 @@ gem 'audited', '~> 5.0', '!= 5.1.0' gem 'will_paginate', '~> 3.3' gem 'ancestry', '~> 4.0' gem 'scoped_search', '>= 4.1.10', '< 5' -gem 'ldap_fluff', '>= 0.7.0', '< 1.0' +gem 'ldap_fluff', '>= 0.9.0', '< 1.0' gem 'apipie-rails', '>= 0.8.0', '< 2' gem 'apipie-dsl', '>= 2.6.2' # Pin rdoc to prevent updating bundled psych (https://github.com/ruby/rdoc/commit/ebe185c8775b2afe844eb3da6fa78adaa79e29a4) diff --git a/app/controllers/api/v2/auth_source_ldaps_controller.rb b/app/controllers/api/v2/auth_source_ldaps_controller.rb index f44eef3a1c..85e0295869 100644 --- a/app/controllers/api/v2/auth_source_ldaps_controller.rb +++ b/app/controllers/api/v2/auth_source_ldaps_controller.rb @@ -45,7 +45,8 @@ def show param :usergroup_sync, :bool, :desc => N_("sync external user groups on login") param :tls, :bool param :groups_base, String, :desc => N_("groups base DN") - param :use_netgroups, :bool, :desc => N_("use NIS netgroups instead of posix groups, applicable only when server_type is posix or free_ipa") + param :use_netgroups, :bool, :desc => N_("use NIS netgroups instead of posix groups, applicable only when server_type is posix or free_ipa. Deprecated in favor of ldap_group_membership = nis_netgroups"), :deprecated => true + param :ldap_group_membership, AuthSourceLdap::GROUP_MEMBERSHIP_TYPES.keys, :desc => N_("type of group membership to use, applicable only when server_type is posix, free_ipa or netiq. Option rfc4519 is only applicable when server_type is posix.") param :server_type, AuthSourceLdap::SERVER_TYPES.keys, :desc => N_("type of the LDAP server") param :ldap_filter, String, :desc => N_("LDAP filter") param_group :taxonomies, ::Api::V2::BaseController diff --git a/app/controllers/concerns/foreman/controller/parameters/auth_source_ldap.rb b/app/controllers/concerns/foreman/controller/parameters/auth_source_ldap.rb index fc33d489a2..71c43acc93 100644 --- a/app/controllers/concerns/foreman/controller/parameters/auth_source_ldap.rb +++ b/app/controllers/concerns/foreman/controller/parameters/auth_source_ldap.rb @@ -22,7 +22,8 @@ def auth_source_ldap_params_filter :server_type, :tls, :usergroup_sync, - :use_netgroups + :use_netgroups, + :ldap_group_membership add_taxonomix_params_filter(filter) end @@ -30,6 +31,18 @@ def auth_source_ldap_params_filter end def auth_source_ldap_params - self.class.auth_source_ldap_params_filter.filter_params(params, parameter_filter_context) + filtered = self.class.auth_source_ldap_params_filter.filter_params(params, parameter_filter_context) + if filtered.key?("use_netgroups") + Foreman::Deprecation.deprecation_warning("3.18", "`use_netgroups` parameter is deprecated, use `ldap_group_membership` instead.") + + expected = filtered["use_netgroups"] ? %w(nis_netgroups) : %w(posix rfc4519) + if filtered["ldap_group_membership"].blank? + filtered["ldap_group_membership"] = expected.first + elsif !expected.include?(filtered["ldap_group_membership"]) + ::Rails.logger.warn("Conflicting values for `use_netgroups` and `ldap_group_membership` parameters provided, ignoring `use_netgroups`.") + end + filtered.delete("use_netgroups") + end + filtered end end diff --git a/app/models/auth_sources/auth_source_ldap.rb b/app/models/auth_sources/auth_source_ldap.rb index bc5bbfd372..24baabe2bb 100644 --- a/app/models/auth_sources/auth_source_ldap.rb +++ b/app/models/auth_sources/auth_source_ldap.rb @@ -22,6 +22,8 @@ class AuthSourceLdap < AuthSource SERVER_TYPES = { :free_ipa => 'FreeIPA', :active_directory => 'Active Directory', :posix => 'POSIX', :netiq => "NetIQ"} + GROUP_MEMBERSHIP_TYPES = { :posix => 'POSIX', :rfc4519 => 'POSIX + RFC4519', :nis_netgroups => 'NIS Netgroups' } + extend FriendlyId friendly_id :name include Parameterizable::ByIdName @@ -35,10 +37,11 @@ class AuthSourceLdap < AuthSource validates :account_password, :length => {:maximum => 69}, :allow_nil => true validates :port, :presence => true, :numericality => {:only_integer => true} validates :server_type, :presence => true, :inclusion => { :in => SERVER_TYPES.keys.map(&:to_s) } + validates :ldap_group_membership, :presence => true, :inclusion => { :in => :allowed_group_membership_types }, :if => proc { |auth| %w[posix netiq free_ipa].include?(auth.server_type.to_s) } validate :validate_ldap_filter, :unless => proc { |auth| auth.ldap_filter.blank? } before_validation :strip_ldap_attributes - before_validation :sanitize_use_netgroups + before_validation :sanitize_group_membership after_initialize :set_defaults, if: :new_record? scoped_search :on => :name, :complete_value => :true @@ -92,7 +95,8 @@ def to_config(login = nil, password = nil) :anon_queries => account.blank?, :service_user => service_user(login), :service_pass => use_user_login_for_service? ? password : account_password, :instrumentation_service => ActiveSupport::Notifications, - :use_netgroups => use_netgroups } + :use_netgroups => ldap_group_membership == 'nis_netgroups', + :use_rfc4519_group_membership => ldap_group_membership == 'rfc4519' } end def encryption_config @@ -195,8 +199,13 @@ def strip_ldap_attributes end end - def sanitize_use_netgroups - self.use_netgroups = false if server_type.to_s == 'active_directory' + def sanitize_group_membership + if server_type.to_s == 'active_directory' + self.ldap_group_membership = nil + else + self.ldap_group_membership ||= 'posix' + end + true end @@ -276,4 +285,15 @@ def use_user_login_for_service? def service_user(login) use_user_login_for_service? ? account.sub("$login", login) : account end + + def allowed_group_membership_types + case server_type.to_s + when 'posix' + GROUP_MEMBERSHIP_TYPES.keys.map(&:to_s) + when 'netiq', 'free_ipa' + ['posix', 'nis_netgroups'] + when 'active_directory' + [] + end + end end diff --git a/app/views/api/v2/auth_source_ldaps/main.json.rabl b/app/views/api/v2/auth_source_ldaps/main.json.rabl index 7be0675786..13e24a718e 100644 --- a/app/views/api/v2/auth_source_ldaps/main.json.rabl +++ b/app/views/api/v2/auth_source_ldaps/main.json.rabl @@ -4,4 +4,8 @@ extends "api/v2/auth_source_ldaps/base" attributes :host, :port, :account, :base_dn, :ldap_filter, :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :attr_photo, :onthefly_register, :usergroup_sync, :tls, :server_type, :groups_base, - :use_netgroups, :created_at, :updated_at + :ldap_group_membership, :created_at, :updated_at + +node :use_netgroups do |auth_source_ldap| + auth_source_ldap.ldap_group_membership == 'nis_netgroups' +end diff --git a/app/views/auth_source_ldaps/_form.html.erb b/app/views/auth_source_ldaps/_form.html.erb index 4011cc0e0b..5b645a426a 100644 --- a/app/views/auth_source_ldaps/_form.html.erb +++ b/app/views/auth_source_ldaps/_form.html.erb @@ -29,7 +29,16 @@ <%= password_f f, :account_password, :help_inline => _("Use this account to authenticate, optional").html_safe, :unset => action_name == "edit" %> <%= text_f f, :base_dn, :label => _("Base DN"), :size => "col-md-8", :label_help => base_dn_help_data[@auth_source_ldap.server_type], :data => { :help => base_dn_help_data } %> <%= text_f f, :groups_base, :label => _("Groups base DN"), :size => "col-md-8", :label_help => groups_base_dn_help_data[@auth_source_ldap.server_type], :data => { :help => groups_base_dn_help_data } %> - <%= checkbox_f f, :use_netgroups, :help_inline => _("Use NIS netgroups instead of posix groups."), :label_help => _('By default we map user groups to standard LDAP Group objects. FreeIPA and POSIX LDAP server types supports alternative way of grouping users through Netgroups. Enable this checkbox if using Netgroups is preferred instead of standard groups.') %> + + <%= select_f f, :ldap_group_membership, AuthSourceLdap::GROUP_MEMBERSHIP_TYPES, :first, :last, + { }, + { :label => _('Group membership type'), + :help_inline => _("Controls which mechanism will be used for looking up users' group membership in LDAP."), + :label_help => "".html_safe } %> <%= textarea_f f, :ldap_filter, :label => _("LDAP filter"), :help_block => _("Custom LDAP search filter, optional").html_safe, :size => "col-md-8" %> <%= checkbox_f f, :onthefly_register, diff --git a/db/migrate/20250528092104_extend_ldap_group_membership_options.rb b/db/migrate/20250528092104_extend_ldap_group_membership_options.rb new file mode 100644 index 0000000000..b624cc9b34 --- /dev/null +++ b/db/migrate/20250528092104_extend_ldap_group_membership_options.rb @@ -0,0 +1,14 @@ +class ExtendLdapGroupMembershipOptions < ActiveRecord::Migration[7.0] + def up + add_column :auth_sources, :ldap_group_membership, :string + AuthSourceLdap.where.not(server_type: 'active_directory').update_all(ldap_group_membership: 'posix') + AuthSourceLdap.where(use_netgroups: true).update_all(ldap_group_membership: 'nis_netgroups') + remove_column :auth_sources, :use_netgroups + end + + def down + add_column :auth_sources, :use_netgroups, :boolean, :default => false + AuthSourceLdap.where(ldap_group_membership: 'nis_netgroups').update_all(use_netgroups: true) + remove_column :auth_sources, :ldap_group_membership + end +end diff --git a/script/lint/lint_core_config.js b/script/lint/lint_core_config.js index dfcb7ed65d..25b20804db 100644 --- a/script/lint/lint_core_config.js +++ b/script/lint/lint_core_config.js @@ -121,6 +121,7 @@ module.exports = { 'poolsurl', 'popstate', 'posinset', + 'posix', 'pqr', 'ptable', 'puppetclass', @@ -137,6 +138,7 @@ module.exports = { 'repo', 'resize', 'rex', + 'rfc4519', 'rhel', 'safemode', 'sbs', diff --git a/test/factories/auth_source_ldap.rb b/test/factories/auth_source_ldap.rb index ad82f11497..41cc160d17 100644 --- a/test/factories/auth_source_ldap.rb +++ b/test/factories/auth_source_ldap.rb @@ -8,6 +8,7 @@ attr_lastname { 'daho' } port { '389' } server_type { 'posix' } + ldap_group_membership { 'posix' } end trait :posix diff --git a/test/models/auth_sources/auth_source_ldap_test.rb b/test/models/auth_sources/auth_source_ldap_test.rb index 4ea4db3c58..f2afecb6bc 100644 --- a/test/models/auth_sources/auth_source_ldap_test.rb +++ b/test/models/auth_sources/auth_source_ldap_test.rb @@ -58,11 +58,21 @@ def setup end test "it enforces use_netgroups to false for active directory" do - auth_source_ldap.use_netgroups = true + auth_source_ldap.ldap_group_membership = 'nis_netgroups' auth_source_ldap.server_type = :active_directory assert auth_source_ldap.valid? - refute auth_source_ldap.use_netgroups + assert_nil auth_source_ldap.ldap_group_membership + end + + test "it propagates group membership to the config" do + auth_source_ldap.ldap_group_membership = 'rfc4519' + assert auth_source_ldap.to_config[:use_rfc4519_group_membership] + refute auth_source_ldap.to_config[:use_netgroups] + + auth_source_ldap.ldap_group_membership = 'nis_netgroups' + refute auth_source_ldap.to_config[:use_rfc4519_group_membership] + assert auth_source_ldap.to_config[:use_netgroups] end test "return nil if login is blank or password is blank" do diff --git a/webpack/assets/javascripts/foreman_auth_source.js b/webpack/assets/javascripts/foreman_auth_source.js index b8e0a2ce95..7730886d09 100644 --- a/webpack/assets/javascripts/foreman_auth_source.js +++ b/webpack/assets/javascripts/foreman_auth_source.js @@ -83,14 +83,30 @@ function updateLdapAccountHelp(selectedType) { export function changeLdapServerType() { const type = $('#auth_source_ldap_server_type').val(); - $('#auth_source_ldap_use_netgroups') - .closest('.form-group') - .toggle(type !== 'active_directory'); + const membershipType = $('#auth_source_ldap_ldap_group_membership'); + + membershipType.closest('.form-group').toggle(type !== 'active_directory'); + + if (type !== 'active_directory') { + const rfc4519 = $( + '#auth_source_ldap_ldap_group_membership option[value="rfc4519"]' + ); + + if (type !== 'posix') { + rfc4519.attr('disabled', 'disabled'); + if (membershipType.find(':selected').val() === 'rfc4519') { + membershipType.val('posix').trigger('change'); + } + } else { + rfc4519.removeAttr('disabled'); + } + } + updateLdapAccountHelp(type); } $(document).ready(() => { - if (window.location.pathname.match('auth_source_ldaps/i')) { + if (window.location.pathname.match(/auth_source_ldaps/i)) { changeLdapServerType(); } });