Add a default admin user at install time
[cascardo/ipsilon.git] / ipsilon / install / server.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2014  Simo Sorce <simo@redhat.com>
4 #
5 # see file 'COPYING' for use and warranty information
6 #
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.
11 #
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.
16 #
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/>.
19
20 from ipsilon.login.common import LoginMgrsInstall
21 from ipsilon.providers.common import ProvidersInstall
22 from ipsilon.util.data import Store
23 import argparse
24 import cherrypy
25 import logging
26 import os
27 import pwd
28 import shutil
29 import socket
30 import sys
31 import time
32
33
34 TEMPLATES = '/usr/share/ipsilon/templates/install'
35 CONFDIR = '/etc/ipsilon'
36 HTTPDCONFD = '/etc/httpd/conf.d'
37
38
39 class ConfigurationError(Exception):
40
41     def __init__(self, message):
42         super(ConfigurationError, self).__init__(message)
43         self.message = message
44
45     def __str__(self):
46         return repr(self.message)
47
48
49 #Silence cherrypy logging to screen
50 cherrypy.log.screen = False
51
52 # Regular logging
53 LOGFILE = '/var/log/ipsilon-install.log'
54 logger = logging.getLogger()
55
56
57 def openlogs():
58     global logger  # pylint: disable=W0603
59     if os.path.isfile(LOGFILE):
60         try:
61             created = '%s' % time.ctime(os.path.getctime(LOGFILE))
62             shutil.move(LOGFILE, '%s.%s' % (LOGFILE, created))
63         except IOError:
64             pass
65     logger = logging.getLogger()
66     try:
67         lh = logging.FileHandler(LOGFILE)
68     except IOError, e:
69         print >> sys.stderr, 'Unable to open %s (%s)' % (LOGFILE, str(e))
70         lh = logging.StreamHandler(sys.stderr)
71     formatter = logging.Formatter('[%(asctime)s] %(message)s')
72     lh.setFormatter(formatter)
73     logger.addHandler(lh)
74
75
76 def install(plugins, args):
77     logger.info('Installation initiated')
78     now = time.strftime("%Y%m%d%H%M%S", time.gmtime())
79
80     logger.info('Installing default config files')
81     ipsilon_conf = os.path.join(CONFDIR, 'ipsilon.conf')
82     idp_conf = os.path.join(CONFDIR, 'idp.conf')
83     args['httpd_conf'] = os.path.join(HTTPDCONFD, 'idp.conf')
84     if os.path.exists(ipsilon_conf):
85         shutil.move(ipsilon_conf, '%s.bakcup.%s' % (ipsilon_conf, now))
86     if os.path.exists(idp_conf):
87         shutil.move(idp_conf, '%s.backup.%s' % (idp_conf, now))
88     shutil.copy(os.path.join(TEMPLATES, 'ipsilon.conf'), CONFDIR)
89     shutil.copy(os.path.join(TEMPLATES, 'idp.conf'), CONFDIR)
90     if not os.path.exists(args['httpd_conf']):
91         os.symlink(idp_conf, args['httpd_conf'])
92     # Load the cherrypy config from the newly installed file so
93     # that db paths and all is properly set before configuring
94     # components
95     cherrypy.config.update(ipsilon_conf)
96
97     # Move pre-existing admin db away
98     admin_db = cherrypy.config['admin.config.db']
99     if os.path.exists(admin_db):
100         shutil.move(admin_db, '%s.backup.%s' % (admin_db, now))
101
102     # Rebuild user db
103     users_db = cherrypy.config['user.prefs.db']
104     if os.path.exists(users_db):
105         shutil.move(users_db, '%s.backup.%s' % (users_db, now))
106     db = Store()
107     db.save_user_preferences(args['admin_user'], {'is_admin': 1})
108
109     logger.info('Configuring login managers')
110     for plugin_name in args['lm_order']:
111         plugin = plugins['Login Managers'][plugin_name]
112         plugin.configure(args)
113
114     logger.info('Configuring Authentication Providers')
115     for plugin_name in plugins['Auth Providers']:
116         plugin = plugins['Auth Providers'][plugin_name]
117         plugin.configure(args)
118
119
120 def uninstall(plugins, args):
121     logger.info('Uninstallation initiated')
122     raise Exception('Not Implemented')
123
124
125 def find_plugins():
126     plugins = {
127         'Login Managers': LoginMgrsInstall().plugins,
128         'Auth Providers': ProvidersInstall().plugins
129     }
130     return plugins
131
132
133 def parse_args(plugins):
134     parser = argparse.ArgumentParser(description='Ipsilon Install Options')
135     parser.add_argument('--version',
136                         action='version', version='%(prog)s 0.1')
137     parser.add_argument('-o', '--login-managers-order', dest='lm_order',
138                         help='Comma separated list of login managers')
139     parser.add_argument('--hostname',
140                         help="Machine's fully qualified host name")
141     parser.add_argument('--system-user', default='ipsilon',
142                         help="User account used to run the server")
143     parser.add_argument('--admin-user', default='admin',
144                         help="User account that is assigned admin privileges")
145     parser.add_argument('--ipa', choices=['yes', 'no'], default='yes',
146                         help='Detect and use an IPA server for authentication')
147     parser.add_argument('--uninstall', action='store_true',
148                         help="Uninstall the server and all data")
149
150     lms = []
151
152     for plugin_group in plugins:
153         group = parser.add_argument_group(plugin_group)
154         for plugin_name in plugins[plugin_group]:
155             plugin = plugins[plugin_group][plugin_name]
156             if plugin.ptype == 'login':
157                 lms.append(plugin.name)
158             plugin.install_args(group)
159
160     args = vars(parser.parse_args())
161
162     if not args['hostname']:
163         args['hostname'] = socket.getfqdn()
164
165     if len(args['hostname'].split('.')) < 2:
166         raise ConfigurationError('Hostname: %s is not a FQDN')
167
168     try:
169         pwd.getpwnam(args['system_user'])
170     except KeyError:
171         raise ConfigurationError('User: %s not found on the system')
172
173     if args['lm_order'] is None:
174         args['lm_order'] = []
175         for name in lms:
176             if args[name] == 'yes':
177                 args['lm_order'].append(name)
178     else:
179         args['lm_order'] = args['lm_order'].split(',')
180
181     if len(args['lm_order']) == 0:
182         #force the basic pam provider if nothing else is selected
183         if 'pam' not in args:
184             parser.print_help()
185             sys.exit(-1)
186         args['lm_order'] = ['pam']
187         args['pam'] = 'yes'
188
189     return args
190
191 if __name__ == '__main__':
192     opts = []
193     out = 0
194     openlogs()
195     try:
196         fplugins = find_plugins()
197         opts = parse_args(fplugins)
198
199         logger.setLevel(logging.DEBUG)
200
201         logger.info('Intallation arguments:')
202         for k in sorted(opts.iterkeys()):
203             logger.info('%s: %s', k, opts[k])
204
205         if 'uninstall' in opts and opts['uninstall'] is True:
206             uninstall(fplugins, opts)
207
208         install(fplugins, opts)
209     except Exception, e:  # pylint: disable=broad-except
210         logger.exception(e)
211         if 'uninstall' in opts and opts['uninstall'] is True:
212             print 'Uninstallation aborted.'
213         else:
214             print 'Installation aborted.'
215         print 'See log file %s for details' % LOGFILE
216         out = 1
217     finally:
218         if out == 0:
219             if 'uninstall' in opts and opts['uninstall'] is True:
220                 print 'Uninstallation complete.'
221             else:
222                 print 'Installation complete.'
223     sys.exit(out)