/
backend.py
120 lines (96 loc) · 4.67 KB
/
backend.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
118
119
120
""" Module defining the Django auth backend class for the Keystone API. """
import logging
from django.utils.translation import ugettext as _
from keystoneclient.v2_0 import client as keystone_client
from keystoneclient import exceptions as keystone_exceptions
from keystoneclient.v2_0.tokens import Token, TokenManager
from .exceptions import KeystoneAuthException
from .user import create_user_from_token
from .utils import check_token_expiration
LOG = logging.getLogger(__name__)
KEYSTONE_CLIENT_ATTR = "_keystoneclient"
class KeystoneBackend(object):
"""
Django authentication backend class for use with ``django.contrib.auth``.
"""
def check_auth_expiry(self, token):
if not check_token_expiration(token):
msg = _("The authentication token issued by the Identity service "
"has expired.")
LOG.warning("The authentication token issued by the Identity "
"service appears to have expired before it was "
"issued. This may indicate a problem with either your "
"server or client configuration.")
raise KeystoneAuthException(msg)
return True
def get_user(self, user_id):
"""
Returns the current user (if authenticated) based on the user ID
and session data.
Note: this required monkey-patching the ``contrib.auth`` middleware
to make the ``request`` object available to the auth backend class.
"""
if user_id == self.request.session["user_id"]:
token = Token(TokenManager(None),
self.request.session['token'],
loaded=True)
endpoint = self.request.session['region_endpoint']
return create_user_from_token(self.request, token, endpoint)
else:
return None
def authenticate(self, request=None, username=None, password=None,
tenant=None, auth_url=None):
""" Authenticates a user via the Keystone Identity API. """
LOG.debug('Beginning user authentication for user "%s".' % username)
try:
client = keystone_client.Client(username=username,
password=password,
tenant_id=tenant,
auth_url=auth_url)
unscoped_token_data = {"token": client.service_catalog.get_token()}
unscoped_token = Token(TokenManager(None),
unscoped_token_data,
loaded=True)
except keystone_exceptions.Unauthorized:
msg = _('Invalid user name or password.')
raise KeystoneAuthException(msg)
except keystone_exceptions.ClientException:
msg = _("An error occurred authenticating. "
"Please try again later.")
raise KeystoneAuthException(msg)
# Check expiry for our unscoped token.
self.check_auth_expiry(unscoped_token)
# FIXME: Log in to default tenant when the Keystone API returns it...
# For now we list all the user's tenants and iterate through.
try:
tenants = client.tenants.list()
except keystone_exceptions.ClientException:
msg = _('Unable to retrieve authorized projects.')
raise KeystoneAuthException(msg)
# Abort if there are no tenants for this user
if not tenants:
msg = _('You are not authorized for any projects.')
raise KeystoneAuthException(msg)
while tenants:
tenant = tenants.pop()
try:
token = client.tokens.authenticate(username=username,
token=unscoped_token.id,
tenant_id=tenant.id)
break
except keystone_exceptions.ClientException:
token = None
if token is None:
msg = _("Unable to authenticate to any available projects.")
raise KeystoneAuthException(msg)
# Check expiry for our new scoped token.
self.check_auth_expiry(token)
# If we made it here we succeeded. Create our User!
user = create_user_from_token(request, token, client.management_url)
if request is not None:
request.session['unscoped_token'] = unscoped_token.id
request.user = user
# Support client caching to save on auth calls.
setattr(request, KEYSTONE_CLIENT_ATTR, client)
LOG.debug('Authentication completed for user "%s".' % username)
return user