Add Krb configuration code
[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 /idp/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 /idp/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         keytab = '  # Krb5KeyTab - No Keytab provided'
128         if opts['krb_httpd_keytab'] is None:
129             if os.path.exists('/etc/httpd/conf/http.keytab'):
130                 keytab = '  Krb5KeyTab /etc/httpd/conf/http.keytab'
131         else:
132             if os.path.exists(opts['krb_httpd_keytab']):
133                 keytab = '  Krb5KeyTab %s' % opts['krb_httpd_keytab']
134             else:
135                 raise Exception('Keytab not found')
136
137         if opts['krb_realms'] is None:
138             realms = '  # KrbAuthRealms - Any trusted realm is allowed'
139         else:
140             realms = '  KrbAuthRealms %s' % opts['krb_realms']
141
142         tmpl = Template(CONF_TEMPLATE)
143         hunk = tmpl.substitute(keytab=keytab, realms=realms)
144         with open(opts['httpd_conf'], 'a') as httpd_conf:
145             httpd_conf.write(hunk)
146
147         # Add configuration data to database
148         po = PluginObject()
149         po.name = 'krb'
150         po.wipe_data()
151
152         # Update global config, put 'krb' always first
153         po.name = 'global'
154         globalconf = po.get_plugin_config(FACILITY)
155         if 'order' in globalconf:
156             order = globalconf['order'].split(',')
157         else:
158             order = []
159         order.insert(0, 'krb')
160         globalconf['order'] = ','.join(order)
161         po.set_config(globalconf)
162         po.save_plugin_config(FACILITY)