module ASF::LDAP
Constants
- CONNECT_LOCK
-
Mutex preventing simultaneous connections to
LDAP
from a single process - LDAP_CREDS
- LDAP_MISC
Public Class Methods
Source
Source
# File lib/whimsy/asf/ldap.rb, line 97 def self.bind(user=nil, password=nil, &block) if not user or not password raise ArgumentError.new('Need user name and password') unless STDIN.isatty require 'etc' require 'io/console' user ||= Etc.getlogin password = STDIN.getpass("Password for #{user}:") end dn = ASF::Person.new(user).dn begin @ldap.unbind if @ldap&.bound? rescue StandardError # ignore end self.rwhosts.each do |rwhost| begin ldap = ASF._init_ldap(true, [rwhost]) Wunderbar.debug("#{ldap.object_id}: bind as #{dn} as #{ldap}") if block ASF.flush_weakrefs ldap.bind(dn, password, &block) ASF._init_ldap(true) else ldap.bind(dn, password) end break rescue ::LDAP::ResultError => e if e.message == "Can't contact LDAP server" # Any others worth a retry? Wunderbar.warn "#{rwhost}: #{e.inspect}, continuing" else raise end end end ensure ASF.flush_weakrefs end
connect to LDAP
with a user and password; generally required for update operations. If a block is passed, the connection will be closed after the block executes.
when run interactively, will default user and prompt for password
Source
# File lib/whimsy/asf/ldap.rb, line 232 def self.configure cert = Dir["#{ETCLDAP}/asf*-ldap-client.pem"].first # verify/obtain/write the cert unless cert cert = "#{ETCLDAP}/asf-ldap-client.pem" File.write cert, self.extract_cert end # read the current configuration file ldap_conf = "#{ETCLDAP}/ldap.conf" content = File.read(ldap_conf) # ensure that the right cert is used unless content =~ /asf.*-ldap-client\.pem/ content.gsub!(/^TLS_CACERT/i, '# TLS_CACERT') content += "TLS_CACERT #{ETCLDAP}/asf-ldap-client.pem\n" end # provide the URIs of the ldap hosts content.gsub!(/^URI/, '# URI') content += "uri \n" unless content =~ /^uri / content[/uri (.*)\n/, 1] = hosts(false).join(' ') # verify/set the base unless content.include? 'base dc=apache' content.gsub!(/^BASE/i, '# BASE') content += "base dc=apache,dc=org\n" end # ensure TLS_REQCERT is allow (macOS only) if ETCLDAP.include? 'openldap' and not content.include? 'REQCERT allow' content.gsub!(/^TLS_REQCERT/i, '# TLS_REQCERT') content += "TLS_REQCERT allow\n" end # write the configuration if there were any changes File.write(ldap_conf, content) unless content == File.read(ldap_conf) end
update /etc/ldap.conf. Usage:
sudo ruby -I /srv/whimsy/lib -r whimsy/asf -e "ASF::LDAP.configure"
Source
# File lib/whimsy/asf/ldap.rb, line 273 def self.configured? return File.read("#{ETCLDAP}/ldap.conf").include? 'asf-ldap-client.pem' end
determine if ldap has been configured at least once
Source
# File lib/whimsy/asf/ldap.rb, line 58 def self.connect(hosts = nil) # If the host list is specified, use that as is # otherwise ensure we start with the next in the default list hosts ||= self.hosts.rotate! # Try each host at most once hosts.each do |host| Wunderbar.info "[#{host}] - Connecting to LDAP server" begin # request connection uri = URI.parse(host) if uri.scheme == 'ldaps' ldap = ::LDAP::SSLConn.new(uri.host, uri.port) else ldap = ::LDAP::Conn.new(uri.host, uri.port) end # save the host @host = host return ldap rescue ::LDAP::ResultError => re Wunderbar.warn "[#{host}] - Error connecting to LDAP server: " + re.message + ' (continuing)' end end Wunderbar.error 'Failed to connect to any LDAP host' return nil end
connect to LDAP
Source
Source
# File lib/whimsy/asf/ldap.rb, line 219 def self.extract_cert(host=nil) host ||= hosts.sample[%r{//(.*?)(/|$)}, 1] host += ':636' unless host =~ %r{:\d+\z} cmd = ['openssl', 's_client', '-connect', host, '-showcerts'] puts cmd.join(' ') out, _, _ = Open3.capture3(*cmd) out.scan(/^-+BEGIN.*?\n-+END[^\n]+\n/m).last end
query and extract cert from openssl output returns the last certificate found (WHIMSY-368)
Source
# File lib/whimsy/asf/ldap.rb, line 158 def self.host @host end
Return the last chosen host (if any)
Source
# File lib/whimsy/asf/ldap.rb, line 192 def self.hosts(use_config = true) return @hosts if @hosts # cache the hosts list # try whimsy config (overrides ldap.conf) hosts = Array(ASF::Config.get(:ldap)) # check system configuration if use_config and hosts.empty? conf = "#{ETCLDAP}/ldap.conf" if File.exist? conf uris = File.read(conf)[/^uri\s+(.*)/i, 1].to_s hosts = uris.scan(%r{ldaps?://\S+}) # May not have a port Wunderbar.debug 'Using hosts from LDAP config' end else Wunderbar.debug 'Using hosts from Whimsy config' end # There is no default raise 'Cannot determine LDAP URI from ldap.conf or local config!' if hosts.empty? hosts.shuffle! # Wunderbar.debug "Hosts:\n#{hosts.join(' ')}" @hosts = hosts end
determine what LDAP
hosts are available use_config=false is needed for the configure method only
Source
# File lib/whimsy/asf/ldap.rb, line 140 def self.http_auth(string, &block) auth = Base64.decode64(string.to_s[/Basic (.*)/, 1] || '') user, password = auth.split(':', 2) return unless password if block self.bind(user, password, &block) else begin ASF::LDAP.bind(user, password) {} return ASF::Person.new(user) rescue ::LDAP::ResultError return nil end end end
validate HTTP authorization, and optionally invoke a block bound to that user.
Source
Source
# File lib/whimsy/asf/ldap.rb, line 163 def self.rwhosts return @rwhosts if @rwhosts # cache the rwhosts list rwhosts = Array(ASF::Config.get(:ldaprw)) # allow separate override for RW LDAP if rwhosts.empty? if File.exist? LDAP_MISC begin ldap_misc = YAML.safe_load(File.read(LDAP_MISC)) rwhosts = Array(ldap_misc['ldapclient_asf']['write_uri']) rescue StandardError => e Wunderbar.warn "Could not parse write_uri: #{e.inspect}" end else Wunderbar.warn "Could not find #{LDAP_MISC}" end if rwhosts.empty? # default to RO hosts rwhosts = hosts Wunderbar.debug 'Using rwhosts from hosts' else Wunderbar.debug 'Using rwhosts from LDAP_MISC' end else Wunderbar.debug 'Using rwhosts from Whimsy config' end raise 'Cannot determine writable LDAP URI from ldap.conf or local config!' if rwhosts.empty? @rwhosts = rwhosts end
allow override of writable host by :ldaprw