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 if os.path.exists(ipsilon_conf):
97 shutil.move(ipsilon_conf, '%s.bakcup.%s' % (ipsilon_conf, now))
98 if os.path.exists(idp_conf):
99 shutil.move(idp_conf, '%s.backup.%s' % (idp_conf, now))
100 if not os.path.exists(instance_conf):
101 os.makedirs(instance_conf, 0700)
102 confopts = {'instance': args['instance'],
103 'datadir': args['data_dir'],
104 'sysuser': args['system_user'],
105 'ipsilondir': BINDIR,
106 'staticdir': STATICDIR,
107 'admindb': args['database_url'] % {
108 'datadir': args['data_dir'], 'dbname': 'adminconfig'},
109 'usersdb': args['database_url'] % {
110 'datadir': args['data_dir'], 'dbname': 'userprefs'},
111 'transdb': args['database_url'] % {
112 'datadir': args['data_dir'], 'dbname': 'transactions'},
113 'secure': "False" if args['secure'] == "no" else "True",
114 'debugging': "True" if args['server_debugging'] else "False"}
115 if args['secure'] == 'no':
116 confopts['secure'] = "False"
117 confopts['sslrequiressl'] = ""
119 confopts['secure'] = "True"
120 confopts['sslrequiressl'] = " SSLRequireSSL"
121 if WSGI_SOCKET_PREFIX:
122 confopts['wsgi_socket'] = 'WSGISocketPrefix %s' % WSGI_SOCKET_PREFIX
124 confopts['wsgi_socket'] = ''
125 files.write_from_template(ipsilon_conf,
126 os.path.join(TEMPLATES, 'ipsilon.conf'),
128 files.write_from_template(idp_conf,
129 os.path.join(TEMPLATES, 'idp.conf'),
131 if not os.path.exists(args['httpd_conf']):
132 os.symlink(idp_conf, args['httpd_conf'])
133 sessdir = os.path.join(args['data_dir'], 'sessions')
134 if not os.path.exists(sessdir):
135 os.makedirs(sessdir, 0700)
136 data_conf = os.path.join(args['data_dir'], 'ipsilon.conf')
137 if not os.path.exists(data_conf):
138 os.symlink(ipsilon_conf, data_conf)
139 # Load the cherrypy config from the newly installed file so
140 # that db paths and all is properly set before configuring
142 cherrypy.config.update(ipsilon_conf)
144 # Move pre-existing admin db away
145 admin_db = cherrypy.config['admin.config.db']
146 if os.path.exists(admin_db):
147 shutil.move(admin_db, '%s.backup.%s' % (admin_db, now))
150 users_db = cherrypy.config['user.prefs.db']
151 if os.path.exists(users_db):
152 shutil.move(users_db, '%s.backup.%s' % (users_db, now))
154 db.save_user_preferences(args['admin_user'], {'is_admin': 1})
156 logger.info('Configuring environment helpers')
157 for plugin_name in plugins['Environment Helpers']:
158 plugin = plugins['Environment Helpers'][plugin_name]
159 plugin.configure_server(args)
161 logger.info('Configuring login managers')
162 for plugin_name in args['lm_order']:
163 plugin = plugins['Login Managers'][plugin_name]
164 plugin.configure(args)
166 logger.info('Configuring Info provider')
167 for plugin_name in plugins['Info Provider']:
168 plugin = plugins['Info Provider'][plugin_name]
169 plugin.configure(args)
171 logger.info('Configuring Authentication Providers')
172 for plugin_name in plugins['Auth Providers']:
173 plugin = plugins['Auth Providers'][plugin_name]
174 plugin.configure(args)
176 # Fixup permissions so only the ipsilon user can read these files
177 files.fix_user_dirs(instance_conf, opts['system_user'])
178 files.fix_user_dirs(args['data_dir'], opts['system_user'])
180 subprocess.call(['/usr/sbin/restorecon', '-R', args['data_dir']])
181 except Exception: # pylint: disable=broad-except
184 def uninstall(plugins, args):
185 logger.info('Uninstallation initiated')
186 raise Exception('Not Implemented')
191 'Environment Helpers': EnvHelpersInstall().plugins,
192 'Login Managers': LoginMgrsInstall().plugins,
193 'Info Provider': InfoProviderInstall().plugins,
194 'Auth Providers': ProvidersInstall().plugins
199 def parse_config_profile(args):
200 config = ConfigParser.RawConfigParser()
201 files = config.read(args['config_profile'])
203 raise ConfigurationError('Config Profile file %s not found!' %
204 args['config_profile'])
206 if 'globals' in config.sections():
207 G = config.options('globals')
209 val = config.get('globals', g)
213 for k in globals().keys():
214 if k.lower() == g.lower():
218 if 'arguments' in config.sections():
219 A = config.options('arguments')
221 args[a] = config.get('arguments', a)
226 def parse_args(plugins):
227 parser = argparse.ArgumentParser(description='Ipsilon Install Options')
228 parser.add_argument('--version',
229 action='version', version='%(prog)s 0.1')
230 parser.add_argument('-o', '--login-managers-order', dest='lm_order',
231 help='Comma separated list of login managers')
232 parser.add_argument('--hostname',
233 help="Machine's fully qualified host name")
234 parser.add_argument('--instance', default='idp',
235 help="IdP instance name, each is a separate idp")
236 parser.add_argument('--system-user', default='ipsilon',
237 help="User account used to run the server")
238 parser.add_argument('--admin-user', default='admin',
239 help="User account that is assigned admin privileges")
240 parser.add_argument('--database-url',
241 default='sqlite:///%(datadir)s/%(dbname)s.sqlite',
242 help="The (templatized) database URL to use")
243 parser.add_argument('--secure', choices=['yes', 'no'], default='yes',
244 help="Turn on all security checks")
245 parser.add_argument('--config-profile', default=None,
246 help="File containing install options")
247 parser.add_argument('--server-debugging', action='store_true',
248 help="Uninstall the server and all data")
249 parser.add_argument('--uninstall', action='store_true',
250 help="Uninstall the server and all data")
254 for plugin_group in plugins:
255 group = parser.add_argument_group(plugin_group)
256 for plugin_name in plugins[plugin_group]:
257 plugin = plugins[plugin_group][plugin_name]
258 if plugin.ptype == 'login':
259 lms.append(plugin.name)
260 plugin.install_args(group)
262 args = vars(parser.parse_args())
264 if args['config_profile']:
265 args = parse_config_profile(args)
267 if not args['hostname']:
268 args['hostname'] = socket.getfqdn()
270 if len(args['hostname'].split('.')) < 2:
271 raise ConfigurationError('Hostname: %s is not a FQDN')
274 pwd.getpwnam(args['system_user'])
276 raise ConfigurationError('User: %s not found on the system')
278 if args['lm_order'] is None:
279 args['lm_order'] = []
281 if args[name] == 'yes':
282 args['lm_order'].append(name)
284 args['lm_order'] = args['lm_order'].split(',')
286 if len(args['lm_order']) == 0:
287 #force the basic pam provider if nothing else is selected
288 if 'pam' not in args:
291 args['lm_order'] = ['pam']
294 #FIXME: check instance is only alphanums
298 if __name__ == '__main__':
303 fplugins = find_plugins()
304 opts = parse_args(fplugins)
306 logger.setLevel(logging.DEBUG)
308 logger.info('Intallation arguments:')
309 for k in sorted(opts.iterkeys()):
310 logger.info('%s: %s', k, opts[k])
312 if 'uninstall' in opts and opts['uninstall'] is True:
313 uninstall(fplugins, opts)
315 install(fplugins, opts)
316 except Exception, e: # pylint: disable=broad-except
318 if 'uninstall' in opts and opts['uninstall'] is True:
319 print 'Uninstallation aborted.'
321 print 'Installation aborted.'
322 print 'See log file %s for details' % LOGFILE
326 if 'uninstall' in opts and opts['uninstall'] is True:
327 print 'Uninstallation complete.'
329 print 'Installation complete.'
330 print 'Please restart HTTPD to enable the IdP instance.'