class ASF::Person

An instance of this class represents a person. Data comes from a variety of sources: LDAP, asf--authorization-template, iclas.txt, members.txt, nominated-members.txt, and potential-member-watch-list.txt.

Constants

SUFFIXES

generational suffixes

Public Class Methods

[](id) click to toggle source

return person only if it actually exits

Calls superclass method ASF::Base::[]
# File lib/whimsy/asf/ldap.rb, line 721
def self.[] id
  person = super
  person.attrs['dn'] ? person : nil
end
add(attrs) click to toggle source

add a new person to LDAP. Attrs must include uid, cn, and mail

# File lib/whimsy/asf/ldap.rb, line 891
def self.add(attrs)
  # convert keys to strings
  attrs = attrs.map {|key, value| [key.to_s, value]}.to_h

  # verify required arguments are present
  %w(uid cn mail).each do |required|
    unless attrs.include? required
      raise ArgumentError.new("missing attribute #{required}")
    end
  end

  availid = attrs['uid']

  # determine next uid and group, unless provided
  nextuid = attrs['uidNumber'] || 
    ASF::search_one(ASF::Person.base, 'uid=*', 'uidNumber').
      flatten.map(&:to_i).max + 1

  nextgid = attrs['gidNumber']
  unless nextgid
    nextgid = ASF::search_one(group_base, 'cn=*', 'gidNumber').
      flatten.map(&:to_i).max + 1

    # create new LDAP group
    entry = [
      mod_add('objectClass', ['posixGroup', 'top']),
      mod_add('cn', availid),
      mod_add('userPassword', '{crypt}*'),
      mod_add('gidNumber', nextgid.to_s),
    ]
  end
 
  # fixed attributes
  attrs.merge!({
    'uidNumber' => nextuid.to_s,
    'gidNumber' => nextuid.to_s,
    'asf-committer-email' => "#{availid}@apache.org",
    'objectClass' => %w(person top posixAccount organizationalPerson
       inetOrgPerson asf-committer hostObject ldapPublicKey)
  })

  # defaults
  attrs['loginShell'] ||= '/usr/local/bin/bash'
  attrs['homeDirectory'] ||= "/home/#{availid}"
  attrs['host'] ||= "home.apache.org"
  attrs['asf-sascore'] ||= "10"

  # parse name
  attrs = ASF::Person.ldap_name(attrs['cn']).merge(attrs)

  # generate a password that is between 8 and 16 alphanumeric characters
  if not attrs['userPassword']
    while attrs['userPassword'].to_s.length < 8
      attrs['userPassword'] = SecureRandom.base64(12).gsub(/\W+/, '')
    end
  end

  ASF::LDAP.add("cn=#{availid},#{group_base}", entry)

  # create new LDAP person
  begin
    entry = attrs.map {|key, value| mod_add(key, value)}
    ASF::LDAP.add("uid=#{availid},#{base}", entry)
  rescue
    # don't leave an orphan group behind
    ASF::LDAP.delete("cn=#{availid},#{group_base}") rescue nil
    raise
  end

  # return person object with password filled in
  person = ASF::Person.find(availid)
  person.attrs['userPassword'] = [attrs['userPassword']]
  person
end
asciize(name, nonWord = '-') click to toggle source

Convert non-ASCII characters to equivalent ASCII optionally: replace any remaining non-word characters (e.g. '.' and space) with '-'

