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