Skip to content

Commit

Permalink
Merge pull request #14 from pkit/devel
Browse files Browse the repository at this point in the history
fixed liteauth.py
  • Loading branch information
Constantine Peresypkin committed Mar 27, 2014
2 parents b3bed4f + 21187d8 commit b059256
Showing 1 changed file with 119 additions and 39 deletions.
158 changes: 119 additions & 39 deletions liteauth/liteauth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from urllib import quote
from time import time
from hashlib import md5
from uuid import uuid4
from liteauth.swauth_manager import get_data_from_url, store_data_in_url
from providers import load_oauth_provider

from swift.common.constraints import MAX_META_VALUE_LENGTH
Expand All @@ -14,7 +16,7 @@
from swift.common.swob import HTTPFound, Response, Request, \
HTTPUnauthorized, HTTPForbidden, HTTPInternalServerError, HTTPNotFound
from swift.common.utils import cache_from_env, get_logger, TRUE_VALUES, \
split_path
split_path, urlparse


def parse_lite_acl(acl_string):
Expand Down Expand Up @@ -143,37 +145,56 @@ def __init__(self, app, conf):
self.app = app
self.conf = conf
self.version = 'v1'
self.service_domain = conf.get('service_domain')
if not self.service_domain:
raise ValueError('service_domain not set in config file')
self.service_endpoint = conf.get('service_endpoint',
'https://' + self.service_domain)
self.auth_path = '/login/google/'
self.auth_endpoint = conf.get('auth_endpoint', '')
if not self.auth_endpoint:
raise ValueError('auth_endpoint not set in config file')
if isinstance(self.auth_endpoint, unicode):
self.auth_endpoint = self.auth_endpoint.encode('utf-8')
parsed_path = urlparse(self.auth_endpoint)
if not parsed_path.netloc:
raise ValueError('auth_endpoint is invalid in config file')
self.auth_domain = parsed_path.netloc
self.login_path = parsed_path.path
self.scheme = parsed_path.scheme
if self.scheme != 'https':
raise ValueError('auth_endpoint must have https:// scheme')
# by default service_domain can be extracted from the endpoint
# in case where auth domain is different from service domain
# you need to set up the service domain separately
# Example:
# auth_endpoint = https://auth.example.com/login
# service_domain = https://www.example.com
self.service_domain = conf.get('service_domain',
'%s://%s'
% (self.scheme, self.auth_domain))
self.logger = get_logger(conf, log_route='lite-auth')
self.log_headers = conf.get('log_headers', 'false').lower() \
in TRUE_VALUES
# try to refresh token
# when less than this amount of seconds left
self.refresh_before = conf.get('token_refresh_before', 60 * 29)
# url for whitelist objects
# Example: /v1/liteauth/whitelist
self.whitelist_url = conf.get('whitelist_url', '').lower().rstrip('/')
# url for invite objects
# Example: /v1/liteauth/invites
self.invite_url = conf.get('invite_url', '').lower().rstrip('/')
self.storage_driver = None
self.metadata_key = conf.get('metadata_key', 'userdata').lower()
self.redirect_url = '%s%s' % (self.service_endpoint, self.auth_path)
self.provider = load_oauth_provider(conf.get('oauth_provider', 'google_oauth'))
self.oauth_login_timeout = 3600

def __call__(self, env, start_response):
req = Request(env)
self.storage_driver = LiteAuthStorage(env)
if req.path.startswith(self.auth_path):
if req.path == self.login_path:
state = None
invite = None
if req.params:
code = req.params.get('code')
state = req.params.get('state')
invite = req.params.get('invite')
if code:
return self.do_google_login(req, code, state)(env, start_response)
return self.do_google_oauth(state)(env, start_response)
return self.do_google_oauth(state=state, invite=invite)(env, start_response)
token = req.headers.get('x-auth-token')
_start_response = start_response
if token:
Expand Down Expand Up @@ -226,11 +247,14 @@ def denied_response(self, req):
self.logger.increment('unauthorized')
return HTTPUnauthorized(request=req)

def do_google_oauth(self, state=None, approval_prompt='auto'):
def do_google_oauth(self, state=None, invite=None, approval_prompt='auto'):
uid = uuid4().hex
self.storage_driver.store_id(uid, (state or '/', invite),
self.oauth_login_timeout)
oauth_client = self.provider.create_for_redirect(
self.conf,
self.redirect_url,
state=state,
self.auth_endpoint,
state=uid,
approval_prompt=approval_prompt)
return HTTPFound(location=oauth_client.redirect)