# File lib/whimsy/asf/person.rb, line 16
def self.asciize(name, nonWord = '-')
  if name.match %r{[^\x00-\x7F]} # at least one non-ASCII character present
    # digraphs.  May be culturally sensitive
    # Note that the combining accents require matching two characters
    name.gsub! %r{\u00df}, 'ss'
    name.gsub! %r{\u00e4|a\u0308}, 'ae' # 308 = combining diaeresis
    name.gsub! %r{\u00e5|a\u030a}, 'aa' # a with ring above: should this translate as 'a'?
    name.gsub! %r{\u00c5|A\u030a}, 'AA' # A with ring above: should this translate as 'A'?
    name.gsub! %r{\u00e6},         'ae' # small letter ae
    name.gsub! %r{\u00c6},         'AE' # large letter AE
    name.gsub! %r{\u00f1|n\u0303}, 'ny' # 303 = combining tilde
    name.gsub! %r{\u00d1|N\u0303}, 'NY' # 303 = combining tilde
    name.gsub! %r{\u00f6|o\u0308}, 'oe' # 308 = combining diaeresis
    name.gsub! %r{\u00d6|O\u0308}, 'OE' # 308 = combining diaeresis
    name.gsub! %r{\u00de},         'TH' # thorn
    name.gsub! %r{\u00fe},         'th' # thorn
    name.gsub! %r{\u00fc|u\u0308}, 'ue' # 308 = combining diaeresis
    name.gsub! %r{\u00dc|U\u0308}, 'UE' # 308 = combining diaeresis

    # latin 1
    name.gsub! %r{[\u00e0-\u00e3]}, 'a' # a with various accents
    name.gsub! %r{[\u00c0-\u00c3]}, 'A' # A with various accents
    name.gsub! %r{\u00e7},          'c' # c-cedilla
    name.gsub! %r{\u00c7},          'C' # C-cedilla
    name.gsub! %r{\u00f0},          'd' # eth
    name.gsub! %r{\u00d0},          'D' # eth
    name.gsub! %r{[\u00e8-\u00eb]}, 'e'
    name.gsub! %r{[\u00c8-\u00cb]}, 'E'
    name.gsub! %r{[\u00ec-\u00ef]}, 'i'
    name.gsub! %r{[\u00cc-\u00cf]}, 'I'
    name.gsub! %r{[\u00f2-\u00f5\u00f8]}, 'o'
    name.gsub! %r{[\u00d2-\u00d5\u00d8]}, 'O'
    name.gsub! %r{[\u00f9-\u00fb]}, 'u'
    name.gsub! %r{[\u00d9-\u00db]}, 'U'
    name.gsub! %r{[\u00fd\u00ff]},  'y'
    name.gsub! %r{[\u00dd\u0178]},  'Y'

    # Latin Extended-A
    name.gsub! %r{[\u0100\u0102\u0104]}, 'A'
    name.gsub! %r{[\u0101\u0103\u0105]}, 'a'
    name.gsub! %r{[\u0106\u0108\u010A\u010C]}, 'C'
    name.gsub! %r{[\u0107\u0109\u010B\u010D]}, 'c'
    name.gsub! %r{[\u010E\u0110]}, 'D'
    name.gsub! %r{[\u010F\u0111]}, 'd'
    name.gsub! %r{[\u0112\u0114\u0116\u0118\u011A]}, 'E'
    name.gsub! %r{[\u0113\u0115\u0117\u0119\u011B]}, 'e'
    name.gsub! %r{[\u014A]}, 'ENG'
    name.gsub! %r{[\u014B]}, 'eng'
    name.gsub! %r{[\u011C\u011E\u0120\u0122]}, 'G'
    name.gsub! %r{[\u011D\u011F\u0121\u0123]}, 'g'
    name.gsub! %r{[\u0124\u0126]}, 'H'
    name.gsub! %r{[\u0125\u0127]}, 'h'
    name.gsub! %r{[\u0128\u012A\u012C\u012E\u0130]}, 'I'
    name.gsub! %r{[\u0129\u012B\u012D\u012F\u0131]}, 'i'
    name.gsub! %r{[\u0132]}, 'IJ'
    name.gsub! %r{[\u0133]}, 'ij'
    name.gsub! %r{[\u0134]}, 'J'
    name.gsub! %r{[\u0135]}, 'j'
    name.gsub! %r{[\u0136]}, 'K'
    name.gsub! %r{[\u0137]}, 'k'
    name.gsub! %r{[\u0138]}, 'kra'
    name.gsub! %r{[\u0139\u013B\u013D\u013F\u0141]}, 'L'
    name.gsub! %r{[\u013A\u013C\u013E\u0140\u0142]}, 'l'
    name.gsub! %r{[\u0143\u0145\u0147]}, 'N'
    name.gsub! %r{[\u0144\u0146\u0148\u0149]}, 'n'
    name.gsub! %r{[\u014C\u014E\u0150]}, 'O'
    name.gsub! %r{[\u014D\u014F\u0151]}, 'o'
    name.gsub! %r{[\u0152]}, 'OE'
    name.gsub! %r{[\u0153]}, 'oe'
    name.gsub! %r{[\u0154\u0156\u0158]}, 'R'
    name.gsub! %r{[\u0155\u0157\u0159]}, 'r'
    name.gsub! %r{[\u015A\u015C\u015E\u0160]}, 'S'
    name.gsub! %r{[\u015B\u015D\u015F\u0161]}, 's'
    name.gsub! %r{[\u0162\u0164\u0166]}, 'T'
    name.gsub! %r{[\u0163\u0165\u0167]}, 't'
    name.gsub! %r{[\u0168\u016A\u016C\u016E\u0170\u0172]}, 'U'
    name.gsub! %r{[\u0169\u016B\u016D\u016F\u0171\u0173]}, 'u'
    name.gsub! %r{[\u0174]}, 'W'
    name.gsub! %r{[\u0175]}, 'w'
    name.gsub! %r{[\u0176\u0178]}, 'Y'
    name.gsub! %r{[\u0177]}, 'y'
    name.gsub! %r{[\u0179\u017B\u017D]}, 'Z'
    name.gsub! %r{[\u017A\u017C\u017E]}, 'z'

    # Latin Extended Additional
    # N.B. Only ones seen in iclas.txt are included here
    name.gsub! %r{\u1ea0},          'A' # A with combining dot below
    name.gsub! %r{\u1ea1},          'a' # a with combining dot below
    name.gsub! %r{\u1ec4},          'E' # E with circumflex and tilde
    name.gsub! %r{\u1ec5},          'e' # e with circumflex and tilde

    # remove unhandled combining diacritics (some combinations are handled above)
    name.gsub! %r{[\u0300-\u036f]}, ''
  end

  if nonWord
    # deal with any remaining non-word characters
    name.strip.gsub %r{[^\w]+}, nonWord if nonWord
  else
    name
  end
