strip errant line feeds in ldap values
[gentoo-keys.git] / gkeyldap / cli.py
1 #!/usr/bin/env python
2
3 from __future__ import print_function
4
5
6 import sys
7 import os
8 import argparse
9
10 from gkeys import log
11 log.set_logger('gkeyldap')
12 from gkeys.log import logger
13
14 from gkeys.config import GKeysConfig, GKEY
15 from gkeys.seed import Seeds
16 from gkeyldap.search import (LdapSearch, UID, gkey2ldap_map, gkey2SEARCH)
17
18
19 # set debug level to min
20 logger.setLevel(0)
21
22
23 # set some defaults
24 KEY_LEN = {
25     'keyid': 8,
26     'longkeyid': 16,
27 }
28
29
30 def get_key_ids(key, info):
31     '''Small utility function to return only keyid (short)
32     or longkeyid's
33
34     @param key: string, the key lenght desired
35     @param info: list of keysid's to process
36     @return list of the desired key lengh id's
37     '''
38     result = []
39     for x in info:
40         if x.startswith('0x'):
41             mylen = KEY_LEN[key] + 2
42         else:
43             mylen = KEY_LEN[key]
44         if len(x) == mylen:
45             result.append(x)
46     return result
47
48
49 class Main(object):
50     '''Main command line interface class'''
51
52
53     def __init__(self, root=None, config=None, print_results=True):
54         """ Main class init function.
55
56         @param root: string, root path to use
57         """
58         self.root = root or "/"
59         self.config = config or GKeysConfig(root=root)
60         self.print_results = print_results
61         self.args = None
62         self.seeds = None
63
64
65     def __call__(self, args=None):
66         if args:
67             self.run(self.parse_args(args))
68         else:
69             self.run(self.parse_args(sys.argv[1:]))
70
71
72     def parse_args(self, args):
73         '''Parse a list of aruments
74
75         @param args: list
76         @returns argparse.Namespace object
77         '''
78         #logger.debug('MAIN: parse_args; args: %s' % args)
79         actions = ['ldapsearch', 'updateseeds']
80         parser = argparse.ArgumentParser(
81             prog='gkeys',
82             description='Gentoo-keys manager program',
83             epilog='''Caution: adding untrusted keys to these keyrings can
84                 be hazardous to your system!''')
85         # actions
86         parser.add_argument('action', choices=actions, nargs='?',
87             default='ldapsearch', help='Search ldap or update the seed file')
88         # options
89         parser.add_argument('-c', '--config', dest='config', default=None,
90             help='The path to an alternate config file')
91         parser.add_argument('-d', '--dest', dest='destination', default=None,
92             help='The destination db file path')
93         parser.add_argument('-N', '--name', dest='name', default=None,
94             help='The name to search for')
95         parser.add_argument('-n', '--nick', dest='nick', default=None,
96             help='The nick or user id (uid) to search for')
97         parser.add_argument('-m', '--mail', dest='mail', default=None,
98             help='The email address to search for')
99         parser.add_argument('-k', '--keyid', dest='keyid', default=None,
100             help='The gpg keyid to search for')
101         parser.add_argument('-f', '--fingerprint', dest='fingerprint', default=None,
102             help='The gpg fingerprint to search for')
103         parser.add_argument('-S', '--status', default=False,
104             help='The seedfile path to use')
105         parser.add_argument('-D', '--debug', default=0,
106             help='The logging level to use and report with')
107
108         return parser.parse_args(args)
109
110
111     def run(self, args):
112         '''Run the args passed in
113
114         @param args: list or argparse.Namespace object
115         '''
116         if not args:
117             logger.error("Main: run; invalid args argument passed in")
118         if isinstance(args, list):
119             args = self.parse_args(args)
120         if args.debug:
121             logger.setLevel(int(args.debug))
122             logger.debug("MAIN: run; Found alternate debug setting: %s" % str(args.debug))
123         if args.config:
124             logger.debug("Main: run; Found alternate config request: %s"
125                 % args.config)
126             self.config.defaults['config'] = args.config
127         # now make it load the config file
128         self.config.read_config()
129
130         func = getattr(self, '_action_%s' % args.action)
131         logger.debug('Main: run; Found action: %s' % args.action)
132         results = func(args)
133         return results
134
135
136     def _action_ldapsearch(self, args):
137         l = LdapSearch()
138         if not l.connect():
139             print("Aborting Search...Connection failed")
140             return False
141         logger.debug("MAIN: _action_ldapsearch; args = %s" % str(args))
142         x, target, search_field = self.get_args(args)
143         results = l.search(target, search_field)
144         devs = l.result2dict(results, gkey2ldap_map[x])
145         for dev in sorted(devs):
146             print(dev, devs[dev])
147         print("============================================")
148         print("Total number of devs in results:", len(devs))
149         return True
150
151
152     def _action_updateseeds(self, args):
153         print("Beginning ldap search...")
154         l = LdapSearch()
155         if not l.connect():
156             print("Aborting Update...Connection failed")
157             return False
158         results = l.search('*', UID)
159         info = l.result2dict(results, 'uid')
160         logger.debug(
161             "MAIN: _action_updateseeds; got results :) converted to info")
162         if not self.create_seedfile(info):
163             logger.error("Dev seed file update failure: "
164                 "Original seed file is intact & untouched.")
165         old = self.config['dev-seedfile'] + '.old'
166         try:
167             print("Backing up existing file...")
168             if os.path.exists(old):
169                 logger.debug(
170                     "MAIN: _action_updateseeds; Removing 'old' seed file: %s"
171                     % old)
172                 os.unlink(old)
173             if os.path.exists(self.config['dev-seedfile']):
174                 logger.debug(
175                     "MAIN: _action_updateseeds; Renaming current seed file to: "
176                     "%s" % old)
177                 os.rename(self.config['dev-seedfile'], old)
178             logger.debug(
179                 "MAIN: _action_updateseeds; Renaming '.new' seed file to: %s"
180                 % self.config['dev-seedfile'])
181             os.rename(self.config['dev-seedfile'] + '.new',
182                 self.config['dev-seedfile'])
183         except IOError:
184             raise
185         print("Developer Seed file updated")
186         return True
187
188
189     def create_seedfile(self, devs):
190         print("Creating seeds from ldap data...")
191         filename = self.config['dev-seedfile'] + '.new'
192         self.seeds = Seeds(filename)
193         count = 0
194         for dev in sorted(devs):
195             if devs[dev]['gentooStatus'][0] not in ['active']:
196                 continue
197             #logger.debug("create_seedfile, dev = "
198             #   "%s, %s" % (str(dev), str(devs[dev])))
199             new_gkey = GKEY._make(self.build_gkeylist(devs[dev]))
200             self.seeds.add(new_gkey)
201             count += 1
202         print("Total number of seeds created:", count)
203         print("Seeds created...saving file: %s" % filename)
204         return self.seeds.save()
205
206
207     @staticmethod
208     def get_args(args):
209         for x in ['nick', 'name', 'gpgkey', 'fingerprint', 'status']:
210             if x:
211                 target = getattr(args, x)
212                 search_field = gkey2SEARCH[x]
213                 break
214         return (x, target, search_field)
215
216
217     @staticmethod
218     def build_gkeydict(info):
219         keyinfo = {}
220         for x in GKEY._fields:
221             field = gkey2ldap_map[x]
222             if not field:
223                 continue
224             try:
225                 values = info[field]
226                 if values and values in ['uid', 'cn' ]:
227                     value = values[0]
228                 else:
229                     value = values
230                 if value:
231                     keyinfo[x] = value
232             except KeyError:
233                 pass
234         return keyinfo
235
236
237     @staticmethod
238     def build_gkeylist(info):
239         keyinfo = []
240         #logger.debug("MAIN: build_gkeylist; info = %s" % str(info))
241         for x in GKEY._fields:
242             field = gkey2ldap_map[x]
243             if not field:
244                 keyinfo.append(None)
245                 continue
246             try:
247                 values = info[field]
248                 # strip errant line feeds
249                 values = [x.strip('\n') for x in values]
250                 if values and field in ['uid', 'cn' ]:
251                     value = values[0].strip('\n')
252                 # separate out short/long key id's
253                 elif values and x in ['keyid', 'longkeyid']:
254                     value = get_key_ids(x, values)
255                 else:
256                     value = values
257                 if 'undefined' in values:
258                     logger.error('%s = "undefined" for %s, %s'
259                         %(field, info['uid'][0], info['cn'][0]))
260                 keyinfo.append(value)
261             except KeyError:
262                 logger.error("Missing %s (%s) for %s, %s"
263                     %(field, x, info['uid'][0], info['cn'][0]))
264                 keyinfo.append(None)
265         return keyinfo
266