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