end
find_by_email(value) click to toggle source

find a Person by email address

# File lib/whimsy/asf/mail.rb, line 273
def self.find_by_email(value)
  person = Mail.list[value.downcase]
  return person if person
end
group_base() click to toggle source
# File lib/whimsy/asf/ldap.rb, line 677
def self.group_base
  'ou=people,' + ASF::Group.base
end
ldap_name(name) click to toggle source

parse a name into LDAP fields

# File lib/whimsy/asf/person.rb, line 136
def self.ldap_name(name)
  words = name.gsub(',', '').split(' ')
  result = {'cn' => name}
  result['title'] = words.shift if words.first == 'Dr.' or words.first == 'Dr'
  if words.last =~ /^Ph\.D\.?/
    title = words.pop # Always pop (||= short-circuits the pop)
    result['title'] ||= title
  end 
  result['generationQualifier'] = words.pop if words.last =~ SUFFIXES
  result['givenName'] = words.shift # TODO does gn allow multiple words?
  # extract surnames like van Gogh etc
  if words.size >= 3 and words[-3..-2] == %w(de la) or words[-3..-2] == %w(van der) or words[-3..-2] == %w(van de) or words[-3..-2] == %w(van den) or words[-3..-2] == %w(von der)
    result['sn'] = words[-3..-1].join(' ')
    result['unused'] = words[0..-4]
  elsif words.size >= 2 and %w(von van Van de De del Del den le Le O Di Du dos St.).include?  words[-2]
    result['sn'] = words[-2..-1].join(' ')
    result['unused'] = words[0..-3]
  else
    result['sn'] = words.pop
    result['unused'] = words
  end 
  result
end
list(filter='uid=*') click to toggle source

Obtain a list of people known to LDAP. LDAP filters may be used to retrieve only a subset.