Expand All @@ -240,15 +264,18 @@ def do_google_login(self, req, code, state=None):
headers={
'x-auth-token': 'logout',
'x-auth-token-expires': 0,
'x-storage-url': '%s/%s'
% (self.service_endpoint,
self.version),
'x-storage-url': self.auth_endpoint,
'location': '%s%s?account=logout'
% (self.service_endpoint, state)})
% (self.service_domain, state)})
req.response = resp
return resp
(stored_state, invite_code) = self.storage_driver.get_id(state)
if not stored_state:
req.response = HTTPUnauthorized(request=req,
body='Login time expired')
return req.response
oauth_client = self.provider.create_for_token(self.conf,
self.redirect_url,
self.auth_endpoint,
code)
token = oauth_client.access_token
if not token:
Expand All @@ -266,19 +293,70 @@ def do_google_login(self, req, code, state=None):
email = user_info.get('email', None)
if not email:
return HTTPForbidden()
whitelist_id = get_account_from_whitelist(
self.whitelist_url, self.app, email, self.logger, req.environ)
self.logger.info('Whitelist is %s for user: %s' % (whitelist_id, email))
if not whitelist_id:
return Response(request=req, status=402,
body='Account not in whitelist')
if whitelist_id.startswith('service_'):
req.environ['liteauth.new_service'] = \
whitelist_id.replace('service_', '', 1)
if not store_account_in_whitelist(self.whitelist_url,
self.app, email, account_id,
req.environ):
return HTTPInternalServerError()
whitelist_data = get_data_from_url(self.whitelist_url,
self.app,
email,
self.logger,
req.environ)
self.logger.info('Whitelist is %s for user: %s' % (whitelist_data, email))
if invite_code:
invite_data = get_data_from_url(self.invite_url,
self.app,
invite_code,
self.logger,
req.environ)
if not invite_data:
return HTTPFound(location='%s%s?error=invite'
% (self.service_domain, stored_state))
try:
invite_data = json.loads(invite_data)
except Exception:
return HTTPInternalServerError(request=req)
if not invite_data.get('service', None):
return HTTPInternalServerError(request=req)
if not invite_data.get('email', None):
invite_data['email'] = email
invite_data['user_id'] = account_id
if not store_data_in_url(self.invite_url,
self.app,
invite_code,
json.dumps(invite_data),
req.environ):
return HTTPInternalServerError(request=req)
if not store_data_in_url(self.whitelist_url,
self.app,
email,
json.dumps(invite_data),
req.environ):
return HTTPInternalServerError(request=req)
elif not whitelist_data and invite_data['email'] == email:
if not store_data_in_url(self.whitelist_url,
self.app,
email,
json.dumps(invite_data),
req.environ):
return HTTPInternalServerError(request=req)
else:
if whitelist_data:
try:
whitelist_data = json.loads(whitelist_data)
except Exception:
return HTTPInternalServerError(request=req)
if not whitelist_data or not isinstance(whitelist_data, dict):
return HTTPFound(location='%s%s?error=whitelist'
% (self.service_domain, stored_state))
if not whitelist_data.get('service', None):
return HTTPInternalServerError(request=req)
current_user = whitelist_data.get('user_id', None)
if not current_user:
whitelist_data['email'] = email
whitelist_data['user_id'] = account_id
if not store_data_in_url(self.whitelist_url,
self.app,
email,
json.dumps(whitelist_data),
req.environ):
return HTTPInternalServerError(request=req)
stored_info = retrieve_metadata(self.app,
self.version,
account_id,
Expand All @@ -287,7 +365,9 @@ def do_google_login(self, req, code, state=None):
if not stored_info:
rtoken = oauth_client.refresh_token
if not rtoken:
return self.do_google_oauth(state=state, approval_prompt='force')
return self.do_google_oauth(state=stored_state,
invite=invite_code,
approval_prompt='force')
user_info['rtoken'] = rtoken
user_info['hash__'] = md5(json.dumps(sorted(user_info.items()))).hexdigest()
if not store_metadata(self.app,
Expand Down Expand Up @@ -320,12 +400,12 @@ def do_google_login(self, req, code, state=None):
'x-auth-token': token,
'x-storage-token': token,
'x-storage-url': '%s/%s/%s'
% (self.service_endpoint,
% (self.service_domain,
self.version,
account_id),
'location': '%s%s?account=%s'
% (self.service_endpoint,
state or '/',
% (self.service_domain,
stored_state or '/',
account_id)})
req.response = resp
return resp
Expand All @@ -343,14 +423,14 @@ def refresh_token(self, env, account_id, expires):
if not rtoken:
return None
oauth_client = self.provider.create_for_refresh(self.conf,
self.redirect_url,
self.auth_endpoint,
rtoken)
headers = {
'X-Auth-Token': oauth_client.access_token,
'X-Auth-Token-Expires': oauth_client.expires_in,
'X-Storage-Token': oauth_client.access_token,
'X-Storage-Url': '%s/%s/%s'
% (self.service_endpoint,
% (self.service_domain,
self.version,
account_id),
}
Expand Down

0 comments on commit b059256

Please sign in to comment.