2022-01-01 13:38:12 +00:00
|
|
|
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
2021-06-01 12:20:20 +00:00
|
|
|
|
2017-04-19 10:09:54 +00:00
|
|
|
class Ldap
|
|
|
|
|
|
|
|
# Class for handling LDAP Groups.
|
|
|
|
class Group
|
|
|
|
include Ldap::FilterLookup
|
|
|
|
|
|
|
|
# Returns the uid attribute.
|
|
|
|
#
|
|
|
|
# @example
|
|
|
|
# Ldap::Group.uid_attribute
|
|
|
|
#
|
|
|
|
# @return [String] The uid attribute.
|
|
|
|
def self.uid_attribute
|
|
|
|
'dn'
|
|
|
|
end
|
|
|
|
|
|
|
|
# Initializes a wrapper around Net::LDAP and ::Ldap to handle LDAP groups.
|
|
|
|
#
|
|
|
|
# @param [Hash] config the configuration for establishing a LDAP connection. Default is Setting 'ldap_config'.
|
|
|
|
# @option config [String] :uid_attribute The uid attribute. Default is determined automatically.
|
|
|
|
# @option config [String] :filter The filter for LDAP groups. Default is determined automatically.
|
|
|
|
# @param ldap [Ldap] An optional existing Ldap class instance. Default is a new connection with given configuration.
|
|
|
|
#
|
|
|
|
# @example
|
|
|
|
# ldap_group = Ldap::Group.new
|
|
|
|
#
|
|
|
|
# @return [nil]
|
|
|
|
def initialize(config = nil, ldap: nil)
|
|
|
|
@ldap = ldap || ::Ldap.new(config)
|
|
|
|
|
|
|
|
handle_config(config)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Lists available LDAP groups.
|
|
|
|
#
|
|
|
|
# @param filter [String] The filter for listing groups. Default is initialization parameter.
|
|
|
|
# @param base_dn [String] The applied base DN for listing groups. Default is Ldap#base_dn.
|
|
|
|
#
|
|
|
|
# @example
|
|
|
|
# ldap_group.list
|
|
|
|
# #=> {"cn=zamamd role admin,ou=zamamd groups,ou=test,dc=domain,dc=tld"=>"cn=zamamd role admin,ou=zamamd groups,ou=test,dc=domain,dc=tld", ...}
|
|
|
|
#
|
|
|
|
# @return [Hash{String=>String}] List of available LDAP groups.
|
|
|
|
def list(filter: nil, base_dn: nil)
|
|
|
|
|
|
|
|
filter ||= filter()
|
|
|
|
|
|
|
|
# don't start a search if no filter was found
|
|
|
|
return {} if filter.blank?
|
|
|
|
|
|
|
|
groups = {}
|
2017-11-23 08:09:44 +00:00
|
|
|
@ldap.search(filter, base: base_dn, attributes: %w[dn]) do |entry|
|
2017-04-19 10:09:54 +00:00
|
|
|
groups[entry.dn.downcase] = entry.dn.downcase
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
groups
|
|
|
|
end
|
|
|
|
|
|
|
|
# Creates a mapping for user DN and local role IDs based on a given group DN to local role ID mapping.
|
|
|
|
#
|
|
|
|
# @param mapping [Hash{String=>String}] The group DN to local role mapping.
|
|
|
|
# @param filter [String] The filter for finding groups. Default is initialization parameter.
|
|
|
|
#
|
|
|
|
# @example
|
|
|
|
# mapping = {"cn=access control assistance operators,cn=builtin,dc=domain,dc=tld"=>"1", ...}
|
|
|
|
# ldap_group.user_roles(mapping)
|
|
|
|
# #=> {"cn=s-1-5-11,cn=foreignsecurityprincipals,dc=domain,dc=tld"=>[1, 2], ...}
|
|
|
|
#
|
|
|
|
# @return [Hash{String=>Array<Number>}] The user DN to local role IDs mapping.
|
|
|
|
def user_roles(mapping, filter: nil)
|
|
|
|
|
|
|
|
filter ||= filter()
|
|
|
|
|
|
|
|
result = {}
|
2018-01-09 19:08:46 +00:00
|
|
|
@ldap.search(filter, attributes: %w[dn member memberuid uniquemember]) do |entry|
|
2017-04-19 10:09:54 +00:00
|
|
|
|
2017-05-30 06:26:45 +00:00
|
|
|
roles = mapping[entry.dn.downcase]
|
|
|
|
next if roles.blank?
|
2017-04-19 10:09:54 +00:00
|
|
|
|
2017-06-16 08:09:42 +00:00
|
|
|
members = group_user_dns(entry)
|
|
|
|
next if members.blank?
|
|
|
|
|
2017-04-19 10:09:54 +00:00
|
|
|
members.each do |user_dn|
|
|
|
|
user_dn_key = user_dn.downcase
|
|
|
|
|
2017-05-30 06:26:45 +00:00
|
|
|
roles.each do |role|
|
|
|
|
role = role.to_i
|
|
|
|
|
|
|
|
result[user_dn_key] ||= []
|
|
|
|
next if result[user_dn_key].include?(role)
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2017-05-30 06:26:45 +00:00
|
|
|
result[user_dn_key].push(role)
|
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
|
|
|
# The active filter of the instance. If none give on initialization an automatic lookup is performed.
|
|
|
|
#
|
|
|
|
# @example
|
|
|
|
# ldap_group.filter
|
|
|
|
# #=> '(objectClass=group)'
|
|
|
|
#
|
|
|
|
# @return [String, nil] The active or found filter or nil if none could be found.
|
|
|
|
def filter
|
2020-04-27 17:36:34 +00:00
|
|
|
@filter ||= lookup_filter(['(objectClass=groupOfUniqueNames)', '(objectClass=groupOfNames)', '(objectClass=group)', '(objectClass=posixgroup)', '(objectClass=organization)'])
|
2017-04-19 10:09:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# The active uid attribute of the instance. If none give on initialization an automatic lookup is performed.
|
|
|
|
#
|
|
|
|
# @example
|
|
|
|
# ldap_group.uid_attribute
|
|
|
|
# #=> 'dn'
|
|
|
|
#
|
|
|
|
# @return [String, nil] The active or found uid attribute or nil if none could be found.
|
|
|
|
def uid_attribute
|
|
|
|
@uid_attribute ||= self.class.uid_attribute
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def handle_config(config)
|
|
|
|
return if config.blank?
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2017-04-19 10:09:54 +00:00
|
|
|
@uid_attribute = config[:uid_attribute]
|
|
|
|
@filter = config[:filter]
|
|
|
|
end
|
2017-06-16 08:09:42 +00:00
|
|
|
|
|
|
|
def group_user_dns(entry)
|
|
|
|
return entry[:member] if entry[:member].present?
|
2018-01-09 13:58:33 +00:00
|
|
|
return group_user_dns_memberuid(entry) if entry[:memberuid].present?
|
|
|
|
return entry[:uniquemember] if entry[:uniquemember].present?
|
|
|
|
end
|
2017-06-16 08:09:42 +00:00
|
|
|
|
2018-01-09 13:58:33 +00:00
|
|
|
def group_user_dns_memberuid(entry)
|
2021-06-24 07:05:39 +00:00
|
|
|
entry[:memberuid].filter_map do |uid|
|
2017-06-16 08:09:42 +00:00
|
|
|
dn = nil
|
2018-05-16 14:36:26 +00:00
|
|
|
@ldap.search("(&(uid=#{uid})#{Import::Ldap.config[:user_filter]})", attributes: %w[dn]) do |user|
|
2017-06-16 08:09:42 +00:00
|
|
|
dn = user.dn
|
|
|
|
end
|
|
|
|
dn
|
2021-06-24 07:05:39 +00:00
|
|
|
end
|
2017-06-16 08:09:42 +00:00
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
end
|
|
|
|
end
|