# File lib/whimsy/asf/ldap.rb, line 683
def self.list(filter='uid=*')
  ASF.search_one(base, filter, 'uid').flatten.map {|uid| find(uid)}
end
listids(filter='uid=*') click to toggle source

Obtain a list of people (ids) known to LDAP. LDAP filters may be used to retrieve only a subset. Result is returned as a list of ids only.

# File lib/whimsy/asf/ldap.rb, line 689
def self.listids(filter='uid=*')
  ASF.search_one(base, filter, 'uid').flatten
end
member_nominees() click to toggle source

Return a hash of nominated members. Keys are ASF::Person objects, values are the nomination text.

# File lib/whimsy/asf/nominees.rb, line 9
def self.member_nominees
  begin
    return Hash[@member_nominees.to_a] if @member_nominees
  rescue
  end

  meetings = ASF::SVN['Meetings']
  nominations = Dir[File.join(meetings, '*', 'nominated-members.txt')].sort.last.untaint

  nominations = File.read(nominations).split(/^\s*---+--\s*/)
  nominations.shift(2)

  nominees = {}
  nominations.each do |nomination|
    id = nomination[/^\s?\w+.*<(\S+)@apache.org>/,1]
    id ||= nomination[/^\s?\w+.*\((\S+)@apache.org\)/,1]
    id ||= nomination[/^\s?\w+.*\(([a-z]+)\)/,1]

    next unless id

    nominees[find(id)] = nomination
  end

  @member_nominees = WeakRef.new(nominees)
  nominees
end
member_watch_list() click to toggle source

Return a hash of individuals in the member watch list. Keys are ASF::Person objects, values are the text from potential-member-watch-list.txt..

# File lib/whimsy/asf/watch.rb, line 8
def self.member_watch_list
  return @member_watch_list if @member_watch_list

  foundation = ASF::SVN['foundation']
  text = File.read File.join(foundation, 'potential-member-watch-list.txt')

  nominations = text.scan(/^\s+\*\)\s+\w.*?\n\s*(?:---|\Z)/m)

  i = 0
  member_watch_list = {}
  nominations.each do |nomination|
    id = nil
    name = nomination[/\*\)\s+(.+?)\s+(\(|\<|$)/,1]
    id ||= nomination[/\*\)\s.+?\s\((.*?)\)/,1]
    id ||= nomination[/\*\)\s.+?\s<(.*?)@apache.org>/,1]

    unless id
      id = "notinavail_#{i+=1}"
      find(id).attrs['cn'] = name
    end

    member_watch_list[find(id)] = nomination
  end

  @member_watch_list = member_watch_list
end
preload(attributes, people=[]) click to toggle source

pre-fetch a given set of attributes, for a given list of people

