Commit 9c7b324e authored by Sofia Papagiannaki's avatar Sofia Papagiannaki
Browse files

pithos: Change pithos views authorization/authentication

Pithos views no longer use the information stored in
the PITHOS_ASTAKOS_COOKIE_NAME cookie
for authenticating the user and authorizing access to the
targeted resource.
They acquire, instead, from the authentication server (astakos)
a short-term token for accessing the specific resource.

The general flow includes the following steps:
1. The user clicks on a resource to view its content.
2. The view requests an authorisation code from astakos
   by providing its identifier, the requested scope,
   and a redirection URI.
3. Astakos authenticates the user and since the pithos view
   is considered a trusted client grants the view's access request.
4. Astakos redirects the user-agent back to the view using
   the redirection URI provided earlier.
   The redirection URI includes an authorisation code.
5. The view requests an access token from astakos
   by including the authorisation code.
   The view also posts a pair of credentials used to
   authenticate itself with astakos and the redirection URI
   used to obtain the authorisation code for verification.
6. Astakos authenticates the view, validates the authorization code,
   and ensures that the redirection URI received matches the URI
   used to redirect the client.
   If valid, astakos responds back with an short-term access token.
7. The view exchanges with astakos the access token
   for the user information to whom the authorisation was granted.
8. The view responses with the resource contents
   if the user has access to the specific resource.
