remove unused GKEY code
[gentoo-keys.git] / gkeyldap / actions.py
1 #
2 #-*- coding:utf-8 -*-
3
4 """
5     Gentoo-keys - gkeyldap/actions.py
6
7     Primary api interface module
8
9     @copyright: 2012 by Brian Dolbec <dol-sen@gentoo.org>
10     @license: GNU GPL2, see COPYING for details.
11 """
12
13 import os
14 import re
15
16 from collections import defaultdict
17 from gkeys.seed import Seeds
18 from gkeyldap.search import (LdapSearch, UID, gkey2ldap_map, gkey2SEARCH)
19
20
21 Available_Actions = ['ldapsearch', 'updateseeds']
22
23
24 def get_key_ids(key_len, keyids):
25     '''Small utility function to return only keyid (short)
26     or longkeyid's
27
28     @param key_len: string, the key length desired
29     @param keyids: list of keysid's to process
30     @return list of the desired key length id's
31     '''
32     result = []
33     for keyid in keyids:
34         target_len = 16
35         if keyid.startswith('0x'):
36             target_len = target_len + 2
37         if len(keyid) == target_len:
38             result.append(keyid)
39     return result
40
41
42 class Actions(object):
43
44
45     def __init__(self, config, output=None, logger=None):
46         self.config = config
47         self.output = output
48         self.logger = logger
49         self.seeds = None
50         self.fingerprint_re = re.compile('[0-9A-Fa-f]{40}')
51
52
53     def ldapsearch(self, args):
54         l = LdapSearch()
55         self.logger.info("Search... Establishing connection")
56         self.output("Search... Establishing connection")
57         if not l.connect():
58             self.logger.info("Aborting search... Connection failed")
59             self.output("Aborting search... Connection failed")
60             return False
61         self.logger.debug("MAIN: _action_ldapsearch; args = %s" % str(args))
62         x, target, search_field = self.get_args(args)
63         results = l.search(target, search_field)
64         devs = l.result2dict(results, gkey2ldap_map[x])
65         for dev in sorted(devs):
66             self.output(dev, devs[dev])
67         self.output("============================================")
68         self.output("Total number of developers in results:", len(devs))
69         self.logger.info("============================================")
70         self.logger.info("Total number of developers in results: %d" % len(devs))
71         return True
72
73
74     def updateseeds(self, args):
75         self.logger.info("Beginning LDAP search...")
76         self.output("Beginning LDAP search...")
77         l = LdapSearch()
78         if not l.connect():
79             self.output("Aborting update... Connection failed")
80             self.logger.info("Aborting update... Connection failed")
81             return False
82         results = l.search('*', UID)
83         info = l.result2dict(results, 'uid')
84         self.logger.debug(
85             "MAIN: _action_updateseeds; got results :) converted to info")
86         if not self.create_seedfile(info):
87             self.logger.error("Developer seed file update failure: "
88                 "Original seed file is intact & untouched.")
89         filename = self.config['dev-seedfile']
90         old = filename + '.old'
91         try:
92             self.output("Backing up existing file...")
93             self.logger.info("Backing up existing file...")
94             if os.path.exists(old):
95                 self.logger.debug(
96                     "MAIN: _action_updateseeds; Removing 'old' seed file: %s"
97                     % old)
98                 os.unlink(old)
99             if os.path.exists(filename):
100                 self.logger.debug(
101                     "MAIN: _action_updateseeds; Renaming current seed file to: "
102                     "%s" % old)
103                 os.rename(filename, old)
104             self.logger.debug(
105                 "MAIN: _action_updateseeds; Renaming '.new' seed file to: %s"
106                 % filename)
107             os.rename(filename + '.new', filename)
108         except IOError:
109             raise
110         self.output("Developer seed file updated!")
111         return True
112
113
114     def create_seedfile(self, devs):
115         self.output("Creating seeds from LDAP data...")
116         filename = self.config['dev-seedfile'] + '.new'
117         self.seeds = Seeds(filename)
118         count = 0
119         error_count = 0
120         for dev in sorted(devs):
121             if devs[dev]['gentooStatus'][0] not in ['active']:
122                 continue
123             #self.logger.debug("create_seedfile, dev = "
124             #   "%s, %s" % (str(dev), str(devs[dev])))
125             developer_attrs = self.build_gkeydict(devs[dev])
126             if developer_attrs:
127                 self.seeds.add(dev, developer_attrs)
128                 count += 1
129             else:
130                 error_count += 1
131         self.output("Total number of seeds created:", count)
132         self.output("Seeds created... Saving file: %s" % filename)
133         self.output("Total number of Dev's with GPG errors:", error_count)
134         self.logger.info("Total number of seeds created: %d" % count)
135         self.logger.info("Seeds created... Saving file: %s" % filename)
136         self.logger.info("Total number of Dev's with GPG errors: %d" % error_count)
137         return self.seeds.save()
138
139
140     @staticmethod
141     def get_args(args):
142         for attr in ['nick', 'name', 'gpgkey', 'fingerprint', 'status']:
143             if attr:
144                 target = getattr(args, attr)
145                 search_field = gkey2SEARCH[attr]
146                 break
147         return (attr, target, search_field)
148
149
150     def build_gkeydict(self, info):
151         keyinfo = defaultdict()
152         keyid_found = False
153         keyid_missing = False
154         # assume it's good until an error is found
155         is_good = True
156         #self.logger.debug("Actions: build_gkeylist; info = %s" % str(info))
157         for attr in gkey2ldap_map:
158             field = gkey2ldap_map[attr]
159             if not field:
160                 keyinfo[attr] = None
161                 continue
162             try:
163                 values = info[field]
164                 # strip errant line feeds
165                 values = [y.strip('\n') for y in values]
166                 # separate out short/long key id's
167                 if values and attr in ['keyid', 'longkeyid']:
168                     if len(get_key_ids(attr, values)):
169                         keyid_found = True
170                 elif values and attr in ['fingerprint']:
171                     values = [v.replace(' ', '') for v in values]
172                 if 'undefined' in values:
173                     self.logger.error('ERROR in LDAP info for: %s, %s'
174                         % (info['uid'][0],info['cn'][0]))
175                     self.logger.error('  %s = "undefined"' % (field))
176                     is_good = False
177                 keyinfo[attr] = values
178             except KeyError:
179                 self.logger.debug('LDAP info for: %s, %s'
180                     % (info['uid'][0],info['cn'][0]))
181                 self.logger.debug('  MISSING or EMPTY LDAP field ' +
182                     '[%s] GPGKey field [%s]' % (field, attr))
183                 if attr in ['keyid', 'longkeyid']:
184                     keyid_missing = True
185                 else:
186                     is_good = False
187                 keyinfo[attr] = None
188         if not keyid_found and keyid_missing:
189             fingerprint = None
190             try:
191                 fingerprint = info[gkey2ldap_map['fingerprint']]
192                 self.logger.debug('  Generate gpgkey, Found LDAP fingerprint field')
193             except KeyError:
194                 gpgkey = 'Missing fingerprint from LDAP info'
195                 self.logger.debug('  Generate gpgkey, LDAP fingerprint KeyError')
196             if fingerprint:
197                 values = [y.strip('\n') for y in fingerprint]
198                 values = [v.replace(' ', '') for v in values]
199                 # assign it to gpgkey to prevent a possible
200                 # "gpgkey" undefined error
201                 gpgkey = ['0x' + x[-16:] for x in values]
202                 keyinfo['longkeyid'] = gpgkey
203                 self.logger.debug('  Generate gpgkey, NEW keyinfo[\'fingerprint\'] = %s'
204                     % str(keyinfo['longkeyid']))
205             else:
206                 gpgkey = 'Missing or Bad fingerprint from LDAP info'
207                 is_good = False
208             if not keyinfo['longkeyid']:
209                 self.logger.error('ERROR in ldap info for: %s, %s'
210                     %(info['uid'][0],info['cn'][0]))
211                 self.logger.error('  A valid keyid, longkeyid or fingerprint '
212                     'was not found for %s : gpgkey = %s' %(info['cn'][0], gpgkey))
213                 is_good = False
214         if is_good:
215             if keyinfo['fingerprint']: # fingerprints exist check
216                 is_ok = self._check_fingerprint_integrity(info, keyinfo)
217                 is_match = self._check_id_fingerprint_match(info, keyinfo)
218                 if not is_ok or not is_match:
219                     is_good = False
220         if is_good:
221             return keyinfo
222         return None
223
224
225     def _check_id_fingerprint_match(self, info, keyinfo):
226         # assume it's good until found an error is found
227         is_good = True
228         for attr in ['keyid', 'longkeyid']:
229             # skip blank id field
230             if not keyinfo[attr]:
231                 continue
232             for y in keyinfo[attr]:
233                 index = len(y.lstrip('0x'))
234                 if y.lstrip('0x').upper() not in \
235                         [x[-index:].upper() for x in keyinfo['fingerprint']]:
236                     self.logger.error('ERROR in LDAP info for: %s, %s'
237                         %(info['uid'][0],info['cn'][0]))
238                     self.logger.error('  ' + str(keyinfo))
239                     self.logger.error('  GPGKey id %s not found in the '
240                         % y.lstrip('0x') + 'listed fingerprint(s)')
241                     is_good = False
242         return is_good
243
244
245     def _check_fingerprint_integrity(self, info, keyinfo):
246         # assume it's good until found an error is found
247         is_good = True
248         for fingerprint in keyinfo['fingerprint']:
249             # check fingerprint integrity
250             if len(fingerprint) != 40:
251                 self.logger.error('ERROR in LDAP info for: %s, %s'
252                     %(info['uid'][0],info['cn'][0]))
253                 self.logger.error('  GPGKey incorrect fingerprint ' +
254                     'length (%s) for fingerprint: %s' %(len(fingerprint), fingerprint))
255                 is_good = False
256                 continue
257             if not self.fingerprint_re.match(fingerprint):
258                 self.logger.error('ERROR in LDAP info for: %s, %s'
259                     % (info['uid'][0],info['cn'][0]))
260                 self.logger.error('  GPGKey: Non hexadecimal digits in ' +
261                     'fingerprint for fingerprint: ' + fingerprint)
262                 is_good = False
263         return is_good