c67b93b82ff34ece285033552f75020f0b84d4e9
[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         return self.page
86
87
88 CONF_TEMPLATE = """
89
90 <Location /${instance}/login/krb/negotiate>
91   AuthType Kerberos
92   AuthName "Kerberos Login"
93   KrbMethodNegotiate on
94   KrbMethodK5Passwd off
95   KrbServiceName HTTP
96   $realms
97   $keytab
98   KrbSaveCredentials off
99   KrbConstrainedDelegation off
100   # KrbLocalUserMapping On
101   Require valid-user
102
103   ErrorDocument 401 /${instance}/login/krb/unauthorized
104 </Location>
105 """
106
107
108 class Installer(object):
109
110     def __init__(self):
111         self.name = 'krb'
112         self.ptype = 'login'
113
114     def install_args(self, group):
115         group.add_argument('--krb', choices=['yes', 'no'], default='no',
116                            help='Configure Kerberos authentication')
117         group.add_argument('--krb-realms',
118                            help='Allowed Kerberos Auth Realms')
119         group.add_argument('--krb-httpd-keytab',
120                            default='/etc/httpd/conf/http.keytab',
121                            help='Kerberos keytab location for HTTPD')
122
123     def configure(self, opts):
124         if opts['krb'] != 'yes':
125             return
126
127         confopts = {'instance': opts['instance']}
128
129         if os.path.exists(opts['krb_httpd_keytab']):
130             confopts['keytab'] = '  Krb5KeyTab %s' % opts['krb_httpd_keytab']
131         else:
132             raise Exception('Keytab not found')
133
134         if opts['krb_realms'] is None:
135             confopts['realms'] = '  # KrbAuthRealms - Any realm is allowed'
136         else:
137             confopts['realms'] = '  KrbAuthRealms %s' % opts['krb_realms']
138
139         tmpl = Template(CONF_TEMPLATE)
140         hunk = tmpl.substitute(**confopts)  # pylint: disable=star-args
141         with open(opts['httpd_conf'], 'a') as httpd_conf:
142             httpd_conf.write(hunk)
143
144         # Add configuration data to database
145         po = PluginObject()
146         po.name = 'krb'
147         po.wipe_data()
148
149         # Update global config, put 'krb' always first
150         po.name = 'global'
151         globalconf = po.get_plugin_config(FACILITY)
152         if 'order' in globalconf:
153             order = globalconf['order'].split(',')
154         else:
155             order = []
156         order.insert(0, 'krb')
157         globalconf['order'] = ','.join(order)
158         po.set_config(globalconf)
159         po.save_plugin_config(FACILITY)