parent b65e7a3b
......@@ -57,3 +57,8 @@
# It limits the maximum number of requests that pithos can serve.
# Extra requests will be blocked until another has completed.
#PITHOS_BACKEND_POOL_SIZE = 5
#
# Set the credentials (client_id, client_secret) issued to authenticate
# the views with astakos during the resource access token generation procedure
#OA2_CLIENT_CREDENTIALS = getattr(settings, 'PITHOS_OA2_CLIENT_CREDENTIALS',
# (None, None))
......@@ -58,6 +58,7 @@ fill_endpoints(pithos_services, BASE_URL)
PITHOS_PREFIX = get_path(pithos_services, 'pithos_object-store.prefix')
PUBLIC_PREFIX = get_path(pithos_services, 'pithos_public.prefix')
UI_PREFIX = get_path(pithos_services, 'pithos_ui.prefix')
VIEW_PREFIX = join_urls(UI_PREFIX, 'view')
COOKIE_NAME = getattr(settings, 'PITHOS_ASTAKOS_COOKIE_NAME', '_pithos2_a')
......@@ -182,3 +183,7 @@ BACKEND_BLOCK_SIZE = getattr(
# The backend block hash algorithm
BACKEND_HASH_ALGORITHM = getattr(
settings, 'PITHOS_BACKEND_HASH_ALGORITHM', 'sha256')
# Set the credentials (client_id, client_secret) issued to authenticate
# the views with astakos during the resource access token generation procedure
OA2_CLIENT_CREDENTIALS = getattr(settings, 'PITHOS_OA2_CLIENT_CREDENTIALS',
(None, None))
......@@ -40,7 +40,7 @@ from snf_django.lib.api.urls import api_patterns
from snf_django.lib.api import api_endpoint_not_found
from snf_django.utils.urls import extend_endpoint_with_slash
from pithos.api.settings import (
BASE_PATH, PITHOS_PREFIX, PUBLIC_PREFIX, UI_PREFIX,
BASE_PATH, PITHOS_PREFIX, PUBLIC_PREFIX, VIEW_PREFIX,
ASTAKOS_AUTH_PROXY_PATH, ASTAKOS_AUTH_URL,
ASTAKOS_ACCOUNT_PROXY_PATH, ASTAKOS_ACCOUNT_URL,
ASTAKOS_UI_PROXY_PATH, ASTAKOS_UI_URL,
......@@ -65,8 +65,8 @@ pithos_api_patterns = api_patterns(
pithos_view_patterns = patterns(
'pithos.api.views',
(r'^view/(?P<v_account>.+?)/(?P<v_container>.+?)/(?P<v_object>.+?)$',
'object_demux'))
(r'^(?P<v_account>.+?)/(?P<v_container>.+?)/(?P<v_object>.+?)$',
'object_read'))
pithos_patterns = patterns(
'',
......@@ -77,6 +77,7 @@ pithos_patterns = patterns(
(r'{0}(?P<v_public>.+?)/?$'.format(prefix_pattern(PUBLIC_PREFIX)),
'pithos.api.public.public_demux'),
(r'{0}'.format(prefix_pattern(UI_PREFIX)),
(r'{0}'.format(prefix_pattern(VIEW_PREFIX)),
include(pithos_view_patterns)))
urlpatterns += patterns(
......
# Copyright 2011-2012 GRNET S.A. All rights reserved.
# Copyright 2011-2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -33,10 +33,9 @@
from functools import wraps
from datetime import datetime
from urllib import quote, unquote
from urllib import quote, unquote, urlencode
from django.http import (HttpResponse, HttpResponseRedirect, Http404,
HttpResponseForbidden)
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.template.loader import render_to_string
from django.utils import simplejson as json
from django.utils.http import http_date, parse_etags
......@@ -44,6 +43,7 @@ from django.utils.encoding import smart_unicode, smart_str
from django.core.files.uploadhandler import FileUploadHandler
from django.core.files.uploadedfile import UploadedFile
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied
from snf_django.lib.api.parsedate import parse_http_date_safe, parse_http_date
from snf_django.lib import api
......@@ -65,7 +65,8 @@ from pithos.api.settings import (BACKEND_DB_MODULE, BACKEND_DB_CONNECTION,
RADOS_STORAGE, RADOS_POOL_BLOCKS,
RADOS_POOL_MAPS, TRANSLATE_UUIDS,
PUBLIC_URL_SECURITY, PUBLIC_URL_ALPHABET,
COOKIE_NAME, BASE_HOST, UPDATE_MD5, LOGIN_URL)
BASE_HOST, UPDATE_MD5, VIEW_PREFIX,
OA2_CLIENT_CREDENTIALS)
from pithos.api.resources import resources
from pithos.backends import connect_backend
......@@ -75,7 +76,7 @@ from pithos.backends.base import (NotAllowedError, QuotaError, ItemNotExists,
from synnefo.lib import join_urls
from astakosclient import AstakosClient
from astakosclient.errors import NoUserName, NoUUID
from astakosclient.errors import NoUserName, NoUUID, AstakosClientException
import logging
import re
......@@ -1119,12 +1120,16 @@ def api_method(http_method=None, token_required=True, user_required=True,
return decorator
def get_token_from_cookie(request):
token = None
if COOKIE_NAME in request.COOKIES:
cookie_value = unquote(request.COOKIES.get(COOKIE_NAME, ''))
account, sep, token = cookie_value.partition('|')
return token
def request_oa2_token(request, client, client_credentials, redirect_uri,
**kwargs):
"""
:raises: AstakosClientException, ValueError
"""
data = client.get_token('authorization_code', *client_credentials,
redirect_uri=redirect_uri, **kwargs)
params = {'access_token': data.get('access_token', '')}
return HttpResponseRedirect('%s?%s' % (redirect_uri,
urlencode(params)))
def view_method():
......@@ -1133,18 +1138,49 @@ def view_method():
def decorator(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
token = get_token_from_cookie(request)
if token is None:
return HttpResponseRedirect('%s?next=%s' % (
LOGIN_URL, join_urls(BASE_HOST, request.path)))
request.META['HTTP_X_AUTH_TOKEN'] = token
# Get the response object
response = func(request, *args, **kwargs)
if response.status_code == 404:
raise Http404()
elif response.status_code in [401, 403]:
return HttpResponseForbidden()
return response
try:
access_token = request.GET.get('access_token')
requested_resource = request.path.split(VIEW_PREFIX, 2)[-1]
astakos = AstakosClient(SERVICE_TOKEN, ASTAKOS_AUTH_URL,
retry=2, use_pool=True,
logger=logger)
if access_token is not None:
# authenticate using the temporary access token
request.user = astakos.validate_token(access_token,
requested_resource)
request.user_uniq = request.user["access"]["user"]["id"]
response = func(request, *args, **kwargs)
if response.status_code == 404:
raise Http404
elif response.status_code in [401, 403]:
raise PermissionDenied
return response
client_id, client_secret = OA2_CLIENT_CREDENTIALS
# TODO: check if client credentials are not set
authorization_code = request.GET.get('code')
if authorization_code is None:
params = {'response_type': 'code',
'client_id': client_id,
'redirect_uri':
request.build_absolute_uri(request.path),
'scope': request.path.split(VIEW_PREFIX, 2)[-1],
'state': '' # TODO include state for security
}
return HttpResponseRedirect('%s?%s' %
(astakos.api_oa2_auth,
urlencode(params)))
else:
redirect_uri = join_urls(BASE_HOST, request.path)
return request_oa2_token(request,
astakos,
OA2_CLIENT_CREDENTIALS,
redirect_uri=redirect_uri,
scope=requested_resource,
code=authorization_code)
except AstakosClientException:
raise PermissionDenied
return wrapper
return decorator
......
......@@ -51,6 +51,7 @@ def object_demux(request, v_account, v_container, v_object):
@view_method()
@api_method('GET', format_allowed=True, user_required=True, logger=logger)
@api_method('GET', format_allowed=True, token_required=False,
user_required=False, logger=logger)
def object_read(request, v_account, v_container, v_object):
return _object_read(request, v_account, v_container, v_object)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment