tuskar-ui/horizon/users.py
Paul McMillan 041b1c44c7 Fixes lp978896 -- Session fixation security fix
Rotates session tokens on logout, and properly clears sessions
to prevent data leakage.

Change-Id: I52d03576d07b1e023a7730857156d0da1887b1df
2012-05-04 16:26:18 -07:00

165 lines
5.3 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Classes and methods related to user handling in Horizon.
"""
import logging
from django.utils.translation import ugettext as _
from horizon import api
from horizon import exceptions
LOG = logging.getLogger(__name__)
def get_user_from_request(request):
""" Checks the current session and returns a :class:`~horizon.users.User`.
If the session contains user data the User will be treated as
authenticated and the :class:`~horizon.users.User` will have all
its attributes set.
If not, the :class:`~horizon.users.User` will have no attributes set.
If the session contains invalid data,
:exc:`~horizon.exceptions.NotAuthorized` will be raised.
"""
if 'user_id' not in request.session:
return User()
try:
return User(id=request.session['user_id'],
token=request.session['token'],
user=request.session['user_name'],
tenant_id=request.session['tenant_id'],
tenant_name=request.session['tenant'],
service_catalog=request.session['serviceCatalog'],
roles=request.session['roles'],
request=request)
except KeyError:
# If any of those keys are missing from the session it is
# overwhelmingly likely that we're dealing with an outdated session.
LOG.exception("Error while creating User from session.")
request.user_logout()
raise exceptions.NotAuthorized(_("Your session has expired. "
"Please log in again."))
class LazyUser(object):
def __get__(self, request, obj_type=None):
if not hasattr(request, '_cached_user'):
request._cached_user = get_user_from_request(request)
return request._cached_user
class User(object):
""" The main user class which Horizon expects.
.. attribute:: token
The id of the Keystone token associated with the current user/tenant.
.. attribute:: username
The name of the current user.
.. attribute:: tenant_id
The id of the Keystone tenant for the current user/token.
.. attribute:: tenant_name
The name of the Keystone tenant for the current user/token.
.. attribute:: service_catalog
The ``ServiceCatalog`` data returned by Keystone.
.. attribute:: roles
A list of dictionaries containing role names and ids as returned
by Keystone.
.. attribute:: admin
Boolean value indicating whether or not this user has admin
privileges. Internally mapped to :meth:`horizon.users.User.is_admin`.
"""
def __init__(self, id=None, token=None, user=None, tenant_id=None,
service_catalog=None, tenant_name=None, roles=None,
authorized_tenants=None, request=None):
self.id = id
self.token = token
self.username = user
self.tenant_id = tenant_id
self.tenant_name = tenant_name
self.service_catalog = service_catalog
self.roles = roles or []
self._authorized_tenants = authorized_tenants
# Store the request for lazy fetching of auth'd tenants
self._request = request
def is_authenticated(self):
"""
Evaluates whether this :class:`.User` instance has been authenticated.
Returns ``True`` or ``False``.
"""
# TODO: deal with token expiration
return self.token
@property
def admin(self):
return self.is_admin()
def is_admin(self):
"""
Evaluates whether this user has admin privileges. Returns
``True`` or ``False``.
"""
for role in self.roles:
if role['name'].lower() == 'admin':
return True
return False
def get_and_delete_messages(self):
"""
Placeholder function for parity with
``django.contrib.auth.models.User``.
"""
return []
@property
def authorized_tenants(self):
if self.is_authenticated() and self._authorized_tenants is None:
try:
token = self._request.session.get("unscoped_token", self.token)
authd = api.tenant_list_for_token(self._request, token)
except:
authd = []
LOG.exception('Could not retrieve tenant list.')
self._authorized_tenants = authd
return self._authorized_tenants
@authorized_tenants.setter
def authorized_tenants(self, tenant_list):
self._authorized_tenants = tenant_list