3 # Copyright (C) 2014 Simo Sorce <simo@redhat.com>
5 # see file 'COPYING' for use and warranty information
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from ipsilon.login.common import LoginMgrsInstall
21 from ipsilon.info.common import InfoProviderInstall
22 from ipsilon.providers.common import ProvidersInstall
23 from ipsilon.helpers.common import EnvHelpersInstall
24 from ipsilon.util.data import UserStore
25 from ipsilon.tools import files
39 TEMPLATES = '/usr/share/ipsilon/templates/install'
40 CONFDIR = '/etc/ipsilon'
41 DATADIR = '/var/lib/ipsilon'
42 HTTPDCONFD = '/etc/httpd/conf.d'
44 STATICDIR = '/usr/share/ipsilon'
45 WSGI_SOCKET_PREFIX = None
48 class ConfigurationError(Exception):
50 def __init__(self, message):
51 super(ConfigurationError, self).__init__(message)
52 self.message = message
55 return repr(self.message)
58 #Silence cherrypy logging to screen
59 cherrypy.log.screen = False
62 LOGFILE = '/var/log/ipsilon-install.log'
63 logger = logging.getLogger()
67 global logger # pylint: disable=W0603
68 if os.path.isfile(LOGFILE):
70 created = '%s' % time.ctime(os.path.getctime(LOGFILE))
71 shutil.move(LOGFILE, '%s.%s' % (LOGFILE, created))
74 logger = logging.getLogger()
76 lh = logging.FileHandler(LOGFILE)
78 print >> sys.stderr, 'Unable to open %s (%s)' % (LOGFILE, str(e))
79 lh = logging.StreamHandler(sys.stderr)
80 formatter = logging.Formatter('[%(asctime)s] %(message)s')
81 lh.setFormatter(formatter)
85 def install(plugins, args):
86 logger.info('Installation initiated')
87 now = time.strftime("%Y%m%d%H%M%S", time.gmtime())
88 instance_conf = os.path.join(CONFDIR, args['instance'])
90 logger.info('Installing default config files')
91 ipsilon_conf = os.path.join(instance_conf, 'ipsilon.conf')
92 idp_conf = os.path.join(instance_conf, 'idp.conf')
93 args['httpd_conf'] = os.path.join(HTTPDCONFD,
94 'ipsilon-%s.conf' % args['instance'])
95 args['data_dir'] = os.path.join(DATADIR, args['instance'])
96 args['public_data_dir'] = os.path.join(args['data_dir'], 'public')
97 args['wellknown_dir'] = os.path.join(args['public_data_dir'],
99 if os.path.exists(ipsilon_conf):
100 shutil.move(ipsilon_conf, '%s.bakcup.%s' % (ipsilon_conf, now))
101 if os.path.exists(idp_conf):
102 shutil.move(idp_conf, '%s.backup.%s' % (idp_conf, now))
103 if not os.path.exists(instance_conf):
104 os.makedirs(instance_conf, 0700)
105 confopts = {'instance': args['instance'],
106 'datadir': args['data_dir'],
107 'publicdatadir': args['public_data_dir'],
108 'wellknowndir': args['wellknown_dir'],
109 'sysuser': args['system_user'],
110 'ipsilondir': BINDIR,
111 'staticdir': STATICDIR,
112 'admindb': args['database_url'] % {
113 'datadir': args['data_dir'], 'dbname': 'adminconfig'},
114 'usersdb': args['database_url'] % {
115 'datadir': args['data_dir'], 'dbname': 'userprefs'},
116 'transdb': args['database_url'] % {
117 'datadir': args['data_dir'], 'dbname': 'transactions'},
118 'secure': "False" if args['secure'] == "no" else "True",
119 'debugging': "True" if args['server_debugging'] else "False"}
120 # Testing database sessions
121 if 'session_type' in args:
122 confopts['sesstype'] = args['session_type']
124 confopts['sesstype'] = 'file'
125 if 'session_dburi' in args:
126 confopts['sessopt'] = 'dburi'
127 confopts['sessval'] = args['session_dburi']
129 confopts['sessopt'] = 'path'
130 confopts['sessval'] = os.path.join(args['data_dir'], 'sessions')
131 # Whetehr to disable security (for testing)
132 if args['secure'] == 'no':
133 confopts['secure'] = "False"
134 confopts['sslrequiressl'] = ""
136 confopts['secure'] = "True"
137 confopts['sslrequiressl'] = " SSLRequireSSL"
138 if WSGI_SOCKET_PREFIX:
139 confopts['wsgi_socket'] = 'WSGISocketPrefix %s' % WSGI_SOCKET_PREFIX
141 confopts['wsgi_socket'] = ''
142 files.write_from_template(ipsilon_conf,
143 os.path.join(TEMPLATES, 'ipsilon.conf'),
145 files.write_from_template(idp_conf,
146 os.path.join(TEMPLATES, 'idp.conf'),
148 if not os.path.exists(args['httpd_conf']):
149 os.symlink(idp_conf, args['httpd_conf'])
150 if not os.path.exists(args['public_data_dir']):
151 os.makedirs(args['public_data_dir'], 0755)
152 if not os.path.exists(args['wellknown_dir']):
153 os.makedirs(args['wellknown_dir'], 0755)
154 sessdir = os.path.join(args['data_dir'], 'sessions')
155 if not os.path.exists(sessdir):
156 os.makedirs(sessdir, 0700)
157 data_conf = os.path.join(args['data_dir'], 'ipsilon.conf')
158 if not os.path.exists(data_conf):
159 os.symlink(ipsilon_conf, data_conf)
160 # Load the cherrypy config from the newly installed file so
161 # that db paths and all is properly set before configuring
163 cherrypy.config.update(ipsilon_conf)
165 # Move pre-existing admin db away
166 admin_db = cherrypy.config['admin.config.db']
167 if os.path.exists(admin_db):
168 shutil.move(admin_db, '%s.backup.%s' % (admin_db, now))
171 users_db = cherrypy.config['user.prefs.db']
172 if os.path.exists(users_db):
173 shutil.move(users_db, '%s.backup.%s' % (users_db, now))
175 db.save_user_preferences(args['admin_user'], {'is_admin': 1})
177 logger.info('Configuring environment helpers')
178 for plugin_name in plugins['Environment Helpers']:
179 plugin = plugins['Environment Helpers'][plugin_name]
180 plugin.configure_server(args)
182 logger.info('Configuring login managers')
183 for plugin_name in args['lm_order']:
184 plugin = plugins['Login Managers'][plugin_name]
185 plugin.configure(args)
187 logger.info('Configuring Info provider')
188 for plugin_name in plugins['Info Provider']:
189 plugin = plugins['Info Provider'][plugin_name]
190 plugin.configure(args)
192 logger.info('Configuring Authentication Providers')
193 for plugin_name in plugins['Auth Providers']:
194 plugin = plugins['Auth Providers'][plugin_name]
195 plugin.configure(args)
197 # Fixup permissions so only the ipsilon user can read these files
198 files.fix_user_dirs(instance_conf, opts['system_user'])
199 files.fix_user_dirs(args['data_dir'], opts['system_user'])
201 subprocess.call(['/usr/sbin/restorecon', '-R', args['data_dir']])
202 except Exception: # pylint: disable=broad-except
205 def uninstall(plugins, args):
206 logger.info('Uninstallation initiated')
207 raise Exception('Not Implemented')
212 'Environment Helpers': EnvHelpersInstall().plugins,
213 'Login Managers': LoginMgrsInstall().plugins,
214 'Info Provider': InfoProviderInstall().plugins,
215 'Auth Providers': ProvidersInstall().plugins
220 def parse_config_profile(args):
221 config = ConfigParser.RawConfigParser()
222 files = config.read(args['config_profile'])
224 raise ConfigurationError('Config Profile file %s not found!' %
225 args['config_profile'])
227 if 'globals' in config.sections():
228 G = config.options('globals')
230 val = config.get('globals', g)
234 for k in globals().keys():
235 if k.lower() == g.lower():
239 if 'arguments' in config.sections():
240 A = config.options('arguments')
242 args[a] = config.get('arguments', a)
247 def parse_args(plugins):
248 parser = argparse.ArgumentParser(description='Ipsilon Install Options')
249 parser.add_argument('--version',
250 action='version', version='%(prog)s 0.1')
251 parser.add_argument('-o', '--login-managers-order', dest='lm_order',
252 help='Comma separated list of login managers')
253 parser.add_argument('--hostname',
254 help="Machine's fully qualified host name")
255 parser.add_argument('--instance', default='idp',
256 help="IdP instance name, each is a separate idp")
257 parser.add_argument('--system-user', default='ipsilon',
258 help="User account used to run the server")
259 parser.add_argument('--admin-user', default='admin',
260 help="User account that is assigned admin privileges")
261 parser.add_argument('--database-url',
262 default='sqlite:///%(datadir)s/%(dbname)s.sqlite',
263 help="The (templatized) database URL to use")
264 parser.add_argument('--secure', choices=['yes', 'no'], default='yes',
265 help="Turn on all security checks")
266 parser.add_argument('--config-profile', default=None,
267 help="File containing install options")
268 parser.add_argument('--server-debugging', action='store_true',
269 help="Enable debugging")
270 parser.add_argument('--uninstall', action='store_true',
271 help="Uninstall the server and all data")
275 for plugin_group in plugins:
276 group = parser.add_argument_group(plugin_group)
277 for plugin_name in plugins[plugin_group]:
278 plugin = plugins[plugin_group][plugin_name]
279 if plugin.ptype == 'login':
280 lms.append(plugin.name)
281 plugin.install_args(group)
283 args = vars(parser.parse_args())
285 if args['config_profile']:
286 args = parse_config_profile(args)
288 if not args['hostname']:
289 args['hostname'] = socket.getfqdn()
291 if len(args['hostname'].split('.')) < 2:
292 raise ConfigurationError('Hostname: %s is not a FQDN')
295 pwd.getpwnam(args['system_user'])
297 raise ConfigurationError('User: %s not found on the system')
299 if args['lm_order'] is None:
300 args['lm_order'] = []
302 if args[name] == 'yes':
303 args['lm_order'].append(name)
305 args['lm_order'] = args['lm_order'].split(',')
307 if len(args['lm_order']) == 0:
308 #force the basic pam provider if nothing else is selected
309 if 'pam' not in args:
312 args['lm_order'] = ['pam']
315 #FIXME: check instance is only alphanums
319 if __name__ == '__main__':
324 fplugins = find_plugins()
325 opts = parse_args(fplugins)
327 logger.setLevel(logging.DEBUG)
329 logger.info('Intallation arguments:')
330 for k in sorted(opts.iterkeys()):
331 logger.info('%s: %s', k, opts[k])
333 if 'uninstall' in opts and opts['uninstall'] is True:
334 uninstall(fplugins, opts)
336 install(fplugins, opts)
337 except Exception, e: # pylint: disable=broad-except
339 if 'uninstall' in opts and opts['uninstall'] is True:
340 print 'Uninstallation aborted.'
342 print 'Installation aborted.'
343 print 'See log file %s for details' % LOGFILE
347 if 'uninstall' in opts and opts['uninstall'] is True:
348 print 'Uninstallation complete.'
350 print 'Installation complete.'
351 print 'Please restart HTTPD to enable the IdP instance.'