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