Fix file permissions and remove shebang's
[cascardo/ipsilon.git] / ipsilon / login / authkrb.py
1 # Copyright (C) 2014  Simo Sorce <simo@redhat.com>
2 #
3 # see file 'COPYING' for use and warranty information
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 from ipsilon.login.common import LoginPageBase, LoginManagerBase
19 from ipsilon.util.plugin import PluginObject
20 from ipsilon.util.user import UserSession
21 from string import Template
22 import cherrypy
23 import os
24
25
26 class Krb(LoginPageBase):
27
28     def root(self, *args, **kwargs):
29         # Someone typed manually or a robot is walking th tree.
30         # Redirect to default page
31         return self.lm.redirect_to_path(self.lm.path)
32
33
34 class KrbAuth(LoginPageBase):
35
36     def root(self, *args, **kwargs):
37         trans = self.get_valid_transaction('login', **kwargs)
38         # If we can get here, we must be authenticated and remote_user
39         # was set. Check the session has a user set already or error.
40         us = UserSession()
41         us.remote_login()
42         self.user = us.get_user()
43         if not self.user.is_anonymous:
44             userdata = {'krb_principal_name': self.user.name}
45             return self.lm.auth_successful(trans, self.user.name,
46                                            'krb', userdata)
47         else:
48             return self.lm.auth_failed(trans)
49
50
51 class KrbError(LoginPageBase):
52
53     def root(self, *args, **kwargs):
54         cherrypy.log.error('REQUEST: %s' % cherrypy.request.headers)
55         # If we have no negotiate header return whatever mod_auth_kerb
56         # generated and wait for the next request
57
58         if 'WWW-Authenticate' not in cherrypy.request.headers:
59             cherrypy.response.status = 401
60
61             next_login = self.lm.next_login()
62             if next_login:
63                 return next_login.page.root(*args, **kwargs)
64
65             conturl = '%s/login' % self.basepath
66             return self._template('login/krb.html',
67                                   title='Kerberos Login',
68                                   cont=conturl)
69
70         # If we get here, negotiate failed
71         trans = self.get_valid_transaction('login', **kwargs)
72         return self.lm.auth_failed(trans)
73
74
75 class LoginManager(LoginManagerBase):
76
77     def __init__(self, *args, **kwargs):
78         super(LoginManager, self).__init__(*args, **kwargs)
79         self.name = 'krb'
80         self.path = 'krb/negotiate'
81         self.page = None
82         self.description = """
83 Kereros Negotiate authentication plugin. Relies on the mod_auth_kerb apache
84 plugin for actual authentication. """
85         self.new_config(self.name)
86
87     def get_tree(self, site):
88         self.page = Krb(site, self)
89         self.page.__dict__['negotiate'] = KrbAuth(site, self)
90         self.page.__dict__['unauthorized'] = KrbError(site, self)
91         self.page.__dict__['failed'] = KrbError(site, self)
92         return self.page
93
94
95 CONF_TEMPLATE = """
96
97 <Location /${instance}/login/krb/negotiate>
98   AuthType Kerberos
99   AuthName "Kerberos Login"
100   KrbMethodNegotiate on
101   KrbMethodK5Passwd off
102   KrbServiceName HTTP
103   $realms
104   $keytab
105   KrbSaveCredentials off
106   KrbConstrainedDelegation off
107   # KrbLocalUserMapping On
108   Require valid-user
109
110   ErrorDocument 401 /${instance}/login/krb/unauthorized
111   ErrorDocument 500 /${instance}/login/krb/failed
112 </Location>
113 """
114
115
116 class Installer(object):
117
118     def __init__(self, *pargs):
119         self.name = 'krb'
120         self.ptype = 'login'
121         self.pargs = pargs
122
123     def install_args(self, group):
124         group.add_argument('--krb', choices=['yes', 'no'], default='no',
125                            help='Configure Kerberos authentication')
126         group.add_argument('--krb-realms',
127                            help='Allowed Kerberos Auth Realms')
128         group.add_argument('--krb-httpd-keytab',
129                            default='/etc/httpd/conf/http.keytab',
130                            help='Kerberos keytab location for HTTPD')
131
132     def configure(self, opts):
133         if opts['krb'] != 'yes':
134             return
135
136         confopts = {'instance': opts['instance']}
137
138         if os.path.exists(opts['krb_httpd_keytab']):
139             confopts['keytab'] = '  Krb5KeyTab %s' % opts['krb_httpd_keytab']
140         else:
141             raise Exception('Keytab not found')
142
143         if opts['krb_realms'] is None:
144             confopts['realms'] = '  # KrbAuthRealms - Any realm is allowed'
145         else:
146             confopts['realms'] = '  KrbAuthRealms %s' % opts['krb_realms']
147
148         tmpl = Template(CONF_TEMPLATE)
149         hunk = tmpl.substitute(**confopts)  # pylint: disable=star-args
150         with open(opts['httpd_conf'], 'a') as httpd_conf:
151             httpd_conf.write(hunk)
152
153         # Add configuration data to database
154         po = PluginObject(*self.pargs)
155         po.name = 'krb'
156         po.wipe_data()
157
158         # Update global config, put 'krb' always first
159         ph = self.pargs[0]
160         ph.refresh_enabled()
161         if 'krb' not in ph.enabled:
162             enabled = []
163             enabled.extend(ph.enabled)
164             enabled.insert(0, 'krb')
165             ph.save_enabled(enabled)