45ab20dca9
for normalizing and parsing DNs
133 lines
4 KiB
Ruby
133 lines
4 KiB
Ruby
module Gitlab
|
|
module LDAP
|
|
class Person
|
|
# Active Directory-specific LDAP filter that checks if bit 2 of the
|
|
# userAccountControl attribute is set.
|
|
# Source: http://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/
|
|
AD_USER_DISABLED = Net::LDAP::Filter.ex("userAccountControl:1.2.840.113556.1.4.803", "2")
|
|
|
|
attr_accessor :entry, :provider
|
|
|
|
def self.find_by_uid(uid, adapter)
|
|
uid = Net::LDAP::Filter.escape(uid)
|
|
adapter.user(adapter.config.uid, uid)
|
|
end
|
|
|
|
def self.find_by_dn(dn, adapter)
|
|
adapter.user('dn', dn)
|
|
end
|
|
|
|
def self.find_by_email(email, adapter)
|
|
email_fields = adapter.config.attributes['email']
|
|
|
|
adapter.user(email_fields, email)
|
|
end
|
|
|
|
def self.disabled_via_active_directory?(dn, adapter)
|
|
adapter.dn_matches_filter?(dn, AD_USER_DISABLED)
|
|
end
|
|
|
|
def self.ldap_attributes(config)
|
|
[
|
|
'dn', # Used in `dn`
|
|
config.uid, # Used in `uid`
|
|
*config.attributes['name'], # Used in `name`
|
|
*config.attributes['email'] # Used in `email`
|
|
]
|
|
end
|
|
|
|
# Returns the UID in a normalized form.
|
|
#
|
|
# 1. Excess spaces are stripped
|
|
# 2. The string is downcased (for case-insensitivity)
|
|
def self.normalize_uid(uid)
|
|
normalize_dn_part(uid)
|
|
rescue StandardError => e
|
|
Rails.logger.info("Returning original UID \"#{uid}\" due to error during normalization attempt: #{e.message}")
|
|
Rails.logger.info(e.backtrace.join("\n"))
|
|
|
|
uid
|
|
end
|
|
|
|
def initialize(entry, provider)
|
|
Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
|
|
@entry = entry
|
|
@provider = provider
|
|
end
|
|
|
|
def name
|
|
attribute_value(:name).first
|
|
end
|
|
|
|
def uid
|
|
entry.public_send(config.uid).first # rubocop:disable GitlabSecurity/PublicSend
|
|
end
|
|
|
|
def username
|
|
uid
|
|
end
|
|
|
|
def email
|
|
attribute_value(:email)
|
|
end
|
|
|
|
def dn
|
|
DN.new(entry.dn).to_s_normalized
|
|
end
|
|
|
|
private
|
|
|
|
def self.normalize_dn_part(part)
|
|
cleaned = part.strip.downcase
|
|
|
|
if cleaned.ends_with?('\\')
|
|
# If it ends with an escape character that is not followed by a
|
|
# character to be escaped, then this part may be malformed. But let's
|
|
# not worry too much about it, and just return it unmodified.
|
|
#
|
|
# Why? Because the reason we clean DNs is to make our simplistic
|
|
# string comparisons work better, even though there are all kinds of
|
|
# ways that equivalent DNs can vary as strings. If we run into a
|
|
# strange DN, we should just try to work with it.
|
|
#
|
|
# See https://www.ldap.com/ldap-dns-and-rdns for more.
|
|
return part unless part.ends_with?(' ')
|
|
|
|
# Ends with an escaped space (which is valid).
|
|
cleaned = cleaned + ' '
|
|
end
|
|
|
|
# Get rid of blanks. This can happen if a split character is followed by
|
|
# whitespace and then another split character.
|
|
#
|
|
# E.g. this DN: 'uid=john+telephoneNumber= +1 555-555-5555'
|
|
#
|
|
# Should be returned as: 'uid=john+telephoneNumber=+1 555-555-5555'
|
|
cleaned = '' if cleaned.blank?
|
|
|
|
cleaned
|
|
end
|
|
|
|
def entry
|
|
@entry
|
|
end
|
|
|
|
def config
|
|
@config ||= Gitlab::LDAP::Config.new(provider)
|
|
end
|
|
|
|
# Using the LDAP attributes configuration, find and return the first
|
|
# attribute with a value. For example, by default, when given 'email',
|
|
# this method looks for 'mail', 'email' and 'userPrincipalName' and
|
|
# returns the first with a value.
|
|
def attribute_value(attribute)
|
|
attributes = Array(config.attributes[attribute.to_s])
|
|
selected_attr = attributes.find { |attr| entry.respond_to?(attr) }
|
|
|
|
return nil unless selected_attr
|
|
|
|
entry.public_send(selected_attr) # rubocop:disable GitlabSecurity/PublicSend
|
|
end
|
|
end
|
|
end
|
|
end
|