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