# File lib/whimsy/asf/ldap.rb, line 694
def self.preload(attributes, people=[])
  list = Hash.new {|hash, name| hash[name] = find(name)}

  attributes = [attributes].flatten

  if people.empty? or people.length > 1000
    filter = "(|#{attributes.map {|attribute| "(#{attribute}=*)"}.join})"
  else
    filter = "(|#{people.map {|person| "(uid=#{person.name})"}.join})"
  end
  
  zero = Hash[attributes.map {|attribute| [attribute,nil]}]

  data = ASF.search_one(base, filter, attributes + ['uid'])
  data = Hash[data.map! {|hash| [list[hash['uid'].first], hash]}]
  data.each {|person, hash| person.attrs.merge!(zero.merge(hash))}

  if people.empty?
    (list.values - data.keys).each do |person|
      person.attrs.merge! zero
    end
  end

  list.values
end
remove(availid) click to toggle source

remove a person from LDAP

# File lib/whimsy/asf/ldap.rb, line 967
def self.remove(availid)
  ASF::LDAP.delete("cn=#{availid},#{group_base}")
  ASF::LDAP.delete("uid=#{availid},#{base}")
end
sortable_name(name) click to toggle source

rearrange line in an order suitable for sorting

# File lib/whimsy/asf/person.rb, line 123
def self.sortable_name(name)
  name = name.split.reverse
  suffix = (name.shift if name.first =~ SUFFIXES)
  suffix += ' ' + name.shift if name.first =~ SUFFIXES
  name << name.shift
  # name << name.shift if name.first=='van'
  name.last.sub! %r{^IJ}, 'Ij'
  name.unshift(suffix) if suffix
  name.map! {|word| asciize(word)}
  name.reverse.join(' ').downcase
end
stem_DRAFT(name) click to toggle source

DRAFT return name suitable for a filename stem Should normally be applied to the legal name

# File lib/whimsy/asf/person.rb, line 163
def self.stem_DRAFT(name)
  # need to split before
  name = name.gsub(',', ' ').split(/ +/).map{|n|n.gsub(%r{^(Dr|Jr|Sr|[A-Z])\.$},'\1')}
  asciize(name.join('-')).downcase.chomp('-')
end

Public Instance Methods

active_emails() click to toggle source

Active emails: primary email address, alt email addresses, and member email addresses.

# File lib/whimsy/asf/mail.rb, line 294
def active_emails
  (mail + alt_email + member_emails).uniq
end
all_mail() click to toggle source

All known email addresses: includes active, obsolete, and apache.org email addresses. (But don't add notinavail@apache.org)

# File lib/whimsy/asf/mail.rb, line 300
def all_mail
  (active_emails + obsolete_emails + (id == 'notinavail' ? [] : ["#{id}@apache.org"])).uniq
end
alt_email() click to toggle source

list all of the alternative emails for this person

# File lib/whimsy/asf/ldap.rb, line 775
def alt_email
  attrs['asf-altEmail'] || []
end
asf_banned?() click to toggle source

determine if the person has asf-banned: yes. If scanning a large list, consider preloading the asf-banned attributes for these people.

# File lib/whimsy/asf/ldap.rb, line 759
def asf_banned?
  # No idea what this means (yet)
  attrs['asf-banned'] == 'yes'
end
asf_committer?() click to toggle source

Is this person listed in the committers LDAP group?

# File lib/whimsy/asf/ldap.rb, line 739
def asf_committer?
   ASF::Group.new('committers').include? self
end
asf_member?() click to toggle source

Returns true if this person is listed as an ASF member in either LDAP or members.txt. Note: LDAP includes infrastructure staff members that may not be ASF Members.

# File lib/whimsy/asf/person.rb, line 203
def asf_member?
  ASF::Member.status[name] or ASF.members.include? self
end
asf_officer_or_member?() click to toggle source

Returns true if this person is listed as an ASF member in either LDAP or members.txt or this person is listed as an PMC chair in LDAP.

# File lib/whimsy/asf/person.rb, line 210
def asf_officer_or_member?
  asf_member? or ASF.pmc_chairs.include? self
end
attrs() click to toggle source

list of LDAP attributes for this person, populated lazily upon first reference.

# File lib/whimsy/asf/ldap.rb, line 728
def attrs
  @attrs ||= LazyHash.new {ASF.search_one(base, "uid=#{name}").first}
end
auth() click to toggle source

return a list of ASF authorizations that contain this individual

# File lib/whimsy/asf/auth.rb, line 80
def auth
  @auths ||= ASF::Authorization.find_by_id(name)
end
banned?() click to toggle source

determine if the person is banned. If scanning a large list, consider preloading the loginShell attributes for these people.

# File lib/whimsy/asf/ldap.rb, line 745
def banned?
  # FreeBSD uses /usr/bin/false; Ubuntu uses /bin/false
  not attrs['loginShell'] or %w(/bin/false bin/nologin bin/no-cla).any? {|a| attrs['loginShell'].first.include? a}
end
committees() click to toggle source

list of LDAP committees that this individual is a member of TODO should this be deleted? It seems to be used partly as LDAP membership and partly as PMC membership (which were originally generally the same) If the former, then it disappears. If the latter, then it needs to be derived from project_owners filtered to keep only PMCs

# File lib/whimsy/asf/ldap.rb, line 799
    def committees
      # legacy LDAP entries
      committees = []
#      committees = weakref(:committees) do
#        Committee.list("member=uid=#{name},#{base}")
#      end

      # add in projects
      # Get list of project names where the person is an owner
      projects = self.projects.select{|prj| prj.owners.include? self}.map(&:name)
      committees += ASF::Committee.pmcs.select do |pmc| 
        projects.include? pmc.name
      end

      # dedup
      committees.uniq
    end
createDate() click to toggle source
# File lib/whimsy/asf/person.rb, line 186
def createDate
  createTimestamp[0..7]
end
createTimestamp() click to toggle source

determine account creation date. Notes:

  • LDAP info is not accurate for dates prior to 2009. See person/override-dates.rb

  • createTimestamp isn't loaded by default (but can either be preloaded or fetched explicitly)

# File lib/whimsy/asf/person.rb, line 179
def createTimestamp
  result = @@create_date[name] 
  result ||= attrs['createTimestamp'][0] rescue nil # in case not loaded
  result ||= ASF.search_one(base, "uid=#{name}", 'createTimestamp')[0][0]
  result
end
dn() click to toggle source

Designated Name from LDAP

# File lib/whimsy/asf/ldap.rb, line 851
def dn
  "uid=#{name},#{ASF::Person.base}"
end
groups() click to toggle source

list of LDAP groups that this individual is a member of

# File lib/whimsy/asf/ldap.rb, line 837
def groups
  weakref(:groups) do
    Group.list("memberUid=#{name}")
  end
end
icla() click to toggle source

ASF::ICLA information for this person.

# File lib/whimsy/asf/icla.rb, line 246
def icla
  @icla ||= ASF::ICLA.find_by_id(name)
end
icla=(icla) click to toggle source

setter for icla, should only be used by ASF::ICLA.preload

# File lib/whimsy/asf/icla.rb, line 251
def icla=(icla)
  @icla = icla
end
icla?() click to toggle source

does this individual have an ICLA on file?

# File lib/whimsy/asf/icla.rb, line 256
def icla?
  @icla || ICLA.availids.include?(name)
end
inactive?() click to toggle source

is the login marked as inactive?

# File lib/whimsy/asf/ldap.rb, line 765
def inactive?
  nologin? || asf_banned?
end
mail() click to toggle source

primary mail addresses

# File lib/whimsy/asf/ldap.rb, line 770
def mail
  attrs['mail'] || []
end
member_emails() click to toggle source

email addresses from members.txt

# File lib/whimsy/asf/member.rb, line 198
def member_emails
  ASF::Member.emails(members_txt)
end
member_name() click to toggle source

Person's name as found in members.txt

# File lib/whimsy/asf/member.rb, line 203
def member_name
  ASF::Member.get_name(members_txt) if members_txt
end
member_nomination() click to toggle source

Return the member nomination text for this individual

# File lib/whimsy/asf/nominees.rb, line 37
def member_nomination
  @member_nomination ||= Person.member_nominees[self]
end
member_watch() click to toggle source

This person's entry in potential-member-watch-list.txt.

# File lib/whimsy/asf/watch.rb, line 36
def member_watch
  text = Person.member_watch_list[self]
  if text
    text.sub!(/\A\s*\n/,'')
    text.sub!(/\n---\Z/,'')
  end
  text
end
members_txt(full = false) click to toggle source

text entry from members.txt. If full is true, this will also include the text delimiters.

# File lib/whimsy/asf/member.rb, line 185
def members_txt(full = false)
  prefix, suffix = " *) ", "\n\n" if full
  # Is the cached text still valid?
  unless @members_time == ASF::Member.mtime
    @members_txt = nil
  end
  # cache the text and its time (may be changed by the find operation)
  @members_txt ||= ASF::Member.find_text_by_id(id)
  @members_time = ASF::Member.mtime
  "#{prefix}#{@members_txt}#{suffix}" if @members_txt
end
method_missing(name, *args) click to toggle source

Allow arbitrary LDAP attibutes to be referenced as object properties. Example: ASF::Person.find('rubys').cn. Can also be used to modify an LDAP attribute.

Calls superclass method
# File lib/whimsy/asf/ldap.rb, line 858
def method_missing(name, *args)
  if name.to_s.end_with? '=' and args.length == 1
    return modify(name.to_s[0..-2], args)
  end

  return super unless args.empty?
  result = self.attrs[name.to_s]
  return super unless result

  if result.empty?
    return nil
  else
    result.map! do |value|
      value = value.dup.force_encoding('utf-8') if String === value
      value
    end

    if result.length == 1
      result.first
    else
      result
    end
  end
end
modify(attr, value) click to toggle source

update an LDAP attribute for this person. This needs to be run either inside or after ASF::LDAP.bind.

# File lib/whimsy/asf/ldap.rb, line 885
def modify(attr, value)
  ASF::LDAP.modify(self.dn, [ASF::Base.mod_replace(attr.to_s, value)])
  attrs[attr.to_s] = value
end
nologin?() click to toggle source

determine if the person has no login. If scanning a large list, consider preloading the loginShell attributes for these people.

# File lib/whimsy/asf/ldap.rb, line 752
def nologin?
  # FreeBSD uses /usr/bin/false; Ubuntu uses /bin/false
  not attrs['loginShell'] or %w(/bin/false bin/nologin bin/no-cla).any? {|a| attrs['loginShell'].first.include? a}
end
obsolete_emails() click to toggle source

List of inactive email addresses: currently only contains the address in iclas.txt if it is not contained in the list of active email addresses.

# File lib/whimsy/asf/mail.rb, line 281
def obsolete_emails
  return @obsolete_emails if @obsolete_emails
  result = []
  if icla
    unless active_emails.any? {|mail| mail.downcase == icla.email.downcase}
      result << icla.email
    end
  end
  @obsolete_emails = result
end
pgp_key_fingerprints() click to toggle source

list all of the PGP key fingerprints

# File lib/whimsy/asf/ldap.rb, line 780
def pgp_key_fingerprints
  attrs['asf-pgpKeyFingerprint'] || []
end
podlings() click to toggle source

list of Podlings that this individual is a member (owner) of

# File lib/whimsy/asf/ldap.rb, line 832
def podlings
  ASF::Podling.current.select{|pod| project_owners.map(&:name).include? pod.name}
end
project_owners() click to toggle source

list of LDAP projects that this individual is an owner of - i.e. on (P)PMC

# File lib/whimsy/asf/ldap.rb, line 825
def project_owners
  weakref(:project_owners) do
    Project.list("owner=uid=#{name},#{base}")
  end
end
projects() click to toggle source

list of LDAP projects that this individual is a member of

# File lib/whimsy/asf/ldap.rb, line 818
def projects
  weakref(:projects) do
    Project.list("member=uid=#{name},#{base}")
  end
end
public_name() click to toggle source

return person's public name, searching a variety of sources, starting with iclas.txt, then LDAP, and finally the archives.

# File lib/whimsy/asf/person.rb, line 192
def public_name
  return icla.name if icla
  cn = [attrs['cn']].flatten.first
  cn.force_encoding('utf-8') if cn.respond_to? :force_encoding
  return cn if cn
  ASF.search_archive_by_id(name)
end
reload!() click to toggle source

reload all attributes from LDAP

# File lib/whimsy/asf/ldap.rb, line 733
def reload!
  @attrs = nil
  attrs
end
services() click to toggle source

list of LDAP services that this individual is a member of

# File lib/whimsy/asf/ldap.rb, line 844
def services
  weakref(:services) do
    Service.listcns("member=#{dn}")
  end
end
sortable_name() click to toggle source

return public name in a sortable order (last name first)

# File lib/whimsy/asf/person.rb, line 170
def sortable_name
  Person.sortable_name(self.public_name)
end
ssh_public_keys() click to toggle source

list all of the ssh public keys

# File lib/whimsy/asf/ldap.rb, line 785
def ssh_public_keys
  attrs['sshPublicKey'] || []
end
urls() click to toggle source

list all of the personal URLs

# File lib/whimsy/asf/ldap.rb, line 790
def urls
  attrs['asf-personalURL'] || []
end