2 # Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
4 from ipsilon.login.common import LoginMgrsInstall
5 from ipsilon.info.common import InfoProviderInstall
6 from ipsilon.providers.common import ProvidersInstall
7 from ipsilon.helpers.common import EnvHelpersInstall
8 from ipsilon.util.data import UserStore
9 from ipsilon.tools import files
23 TEMPLATES = '/usr/share/ipsilon/templates/install'
24 CONFDIR = '/etc/ipsilon'
25 DATADIR = '/var/lib/ipsilon'
26 HTTPDCONFD = '/etc/httpd/conf.d'
27 BINDIR = '/usr/libexec'
28 STATICDIR = '/usr/share/ipsilon'
29 WSGI_SOCKET_PREFIX = None
32 class ConfigurationError(Exception):
34 def __init__(self, message):
35 super(ConfigurationError, self).__init__(message)
36 self.message = message
39 return repr(self.message)
42 #Silence cherrypy logging to screen
43 cherrypy.log.screen = False
46 LOGFILE = '/var/log/ipsilon-install.log'
47 logger = logging.getLogger()
51 global logger # pylint: disable=W0603
52 if os.path.isfile(LOGFILE):
54 created = '%s' % time.ctime(os.path.getctime(LOGFILE))
55 shutil.move(LOGFILE, '%s.%s' % (LOGFILE, created))
58 logger = logging.getLogger()
60 lh = logging.FileHandler(LOGFILE)
62 print >> sys.stderr, 'Unable to open %s (%s)' % (LOGFILE, str(e))
63 lh = logging.StreamHandler(sys.stderr)
64 formatter = logging.Formatter('[%(asctime)s] %(message)s')
65 lh.setFormatter(formatter)
66 lh.setLevel(logging.DEBUG)
68 logger.propagate = False
69 ch = logging.StreamHandler(sys.stdout)
70 formatter = logging.Formatter('%(message)s')
71 ch.setFormatter(formatter)
72 ch.setLevel(logging.INFO)
74 cherrypy.log.error_log.setLevel(logging.DEBUG)
77 def install(plugins, args):
78 logger.info('Installation initiated')
79 now = time.strftime("%Y%m%d%H%M%S", time.gmtime())
80 instance_conf = os.path.join(CONFDIR, args['instance'])
82 logger.info('Installing default config files')
83 ipsilon_conf = os.path.join(instance_conf, 'ipsilon.conf')
84 idp_conf = os.path.join(instance_conf, 'idp.conf')
85 args['httpd_conf'] = os.path.join(HTTPDCONFD,
86 'ipsilon-%s.conf' % args['instance'])
87 args['data_dir'] = os.path.join(DATADIR, args['instance'])
88 args['public_data_dir'] = os.path.join(args['data_dir'], 'public')
89 args['wellknown_dir'] = os.path.join(args['public_data_dir'],
91 if os.path.exists(ipsilon_conf):
92 shutil.move(ipsilon_conf, '%s.bakcup.%s' % (ipsilon_conf, now))
93 if os.path.exists(idp_conf):
94 shutil.move(idp_conf, '%s.backup.%s' % (idp_conf, now))
95 if not os.path.exists(instance_conf):
96 os.makedirs(instance_conf, 0700)
97 confopts = {'instance': args['instance'],
98 'datadir': args['data_dir'],
99 'publicdatadir': args['public_data_dir'],
100 'wellknowndir': args['wellknown_dir'],
101 'sysuser': args['system_user'],
102 'ipsilondir': BINDIR,
103 'staticdir': STATICDIR,
104 'admindb': args['admin_dburi'] or args['database_url'] % {
105 'datadir': args['data_dir'], 'dbname': 'adminconfig'},
106 'usersdb': args['users_dburi'] or args['database_url'] % {
107 'datadir': args['data_dir'], 'dbname': 'userprefs'},
108 'transdb': args['transaction_dburi'] or args['database_url'] %
109 {'datadir': args['data_dir'], 'dbname': 'transactions'},
110 'secure': "False" if args['secure'] == "no" else "True",
111 'debugging': "True" if args['server_debugging'] else "False"}
112 # Testing database sessions
113 if 'session_type' in args:
114 confopts['sesstype'] = args['session_type']
116 confopts['sesstype'] = 'file'
117 if 'session_dburi' in args:
118 confopts['sessopt'] = 'dburi'
119 confopts['sessval'] = args['session_dburi']
121 confopts['sessopt'] = 'path'
122 confopts['sessval'] = os.path.join(args['data_dir'], 'sessions')
123 # Whether to disable security (for testing)
124 if args['secure'] == 'no':
125 confopts['secure'] = "False"
126 confopts['sslrequiressl'] = ""
128 confopts['secure'] = "True"
129 confopts['sslrequiressl'] = " SSLRequireSSL"
130 if WSGI_SOCKET_PREFIX:
131 confopts['wsgi_socket'] = 'WSGISocketPrefix %s' % WSGI_SOCKET_PREFIX
133 confopts['wsgi_socket'] = ''
134 files.write_from_template(ipsilon_conf,
135 os.path.join(TEMPLATES, 'ipsilon.conf'),
137 files.write_from_template(idp_conf,
138 os.path.join(TEMPLATES, 'idp.conf'),
140 if not os.path.exists(args['httpd_conf']):
141 os.symlink(idp_conf, args['httpd_conf'])
142 if not os.path.exists(args['public_data_dir']):
143 os.makedirs(args['public_data_dir'], 0755)
144 if not os.path.exists(args['wellknown_dir']):
145 os.makedirs(args['wellknown_dir'], 0755)
146 sessdir = os.path.join(args['data_dir'], 'sessions')
147 if not os.path.exists(sessdir):
148 os.makedirs(sessdir, 0700)
149 data_conf = os.path.join(args['data_dir'], 'ipsilon.conf')
150 if not os.path.exists(data_conf):
151 os.symlink(ipsilon_conf, data_conf)
152 # Load the cherrypy config from the newly installed file so
153 # that db paths and all is properly set before configuring
155 cherrypy.config.update(ipsilon_conf)
157 # Move pre-existing admin db away
158 admin_db = cherrypy.config['admin.config.db']
159 if os.path.exists(admin_db):
160 shutil.move(admin_db, '%s.backup.%s' % (admin_db, now))
163 users_db = cherrypy.config['user.prefs.db']
164 if os.path.exists(users_db):
165 shutil.move(users_db, '%s.backup.%s' % (users_db, now))
167 db.save_user_preferences(args['admin_user'], {'is_admin': 1})
169 logger.info('Configuring environment helpers')
170 for plugin_name in plugins['Environment Helpers']:
171 plugin = plugins['Environment Helpers'][plugin_name]
172 if plugin.configure_server(args) == False:
173 logger.info('Configuration of environment helper %s failed' % plugin_name)
175 logger.info('Configuring login managers')
176 for plugin_name in args['lm_order']:
178 plugin = plugins['Login Managers'][plugin_name]
180 sys.exit('Login provider %s not installed' % plugin_name)
181 if plugin.configure(args) == False:
182 logger.info('Configuration of login manager %s failed' % plugin_name)
184 logger.info('Configuring Info provider')
185 for plugin_name in plugins['Info Provider']:
186 plugin = plugins['Info Provider'][plugin_name]
187 if plugin.configure(args) == False:
188 logger.info('Configuration of info provider %s failed' % plugin_name)
190 logger.info('Configuring Authentication Providers')
191 for plugin_name in plugins['Auth Providers']:
192 plugin = plugins['Auth Providers'][plugin_name]
193 if plugin.configure(args) == False:
194 logger.info('Configuration of auth provider %s failed' % plugin_name)
196 # Fixup permissions so only the ipsilon user can read these files
197 files.fix_user_dirs(instance_conf, opts['system_user'])
198 files.fix_user_dirs(args['data_dir'], opts['system_user'])
200 subprocess.call(['/usr/sbin/restorecon', '-R', args['data_dir']])
201 except Exception: # pylint: disable=broad-except
205 def uninstall(plugins, args):
206 logger.info('Uninstallation initiated')
207 instance_conf = os.path.join(CONFDIR, args['instance'])
209 httpd_conf = os.path.join(HTTPDCONFD,
210 'ipsilon-%s.conf' % args['instance'])
211 data_dir = os.path.join(DATADIR, args['instance'])
213 if not os.path.exists(instance_conf):
214 raise Exception('Could not find instance %s configuration'
216 if not os.path.exists(httpd_conf):
217 raise Exception('Could not find instance %s httpd configuration'
220 sure = raw_input(('Are you certain you want to erase instance %s ' +
224 raise Exception('Aborting')
226 logger.info('Removing environment helpers')
227 for plugin_name in plugins['Environment Helpers']:
228 plugin = plugins['Environment Helpers'][plugin_name]
229 if plugin.unconfigure(args) == False:
230 logger.info('Removal of environment helper %s failed' % plugin_name)
232 logger.info('Removing login managers')
233 for plugin_name in plugins['Login Managers']:
234 plugin = plugins['Login Managers'][plugin_name]
235 if plugin.unconfigure(args) == False:
236 logger.info('Removal of login manager %s failed' % plugin_name)
238 logger.info('Removing Info providers')
239 for plugin_name in plugins['Info Provider']:
240 plugin = plugins['Info Provider'][plugin_name]
241 if plugin.unconfigure(args) == False:
242 logger.info('Removal of info provider %s failed' % plugin_name)
244 logger.info('Removing Authentication Providers')
245 for plugin_name in plugins['Auth Providers']:
246 plugin = plugins['Auth Providers'][plugin_name]
247 if plugin.unconfigure(args) == False:
248 logger.info('Removal of auth provider %s failed' % plugin_name)
250 logger.info('Removing httpd configuration')
251 os.remove(httpd_conf)
252 logger.info('Erasing instance configuration')
253 shutil.rmtree(instance_conf)
254 logger.info('Erasing instance data')
255 shutil.rmtree(data_dir)
256 logger.info('Uninstalled instance %s' % args['instance'])
261 'Environment Helpers': EnvHelpersInstall().plugins,
262 'Login Managers': LoginMgrsInstall().plugins,
263 'Info Provider': InfoProviderInstall().plugins,
264 'Auth Providers': ProvidersInstall().plugins
269 def parse_config_profile(args):
270 config = ConfigParser.RawConfigParser()
271 files = config.read(args['config_profile'])
273 raise ConfigurationError('Config Profile file %s not found!' %
274 args['config_profile'])
276 if 'globals' in config.sections():
277 G = config.options('globals')
279 val = config.get('globals', g)
283 for k in globals().keys():
284 if k.lower() == g.lower():
288 if 'arguments' in config.sections():
289 A = config.options('arguments')
291 args[a] = config.get('arguments', a)
296 def parse_args(plugins):
297 parser = argparse.ArgumentParser(description='Ipsilon Install Options')
298 parser.add_argument('--version',
299 action='version', version='%(prog)s 0.1')
300 parser.add_argument('-o', '--login-managers-order', dest='lm_order',
301 help='Comma separated list of login managers')
302 parser.add_argument('--hostname',
303 help="Machine's fully qualified host name")
304 parser.add_argument('--instance', default='idp',
305 help="IdP instance name, each is a separate idp")
306 parser.add_argument('--system-user', default='ipsilon',
307 help="User account used to run the server")
308 parser.add_argument('--admin-user', default='admin',
309 help="User account that is assigned admin privileges")
310 parser.add_argument('--database-url',
311 default='sqlite:///%(datadir)s/%(dbname)s.sqlite',
312 help="The (templatized) database URL to use")
313 parser.add_argument('--secure', choices=['yes', 'no'], default='yes',
314 help="Turn on all security checks")
315 parser.add_argument('--config-profile', default=None,
316 help=argparse.SUPPRESS)
317 parser.add_argument('--server-debugging', action='store_true',
318 help="Enable debugging")
319 parser.add_argument('--uninstall', action='store_true',
320 help="Uninstall the server and all data")
321 parser.add_argument('--yes', action='store_true',
322 help="Always answer yes")
323 parser.add_argument('--admin-dburi',
324 help='Configuration database URI (override template)')
325 parser.add_argument('--users-dburi',
326 help='User configuration database URI (override '
328 parser.add_argument('--transaction-dburi',
329 help='Transaction database URI (override template)')
333 for plugin_group in plugins:
334 group = parser.add_argument_group(plugin_group)
335 for plugin_name in plugins[plugin_group]:
336 plugin = plugins[plugin_group][plugin_name]
337 if plugin.ptype == 'login':
338 lms.append(plugin.name)
339 plugin.install_args(group)
341 args = vars(parser.parse_args())
343 if args['config_profile']:
344 args = parse_config_profile(args)
346 if not args['hostname']:
347 args['hostname'] = socket.getfqdn()
349 if args['uninstall']:
352 if len(args['hostname'].split('.')) < 2:
353 raise ConfigurationError('Hostname: %s is not a FQDN')
355 for plugin_group in plugins:
356 for plugin_name in plugins[plugin_group]:
357 plugin = plugins[plugin_group][plugin_name]
358 plugin.validate_args(args)
361 pwd.getpwnam(args['system_user'])
363 raise ConfigurationError('User: %s not found on the system')
365 if args['lm_order'] is None:
366 args['lm_order'] = []
368 if args[name] == 'yes':
369 args['lm_order'].append(name)
371 args['lm_order'] = args['lm_order'].split(',')
373 if len(args['lm_order']) == 0:
374 sys.exit('No login plugins are enabled.')
376 #FIXME: check instance is only alphanums
380 if __name__ == '__main__':
385 fplugins = find_plugins()
386 opts = parse_args(fplugins)
388 logger.setLevel(logging.DEBUG)
390 logger.debug('Installation arguments:')
391 for k in sorted(opts.iterkeys()):
392 logger.debug('%s: %s', k, opts[k])
394 if 'uninstall' in opts and opts['uninstall'] is True:
395 if not os.path.exists(os.path.join(CONFDIR, opts['instance'])):
396 logger.info('Instance %s could not be found' % opts['instance'])
398 uninstall(fplugins, opts)
400 install(fplugins, opts)
401 except Exception, e: # pylint: disable=broad-except
403 if 'uninstall' in opts and opts['uninstall'] is True:
404 logger.info('Uninstallation aborted.')
406 logger.info('Installation aborted.')
407 logger.info('See log file %s for details' % LOGFILE)
414 if 'uninstall' in opts and opts['uninstall'] is True:
415 logger.info('Uninstallation complete.')
417 logger.info('Installation complete.')
418 logger.info('Please restart HTTPD to enable the IdP instance.')