Don't add a dev's seed if there were errors
[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
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         return True
78
79
80     def updateseeds(self, args):
81         self.logger.info("Beginning ldap search...")
82         self.output("Beginning ldap search...")
83         l = LdapSearch()
84         if not l.connect():
85             self.output("Aborting Update...Connection failed")
86             return False
87         results = l.search('*', UID)
88         info = l.result2dict(results, 'uid')
89         self.logger.debug(
90             "MAIN: _action_updateseeds; got results :) converted to info")
91         if not self.create_seedfile(info):
92             self.logger.error("Dev seed file update failure: "
93                 "Original seed file is intact & untouched.")
94         old = self.config['dev-seedfile'] + '.old'
95         try:
96             self.output("Backing up existing file...")
97             if os.path.exists(old):
98                 self.logger.debug(
99                     "MAIN: _action_updateseeds; Removing 'old' seed file: %s"
100                     % old)
101                 os.unlink(old)
102             if os.path.exists(self.config['dev-seedfile']):
103                 self.logger.debug(
104                     "MAIN: _action_updateseeds; Renaming current seed file to: "
105                     "%s" % old)
106                 os.rename(self.config['dev-seedfile'], old)
107             self.logger.debug(
108                 "MAIN: _action_updateseeds; Renaming '.new' seed file to: %s"
109                 % self.config['dev-seedfile'])
110             os.rename(self.config['dev-seedfile'] + '.new',
111                 self.config['dev-seedfile'])
112         except IOError:
113             raise
114         self.output("Developer Seed file updated")
115         return True
116
117
118     def create_seedfile(self, devs):
119         self.output("Creating seeds from ldap data...")
120         filename = self.config['dev-seedfile'] + '.new'
121         self.seeds = Seeds(filename)
122         count = 0
123         for dev in sorted(devs):
124             if devs[dev]['gentooStatus'][0] not in ['active']:
125                 continue
126             #self.logger.debug("create_seedfile, dev = "
127             #   "%s, %s" % (str(dev), str(devs[dev])))
128             keyinfo = self.build_gkeylist(devs[dev])
129             if keyinfo:
130                 new_gkey = GKEY._make(keyinfo)
131                 self.seeds.add(new_gkey)
132                 count += 1
133         self.output("Total number of seeds created:", count)
134         self.output("Seeds created...saving file: %s" % filename)
135         return self.seeds.save()
136
137
138     @staticmethod
139     def get_args(args):
140         for x in ['nick', 'name', 'gpgkey', 'fingerprint', 'status']:
141             if x:
142                 target = getattr(args, x)
143                 search_field = gkey2SEARCH[x]
144                 break
145         return (x, target, search_field)
146
147
148
149     def build_gkeydict(self, info):
150         keyinfo = {}
151         for x in GKEY._fields:
152             field = gkey2ldap_map[x]
153             if not field:
154                 continue
155             try:
156                 # strip errant line feeds
157                 values = [y.strip('\n') for y in info[field]]
158                 if values and values in ['uid', 'cn' ]:
159                     value = values[0]
160                 # separate out short/long key id's
161                 elif values and x in ['keyid', 'longkeyid']:
162                     value = get_key_ids(x, values)
163                 else:
164                     value = values
165                 if 'undefined' in values:
166                     self.logger.error('%s = "undefined" for %s, %s'
167                         %(field, info['uid'][0], info['cn'][0]))
168                 if value:
169                     keyinfo[x] = value
170             except KeyError:
171                 pass
172         return keyinfo
173
174
175     def build_gkeylist(self, info):
176         keyinfo = []
177         keyid_found = False
178         keyid_missing = False
179         # assume it's good until found an error is found
180         is_good = True
181         #self.logger.debug("MAIN: build_gkeylist; info = %s" % str(info))
182         for x in GKEY._fields:
183             field = gkey2ldap_map[x]
184             if not field:
185                 keyinfo.append(None)
186                 continue
187             try:
188                 # strip errant line feeds
189                 values = [y.strip('\n') for y in info[field]]
190                 if values and field in ['uid', 'cn' ]:
191                     value = values[0]
192                 # separate out short/long key id's
193                 elif values and x in ['keyid', 'longkeyid']:
194                     value = get_key_ids(x, values)
195                     if len(value):
196                         keyid_found = True
197                 elif values and x in ['fingerprint']:
198                     value = [v.replace(' ', '') for v in values]
199                 else:
200                     value = values
201                 if 'undefined' in values:
202                     self.logger.error('ERROR in ldap info for: %s, %s'
203                         %(info['uid'][0],info['cn'][0]))
204                     self.logger.error('  %s = "undefined"' %(field))
205                     is_good = False
206                 keyinfo.append(value)
207             except KeyError:
208                 self.logger.error('ERROR in ldap info for: %s, %s'
209                     %(info['uid'][0],info['cn'][0]))
210                 self.logger.error('  MISSING or EMPTY ldap field ' +
211                     '[%s] GPGKey field [%s]' %(field, x))
212                 if x in ['keyid', 'longkeyid']:
213                     keyid_missing = True
214                 keyinfo.append(None)
215                 is_good = False
216         if not keyid_found and not keyid_missing:
217             try:
218                 gpgkey = info[gkey2ldap_map['longkeyid']]
219             except KeyError:
220                 gpgkey = 'Missing from ldap info'
221             self.logger.error('ERROR in ldap info for: %s, %s'
222                 %(info['uid'][0],info['cn'][0]))
223             self.logger.error('  A valid keyid or longkeyid was not found '
224                 "%s : gpgkey = %s" %(info['cn'][0], gpgkey))
225             is_good = False
226         else:
227             if keyinfo[5]: # fingerprints exist check
228                 is_ok = self._check_fingerprint_integrity(info, keyinfo)
229                 is_match = self._check_id_fingerprint_match(info, keyinfo)
230                 if not is_ok or not is_match:
231                     is_good = False
232         if is_good:
233             return keyinfo
234         return None
235
236
237     def _check_id_fingerprint_match(self, info, keyinfo):
238         # assume it's good until found an error is found
239         is_good = True
240         for x in [2, 3]:
241             # skip blank id field
242             if not keyinfo[x]:
243                 continue
244             for y in keyinfo[x]:
245                 index = len(y.lstrip('0x'))
246                 if y.lstrip('0x').lower() not in [x[-index:].lower() for x in keyinfo[5]]:
247                     self.logger.error('ERROR in ldap info for: %s, %s'
248                         %(info['uid'][0],info['cn'][0]))
249                     self.logger.error('  ' + str(keyinfo))
250                     self.logger.error('  GPGKey id %s not found in the '
251                         % y.lstrip('0x') + 'listed fingerprint(s)')
252                     is_good = False
253         return is_good
254
255
256     def _check_fingerprint_integrity(self, info, keyinfo):
257         # assume it's good until found an error is found
258         is_good = True
259         for x in keyinfo[5]:
260             # check fingerprint integrity
261             if len(x) != 40:
262                 self.logger.error('ERROR in ldap info for: %s, %s'
263                     %(info['uid'][0],info['cn'][0]))
264                 self.logger.error('  GPGKey incorrect fingerprint ' +
265                     'length (%s) for fingerprint: %s' %(len(x), x))
266                 is_good = False
267                 continue
268             if not self.fingerprint_re.match(x):
269                 self.logger.error('ERROR in ldap info for: %s, %s'
270                     %(info['uid'][0],info['cn'][0]))
271                 self.logger.error('  GPGKey: Non hexadecimal digits in ' +
272                     'fingerprint for fingerprint: ' + x)
273                 is_good = False
274         return is_good