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 Store
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 'secure': "False" if args['secure'] == "no" else "True",
108 'debugging': "True" if args['server_debugging'] else "False"}
109 if args['secure'] == 'no':
110 confopts['secure'] = "False"
111 confopts['sslrequiressl'] = ""
113 confopts['secure'] = "True"
114 confopts['sslrequiressl'] = " SSLRequireSSL"
115 if WSGI_SOCKET_PREFIX:
116 confopts['wsgi_socket'] = 'WSGISocketPrefix %s' % WSGI_SOCKET_PREFIX
118 confopts['wsgi_socket'] = ''
119 files.write_from_template(ipsilon_conf,
120 os.path.join(TEMPLATES, 'ipsilon.conf'),
122 files.write_from_template(idp_conf,
123 os.path.join(TEMPLATES, 'idp.conf'),
125 if not os.path.exists(args['httpd_conf']):
126 os.symlink(idp_conf, args['httpd_conf'])
127 sessdir = os.path.join(args['data_dir'], 'sessions')
128 if not os.path.exists(sessdir):
129 os.makedirs(sessdir, 0700)
130 data_conf = os.path.join(args['data_dir'], 'ipsilon.conf')
131 if not os.path.exists(data_conf):
132 os.symlink(ipsilon_conf, data_conf)
133 # Load the cherrypy config from the newly installed file so
134 # that db paths and all is properly set before configuring
136 cherrypy.config.update(ipsilon_conf)
138 # Move pre-existing admin db away
139 admin_db = cherrypy.config['admin.config.db']
140 if os.path.exists(admin_db):
141 shutil.move(admin_db, '%s.backup.%s' % (admin_db, now))
144 users_db = cherrypy.config['user.prefs.db']
145 if os.path.exists(users_db):
146 shutil.move(users_db, '%s.backup.%s' % (users_db, now))
148 db.save_user_preferences(args['admin_user'], {'is_admin': 1})
150 logger.info('Configuring environment helpers')
151 for plugin_name in plugins['Environment Helpers']:
152 plugin = plugins['Environment Helpers'][plugin_name]
153 plugin.configure_server(args)
155 logger.info('Configuring login managers')
156 for plugin_name in args['lm_order']:
157 plugin = plugins['Login Managers'][plugin_name]
158 plugin.configure(args)
160 logger.info('Configuring Info provider')
161 for plugin_name in plugins['Info Provider']:
162 plugin = plugins['Info Provider'][plugin_name]
163 plugin.configure(args)
165 logger.info('Configuring Authentication Providers')
166 for plugin_name in plugins['Auth Providers']:
167 plugin = plugins['Auth Providers'][plugin_name]
168 plugin.configure(args)
170 # Fixup permissions so only the ipsilon user can read these files
171 files.fix_user_dirs(instance_conf, opts['system_user'])
172 files.fix_user_dirs(args['data_dir'], opts['system_user'])
174 subprocess.call(['/usr/sbin/restorecon', '-R', args['data_dir']])
175 except Exception: # pylint: disable=broad-except
178 def uninstall(plugins, args):
179 logger.info('Uninstallation initiated')
180 raise Exception('Not Implemented')
185 'Environment Helpers': EnvHelpersInstall().plugins,
186 'Login Managers': LoginMgrsInstall().plugins,
187 'Info Provider': InfoProviderInstall().plugins,
188 'Auth Providers': ProvidersInstall().plugins
193 def parse_config_profile(args):
194 config = ConfigParser.ConfigParser()
195 files = config.read(args['config_profile'])
197 raise ConfigurationError('Config Profile file %s not found!' %
198 args['config_profile'])
200 if 'globals' in config.sections():
201 G = config.options('globals')
203 val = config.get('globals', g)
207 for k in globals().keys():
208 if k.lower() == g.lower():
212 if 'arguments' in config.sections():
213 A = config.options('arguments')
215 args[a] = config.get('arguments', a)
220 def parse_args(plugins):
221 parser = argparse.ArgumentParser(description='Ipsilon Install Options')
222 parser.add_argument('--version',
223 action='version', version='%(prog)s 0.1')
224 parser.add_argument('-o', '--login-managers-order', dest='lm_order',
225 help='Comma separated list of login managers')
226 parser.add_argument('--hostname',
227 help="Machine's fully qualified host name")
228 parser.add_argument('--instance', default='idp',
229 help="IdP instance name, each is a separate idp")
230 parser.add_argument('--system-user', default='ipsilon',
231 help="User account used to run the server")
232 parser.add_argument('--admin-user', default='admin',
233 help="User account that is assigned admin privileges")
234 parser.add_argument('--secure', choices=['yes', 'no'], default='yes',
235 help="Turn on all security checks")
236 parser.add_argument('--config-profile', default=None,
237 help="File containing install options")
238 parser.add_argument('--server-debugging', action='store_true',
239 help="Uninstall the server and all data")
240 parser.add_argument('--uninstall', action='store_true',
241 help="Uninstall the server and all data")
245 for plugin_group in plugins:
246 group = parser.add_argument_group(plugin_group)
247 for plugin_name in plugins[plugin_group]:
248 plugin = plugins[plugin_group][plugin_name]
249 if plugin.ptype == 'login':
250 lms.append(plugin.name)
251 plugin.install_args(group)
253 args = vars(parser.parse_args())
255 if args['config_profile']:
256 args = parse_config_profile(args)
258 if not args['hostname']:
259 args['hostname'] = socket.getfqdn()
261 if len(args['hostname'].split('.')) < 2:
262 raise ConfigurationError('Hostname: %s is not a FQDN')
265 pwd.getpwnam(args['system_user'])
267 raise ConfigurationError('User: %s not found on the system')
269 if args['lm_order'] is None:
270 args['lm_order'] = []
272 if args[name] == 'yes':
273 args['lm_order'].append(name)
275 args['lm_order'] = args['lm_order'].split(',')
277 if len(args['lm_order']) == 0:
278 #force the basic pam provider if nothing else is selected
279 if 'pam' not in args:
282 args['lm_order'] = ['pam']
285 #FIXME: check instance is only alphanums
289 if __name__ == '__main__':
294 fplugins = find_plugins()
295 opts = parse_args(fplugins)
297 logger.setLevel(logging.DEBUG)
299 logger.info('Intallation arguments:')
300 for k in sorted(opts.iterkeys()):
301 logger.info('%s: %s', k, opts[k])
303 if 'uninstall' in opts and opts['uninstall'] is True:
304 uninstall(fplugins, opts)
306 install(fplugins, opts)
307 except Exception, e: # pylint: disable=broad-except
309 if 'uninstall' in opts and opts['uninstall'] is True:
310 print 'Uninstallation aborted.'
312 print 'Installation aborted.'
313 print 'See log file %s for details' % LOGFILE
317 if 'uninstall' in opts and opts['uninstall'] is True:
318 print 'Uninstallation complete.'
320 print 'Installation complete.'
321 print 'Please restart HTTPD to enable the IdP instance.'