Refactor plugin configuration
[cascardo/ipsilon.git] / ipsilon / login / authkrb.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 LoginPageBase, LoginManagerBase
21 from ipsilon.login.common import FACILITY
22 from ipsilon.util.plugin import PluginObject
23 from ipsilon.util.user import UserSession
24 from string import Template
25 import cherrypy
26 import os
27
28
29 class Krb(LoginPageBase):
30
31     def root(self, *args, **kwargs):
32         # Someone typed manually or a robot is walking th tree.
33         # Redirect to default page
34         return self.lm.redirect_to_path(self.lm.path)
35
36
37 class KrbAuth(LoginPageBase):
38
39     def root(self, *args, **kwargs):
40         trans = self.get_valid_transaction('login', **kwargs)
41         # If we can get here, we must be authenticated and remote_user
42         # was set. Check the session has a user set already or error.
43         us = UserSession()
44         us.remote_login()
45         self.user = us.get_user()
46         if not self.user.is_anonymous:
47             userdata = {'krb_principal_name': self.user.name}
48             return self.lm.auth_successful(trans, self.user.name,
49                                            'krb', userdata)
50         else:
51             return self.lm.auth_failed(trans)
52
53
54 class KrbError(LoginPageBase):
55
56     def root(self, *args, **kwargs):
57         cherrypy.log.error('REQUEST: %s' % cherrypy.request.headers)
58         # If we have no negotiate header return whatever mod_auth_kerb
59         # generated and wait for the next request
60
61         if 'WWW-Authenticate' not in cherrypy.request.headers:
62             cherrypy.response.status = 401
63
64             if self.lm.next_login:
65                 return self.lm.next_login.page.root(*args, **kwargs)
66
67             conturl = '%s/login' % self.basepath
68             return self._template('login/krb.html',
69                                   title='Kerberos Login',
70                                   cont=conturl)
71
72         # If we get here, negotiate failed
73         trans = self.get_valid_transaction('login', **kwargs)
74         return self.lm.auth_failed(trans)
75
76
77 class LoginManager(LoginManagerBase):
78
79     def __init__(self, *args, **kwargs):
80         super(LoginManager, self).__init__(*args, **kwargs)
81         self.name = 'krb'
82         self.path = 'krb/negotiate'
83         self.page = None
84         self.description = """
85 Kereros Negotiate authentication plugin. Relies on the mod_auth_kerb apache
86 plugin for actual authentication. """
87         self.new_config(self.name)
88
89     def get_tree(self, site):
90         self.page = Krb(site, self)
91         self.page.__dict__['negotiate'] = KrbAuth(site, self)
92         self.page.__dict__['unauthorized'] = KrbError(site, self)
93         self.page.__dict__['failed'] = KrbError(site, self)
94         return self.page
95
96
97 CONF_TEMPLATE = """
98
99 <Location /${instance}/login/krb/negotiate>
100   AuthType Kerberos
101   AuthName "Kerberos Login"
102   KrbMethodNegotiate on
103   KrbMethodK5Passwd off
104   KrbServiceName HTTP
105   $realms
106   $keytab
107   KrbSaveCredentials off
108   KrbConstrainedDelegation off
109   # KrbLocalUserMapping On
110   Require valid-user
111
112   ErrorDocument 401 /${instance}/login/krb/unauthorized
113   ErrorDocument 500 /${instance}/login/krb/failed
114 </Location>
115 """
116
117
118 class Installer(object):
119
120     def __init__(self):
121         self.name = 'krb'
122         self.ptype = 'login'
123
124     def install_args(self, group):
125         group.add_argument('--krb', choices=['yes', 'no'], default='no',
126                            help='Configure Kerberos authentication')
127         group.add_argument('--krb-realms',
128                            help='Allowed Kerberos Auth Realms')
129         group.add_argument('--krb-httpd-keytab',
130                            default='/etc/httpd/conf/http.keytab',
131                            help='Kerberos keytab location for HTTPD')
132
133     def configure(self, opts):
134         if opts['krb'] != 'yes':
135             return
136
137         confopts = {'instance': opts['instance']}
138
139         if os.path.exists(opts['krb_httpd_keytab']):
140             confopts['keytab'] = '  Krb5KeyTab %s' % opts['krb_httpd_keytab']
141         else:
142             raise Exception('Keytab not found')
143
144         if opts['krb_realms'] is None:
145             confopts['realms'] = '  # KrbAuthRealms - Any realm is allowed'
146         else:
147             confopts['realms'] = '  KrbAuthRealms %s' % opts['krb_realms']
148
149         tmpl = Template(CONF_TEMPLATE)
150         hunk = tmpl.substitute(**confopts)  # pylint: disable=star-args
151         with open(opts['httpd_conf'], 'a') as httpd_conf:
152             httpd_conf.write(hunk)
153
154         # Add configuration data to database
155         po = PluginObject()
156         po.name = 'krb'
157         po.wipe_data()
158
159         # Update global config, put 'krb' always first
160         po.name = 'global'
161         globalconf = po.get_plugin_config(FACILITY)
162         if 'order' in globalconf:
163             order = globalconf['order'].split(',')
164         else:
165             order = []
166         order.insert(0, 'krb')
167         globalconf['order'] = ','.join(order)
168         po.save_plugin_config(FACILITY, globalconf)