"Fossies" - the Fresh Open Source Software Archive

Member "buildbot-2.5.1/buildbot/www/ldapuserinfo.py" (24 Nov 2019, 6436 Bytes) of package /linux/misc/buildbot-2.5.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "ldapuserinfo.py" see the Fossies "Dox" file reference documentation.

    1 # This file is part of Buildbot.  Buildbot is free software: you can
    2 # redistribute it and/or modify it under the terms of the GNU General Public
    3 # License as published by the Free Software Foundation, version 2.
    4 #
    5 # This program is distributed in the hope that it will be useful, but WITHOUT
    6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    7 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    8 # details.
    9 #
   10 # You should have received a copy of the GNU General Public License along with
   11 # this program; if not, write to the Free Software Foundation, Inc., 51
   12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   13 #
   14 # Copyright Buildbot Team Members
   15 
   16 
   17 # NOTE regarding LDAP encodings:
   18 #
   19 # By default the encoding used in ldap3 is utf-8. The encoding is user-configurable, though.
   20 # For more information check ldap3's documentation on this topic:
   21 # http://ldap3.readthedocs.io/encoding.html
   22 #
   23 # It is recommended to use ldap3's auto-decoded `attributes` values for
   24 # `unicode` and `raw_*` attributes for `bytes`.
   25 
   26 
   27 from urllib.parse import urlparse
   28 
   29 import ldap3
   30 
   31 from twisted.internet import threads
   32 
   33 from buildbot.util import bytes2unicode
   34 from buildbot.util import flatten
   35 from buildbot.www import auth
   36 from buildbot.www import avatar
   37 
   38 
   39 class LdapUserInfo(avatar.AvatarBase, auth.UserInfoProviderBase):
   40     name = 'ldap'
   41 
   42     def __init__(self, uri, bindUser, bindPw,
   43                  accountBase,
   44                  accountPattern,
   45                  accountFullName,
   46                  accountEmail,
   47                  groupBase=None,
   48                  groupMemberPattern=None,
   49                  groupName=None,
   50                  avatarPattern=None,
   51                  avatarData=None,
   52                  accountExtraFields=None):
   53         self.uri = uri
   54         self.bindUser = bindUser
   55         self.bindPw = bindPw
   56         self.accountBase = accountBase
   57         self.accountEmail = accountEmail
   58         self.accountPattern = accountPattern
   59         self.accountFullName = accountFullName
   60         group_params = [p for p in (groupName, groupMemberPattern, groupBase)
   61                         if p is not None]
   62         if len(group_params) not in (0, 3):
   63             raise ValueError(
   64                 "Incomplete LDAP groups configuration. "
   65                 "To use Ldap groups, you need to specify the three "
   66                 "parameters (groupName, groupMemberPattern and groupBase). ")
   67 
   68         self.groupName = groupName
   69         self.groupMemberPattern = groupMemberPattern
   70         self.groupBase = groupBase
   71         self.avatarPattern = avatarPattern
   72         self.avatarData = avatarData
   73         if accountExtraFields is None:
   74             accountExtraFields = []
   75         self.accountExtraFields = accountExtraFields
   76         self.ldap_encoding = ldap3.get_config_parameter('DEFAULT_SERVER_ENCODING')
   77 
   78     def connectLdap(self):
   79         server = urlparse(self.uri)
   80         netloc = server.netloc.split(":")
   81         # define the server and the connection
   82         s = ldap3.Server(netloc[0], port=int(netloc[1]), use_ssl=server.scheme == 'ldaps',
   83                          get_info=ldap3.ALL)
   84 
   85         auth = ldap3.SIMPLE
   86         if self.bindUser is None and self.bindPw is None:
   87             auth = ldap3.ANONYMOUS
   88 
   89         c = ldap3.Connection(s, auto_bind=True, client_strategy=ldap3.SYNC,
   90                              user=self.bindUser, password=self.bindPw,
   91                              authentication=auth)
   92         return c
   93 
   94     def search(self, c, base, filterstr='f', attributes=None):
   95         c.search(base, filterstr, ldap3.SUBTREE, attributes=attributes)
   96         return c.response
   97 
   98     def getUserInfo(self, username):
   99         username = bytes2unicode(username)
  100 
  101         def thd():
  102             c = self.connectLdap()
  103             infos = {'username': username}
  104             pattern = self.accountPattern % dict(username=username)
  105             res = self.search(c, self.accountBase, pattern,
  106                               attributes=[
  107                                   self.accountEmail, self.accountFullName] +
  108                               self.accountExtraFields)
  109             if len(res) != 1:
  110                 raise KeyError(
  111                     "ldap search \"%s\" returned %d results" % (pattern, len(res)))
  112             dn, ldap_infos = res[0]['dn'], res[0]['attributes']
  113 
  114             def getFirstLdapInfo(x):
  115                 if isinstance(x, list):
  116                     x = x[0] if x else None
  117                 return x
  118 
  119             infos['full_name'] = getFirstLdapInfo(ldap_infos[self.accountFullName])
  120             infos['email'] = getFirstLdapInfo(ldap_infos[self.accountEmail])
  121             for f in self.accountExtraFields:
  122                 if f in ldap_infos:
  123                     infos[f] = getFirstLdapInfo(ldap_infos[f])
  124 
  125             if self.groupMemberPattern is None:
  126                 infos['groups'] = []
  127                 return infos
  128 
  129             # needs double quoting of backslashing
  130             pattern = self.groupMemberPattern % dict(dn=dn)
  131             res = self.search(c, self.groupBase, pattern,
  132                               attributes=[self.groupName])
  133             infos['groups'] = flatten([group_infos['attributes'][self.groupName]
  134                                       for group_infos in res])
  135 
  136             return infos
  137         return threads.deferToThread(thd)
  138 
  139     def findAvatarMime(self, data):
  140         # http://en.wikipedia.org/wiki/List_of_file_signatures
  141         if data.startswith(b"\xff\xd8\xff"):
  142             return ("image/jpeg", data)
  143         if data.startswith(b"\x89PNG"):
  144             return ("image/png", data)
  145         if data.startswith(b"GIF8"):
  146             return ("image/gif", data)
  147         # ignore unknown image format
  148         return None
  149 
  150     def getUserAvatar(self, user_email, size, defaultAvatarUrl):
  151         user_email = bytes2unicode(user_email)
  152 
  153         def thd():
  154             c = self.connectLdap()
  155             pattern = self.avatarPattern % dict(email=user_email)
  156             res = self.search(c, self.accountBase, pattern,
  157                               attributes=[self.avatarData])
  158             if not res:
  159                 return None
  160             ldap_infos = res[0]['raw_attributes']
  161             if self.avatarData in ldap_infos and ldap_infos[self.avatarData]:
  162                 data = ldap_infos[self.avatarData][0]
  163                 return self.findAvatarMime(data)
  164             return None
  165         return threads.deferToThread(thd)