ee5efd9fd747bcc0bd20dab135e14edc1acf29f8
[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             new_gkey = GKEY._make(self.build_gkeylist(devs[dev]))
129             self.seeds.add(new_gkey)
130             count += 1
131         self.output("Total number of seeds created:", count)
132         self.output("Seeds created...saving file: %s" % filename)
133         return self.seeds.save()
134
135
136     @staticmethod
137     def get_args(args):
138         for x in ['nick', 'name', 'gpgkey', 'fingerprint', 'status']:
139             if x:
140                 target = getattr(args, x)
141                 search_field = gkey2SEARCH[x]
142                 break
143         return (x, target, search_field)
144
145
146
147     def build_gkeydict(self, info):
148         keyinfo = {}
149         for x in GKEY._fields:
150             field = gkey2ldap_map[x]
151             if not field:
152                 continue
153             try:
154                 # strip errant line feeds
155                 values = [y.strip('\n') for y in info[field]]
156                 if values and values in ['uid', 'cn' ]:
157                     value = values[0]
158                 # separate out short/long key id's
159                 elif values and x in ['keyid', 'longkeyid']:
160                     value = get_key_ids(x, values)
161                 else:
162                     value = values
163                 if 'undefined' in values:
164                     self.logger.error('%s = "undefined" for %s, %s'
165                         %(field, info['uid'][0], info['cn'][0]))
166                 if value:
167                     keyinfo[x] = value
168             except KeyError:
169                 pass
170         return keyinfo
171
172
173     def build_gkeylist(self, info):
174         keyinfo = []
175         keyid_found = False
176         keyid_missing = False
177         #self.logger.debug("MAIN: build_gkeylist; info = %s" % str(info))
178         for x in GKEY._fields:
179             field = gkey2ldap_map[x]
180             if not field:
181                 keyinfo.append(None)
182                 continue
183             try:
184                 # strip errant line feeds
185                 values = [y.strip('\n') for y in info[field]]
186                 if values and field in ['uid', 'cn' ]:
187                     value = values[0]
188                 # separate out short/long key id's
189                 elif values and x in ['keyid', 'longkeyid']:
190                     value = get_key_ids(x, values)
191                     if len(value):
192                         keyid_found = True
193                 elif values and x in ['fingerprint']:
194                     value = [v.replace(' ', '') for v in values]
195                 else:
196                     value = values
197                 if 'undefined' in values:
198                     self.logger.error('ERROR in ldap info for: %s, %s'
199                         %(info['uid'][0],info['cn'][0]))
200                     self.logger.error('  %s = "undefined"' %(field))
201                 keyinfo.append(value)
202             except KeyError:
203                 self.logger.error('ERROR in ldap info for: %s, %s'
204                     %(info['uid'][0],info['cn'][0]))
205                 self.logger.error('  MISSING or EMPTY ldap field ' +
206                     '[%s] GPGKey field [%s]' %(field, x))
207                 if x in ['keyid', 'longkeyid']:
208                     keyid_missing = True
209                 keyinfo.append(None)
210         if not keyid_found and not keyid_missing:
211             try:
212                 gpgkey = info[gkey2ldap_map['longkeyid']]
213             except KeyError:
214                 gpgkey = 'Missing from ldap info'
215             self.logger.error('ERROR in ldap info for: %s, %s'
216                 %(info['uid'][0],info['cn'][0]))
217             self.logger.error('  A valid keyid or longkeyid was not found '
218                 "%s : gpgkey = %s" %(info['cn'][0], gpgkey))
219         else:
220             if keyinfo[5]: # fingerprints exist check
221                 self._check_fingerprint_integrity(info, keyinfo)
222                 self._check_id_fingerprint_match(info, keyinfo)
223         return keyinfo
224
225
226     def _check_id_fingerprint_match(self, info, keyinfo):
227         for x in [2, 3]:
228             # skip blank id field
229             if not keyinfo[x]:
230                 continue
231             for y in keyinfo[x]:
232                 index = len(y.lstrip('0x'))
233                 if y.lstrip('0x').lower() not in [x[-index:].lower() for x in keyinfo[5]]:
234                     self.logger.error('ERROR in ldap info for: %s, %s'
235                         %(info['uid'][0],info['cn'][0]))
236                     self.logger.error('  ' + str(keyinfo))
237                     self.logger.error('  GPGKey id %s not found in the '
238                         % y.lstrip('0x') + 'listed fingerprint(s)')
239         return
240
241
242     def _check_fingerprint_integrity(self, info, keyinfo):
243         for x in keyinfo[5]:
244             # check fingerprint integrity
245             if len(x) != 40:
246                 self.logger.error('ERROR in ldap info for: %s, %s'
247                     %(info['uid'][0],info['cn'][0]))
248                 self.logger.error('  GPGKey incorrect fingerprint ' +
249                     'length (%s) for fingerprint: %s' %(len(x), x))
250                 continue
251             if not self.fingerprint_re.match(x):
252                 self.logger.error('ERROR in ldap info for: %s, %s'
253                     %(info['uid'][0],info['cn'][0]))
254                 self.logger.error('  GPGKey: Non hexadecimal digits in ' +
255                     'fingerprint for fingerprint: ' + x)
256         return