e426d2cca70a9510d487a33127169e4893317884
[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.util.plugin import PluginObject
22 from ipsilon.util.user import UserSession
23 from string import Template
24 import cherrypy
25 import os
26
27
28 class Krb(LoginPageBase):
29
30     def root(self, *args, **kwargs):
31         # Someone typed manually or a robot is walking th tree.
32         # Redirect to default page
33         return self.lm.redirect_to_path(self.lm.path)
34
35
36 class KrbAuth(LoginPageBase):
37
38     def root(self, *args, **kwargs):
39         trans = self.get_valid_transaction('login', **kwargs)
40         # If we can get here, we must be authenticated and remote_user
41         # was set. Check the session has a user set already or error.
42         us = UserSession()
43         us.remote_login()
44         self.user = us.get_user()
45         if not self.user.is_anonymous:
46             userdata = {'krb_principal_name': self.user.name}
47             return self.lm.auth_successful(trans, self.user.name,
48                                            'krb', userdata)
49         else:
50             return self.lm.auth_failed(trans)
51
52
53 class KrbError(LoginPageBase):
54
55     def root(self, *args, **kwargs):
56         cherrypy.log.error('REQUEST: %s' % cherrypy.request.headers)
57         # If we have no negotiate header return whatever mod_auth_kerb
58         # generated and wait for the next request
59
60         if 'WWW-Authenticate' not in cherrypy.request.headers:
61             cherrypy.response.status = 401
62
63             next_login = self.lm.next_login()
64             if next_login:
65                 return 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, *pargs):
121         self.name = 'krb'
122         self.ptype = 'login'
123         self.pargs = pargs
124
125     def install_args(self, group):
126         group.add_argument('--krb', choices=['yes', 'no'], default='no',
127                            help='Configure Kerberos authentication')
128         group.add_argument('--krb-realms',
129                            help='Allowed Kerberos Auth Realms')
130         group.add_argument('--krb-httpd-keytab',
131                            default='/etc/httpd/conf/http.keytab',
132                            help='Kerberos keytab location for HTTPD')
133
134     def configure(self, opts):
135         if opts['krb'] != 'yes':
136             return
137
138         confopts = {'instance': opts['instance']}
139
140         if os.path.exists(opts['krb_httpd_keytab']):
141             confopts['keytab'] = '  Krb5KeyTab %s' % opts['krb_httpd_keytab']
142         else:
143             raise Exception('Keytab not found')
144
145         if opts['krb_realms'] is None:
146             confopts['realms'] = '  # KrbAuthRealms - Any realm is allowed'
147         else:
148             confopts['realms'] = '  KrbAuthRealms %s' % opts['krb_realms']
149
150         tmpl = Template(CONF_TEMPLATE)
151         hunk = tmpl.substitute(**confopts)  # pylint: disable=star-args
152         with open(opts['httpd_conf'], 'a') as httpd_conf:
153             httpd_conf.write(hunk)
154
155         # Add configuration data to database
156         po = PluginObject(*self.pargs)
157         po.name = 'krb'
158         po.wipe_data()
159
160         # Update global config, put 'krb' always first
161         ph = self.pargs[0]
162         ph.refresh_enabled()
163         if 'krb' not in ph.enabled:
164             enabled = []
165             enabled.extend(ph.enabled)
166             enabled.insert(0, 'krb')
167             ph.save_enabled(enabled)