-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathflask_kerberos.py
executable file
·117 lines (100 loc) · 3.68 KB
/
flask_kerberos.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import kerberos
from flask import Response
from flask import _request_ctx_stack as stack
from flask import make_response
from flask import request
from functools import wraps
from socket import gethostname
from os import environ
_SERVICE_NAME = None
def init_kerberos(app, service='HTTP', hostname=gethostname()):
'''
Configure the GSSAPI service name, and validate the presence of the
appropriate principal in the kerberos keytab.
:param app: a flask application
:type app: flask.Flask
:param service: GSSAPI service name
:type service: str
:param hostname: hostname the service runs under
:type hostname: str
'''
global _SERVICE_NAME
_SERVICE_NAME = "%s@%s" % (service, hostname)
if 'KRB5_KTNAME' not in environ:
app.logger.warn("Kerberos: set KRB5_KTNAME to your keytab file")
else:
try:
principal = kerberos.getServerPrincipalDetails(service, hostname)
except kerberos.KrbError as exc:
app.logger.warn("Kerberos: %s" % exc.message[0])
else:
app.logger.info("Kerberos: server is %s" % principal)
def _unauthorized():
'''
Indicate that authentication is required
'''
return Response('Unauthorized', 401, {'WWW-Authenticate': 'Negotiate'})
def _forbidden():
'''
Indicate a complete authentication failure
'''
return Response('Forbidden', 403)
def _gssapi_authenticate(token):
'''
Performs GSSAPI Negotiate Authentication
On success also stashes the server response token for mutual authentication
at the top of request context with the name kerberos_token, along with the
authenticated user principal with the name kerberos_user.
@param token: GSSAPI Authentication Token
@type token: str
@returns gssapi return code or None on failure
@rtype: int or None
'''
state = None
ctx = stack.top
try:
rc, state = kerberos.authGSSServerInit(_SERVICE_NAME)
if rc != kerberos.AUTH_GSS_COMPLETE:
return None
rc = kerberos.authGSSServerStep(state, token)
if rc == kerberos.AUTH_GSS_COMPLETE:
ctx.kerberos_token = kerberos.authGSSServerResponse(state)
ctx.kerberos_user = kerberos.authGSSServerUserName(state)
return rc
elif rc == kerberos.AUTH_GSS_CONTINUE:
return kerberos.AUTH_GSS_CONTINUE
else:
return None
except kerberos.GSSError:
return None
finally:
if state:
kerberos.authGSSServerClean(state)
def requires_authentication(function):
'''
Require that the wrapped view function only be called by users
authenticated with Kerberos. The view function will have the authenticated
users principal passed to it as its first argument.
:param function: flask view function
:type function: function
:returns: decorated function
:rtype: function
'''
@wraps(function)
def decorated(*args, **kwargs):
header = request.headers.get("Authorization")
if header:
ctx = stack.top
token = ''.join(header.split()[1:])
rc = _gssapi_authenticate(token)
if rc == kerberos.AUTH_GSS_COMPLETE:
response = function(ctx.kerberos_user, *args, **kwargs)
response = make_response(response)
if ctx.kerberos_token is not None:
response.headers['WWW-Authenticate'] = ' '.join(['negotiate',
ctx.kerberos_token])
return response
elif rc != kerberos.AUTH_GSS_CONTINUE:
return _forbidden()
return _unauthorized()
return decorated