Merge "Implements REMOTE_USER authentication support."

This commit is contained in:
Jenkins 2012-11-01 20:10:33 +00:00 committed by Gerrit Code Review
commit 8ee6963506
3 changed files with 116 additions and 12 deletions

View File

@ -199,6 +199,10 @@ class Application(BaseApplication):
context = req.environ.get(CONTEXT_ENV, {})
context['query_string'] = dict(req.params.iteritems())
params = req.environ.get(PARAMS_ENV, {})
if 'REMOTE_USER' in req.environ:
context['REMOTE_USER'] = req.environ['REMOTE_USER']
elif context.get('REMOTE_USER', None) is not None:
del context['REMOTE_USER']
params.update(arg_dict)
# TODO(termie): do some basic normalization on methods

View File

@ -289,6 +289,18 @@ class TokenController(wsgi.Application):
raise exception.ValidationError(attribute='auth',
target='request body')
if auth is None:
auth = {}
remote_auth = False
if 'REMOTE_USER' in context and not 'token' in auth:
# authenticated external request
remote_auth = True
if 'passwordCredentials' not in auth:
auth['passwordCredentials'] = {}
auth['passwordCredentials']['username'] = context.get(
'REMOTE_USER', None)
if 'passwordCredentials' in auth:
user_id = auth['passwordCredentials'].get('userId', None)
username = auth['passwordCredentials'].get('username', '')
@ -300,6 +312,10 @@ class TokenController(wsgi.Application):
attribute='username or userId',
target='passwordCredentials')
tenant_ref = None
user_ref = None
metadata_ref = {}
if username:
try:
user_ref = self.identity_api.get_user_by_name(
@ -308,7 +324,7 @@ class TokenController(wsgi.Application):
except exception.UserNotFound:
raise exception.Unauthorized()
if not password:
if not password and not remote_auth:
raise exception.ValidationError(
attribute='password',
target='passwordCredentials')
@ -324,11 +340,30 @@ class TokenController(wsgi.Application):
raise exception.Unauthorized()
try:
auth_info = self.identity_api.authenticate(context=context,
user_id=user_id,
password=password,
tenant_id=tenant_id)
(user_ref, tenant_ref, metadata_ref) = auth_info
if not remote_auth:
# local identity authentication required
auth_info = self.identity_api.authenticate(
context=context,
user_id=user_id,
password=password,
tenant_id=tenant_id)
(user_ref, tenant_ref, metadata_ref) = auth_info
else:
# remote authentication already performed
if not user_ref:
user_ref = self.identity_api.get_user(
self.identity_api,
user_id)
if tenant_id:
if not tenant_ref:
tenant_ref = self.identity_api.get_tenant(
self.identity_api,
tenant_id)
metadata_ref = self.identity_api.get_metadata(
self.identity_api,
user_id,
tenant_id)
auth_info = (user_ref, tenant_ref, metadata_ref)
# If the user is disabled don't allow them to authenticate
if not user_ref.get('enabled', True):

View File

@ -12,10 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
import default_fixtures
from keystone import exception
from keystone import identity
from keystone import service
from keystone import test
from keystone.identity.backends import kvs as kvs_identity
class FakeIdentityManager(object):
@ -34,33 +37,95 @@ class TokenControllerTest(test.TestCase):
right exception."""
body_dict = {'passwordCredentials': {}, 'tenantName': 'demo'}
self.assertRaises(exception.ValidationError, self.api.authenticate,
None, body_dict)
{}, body_dict)
def test_authenticate_no_username(self):
"""Verify skipping username raises the right exception."""
body_dict = {'passwordCredentials': {'password': 'pass'},
'tenantName': 'demo'}
self.assertRaises(exception.ValidationError, self.api.authenticate,
None, body_dict)
{}, body_dict)
def test_authenticate_no_password(self):
"""Verify skipping password raises the right exception."""
body_dict = {'passwordCredentials': {'username': 'user1'},
'tenantName': 'demo'}
self.assertRaises(exception.ValidationError, self.api.authenticate,
None, body_dict)
{}, body_dict)
def test_authenticate_blank_request_body(self):
"""Verify sending empty json dict raises the right exception."""
self.assertRaises(exception.ValidationError, self.api.authenticate,
None, {})
{}, {})
def test_authenticate_blank_auth(self):
"""Verify sending blank 'auth' raises the right exception."""
self.assertRaises(exception.ValidationError, self.api.authenticate,
None, {'auth': {}})
{}, {'auth': {}})
def test_authenticate_invalid_auth_content(self):
"""Verify sending invalid 'auth' raises the right exception."""
self.assertRaises(exception.ValidationError, self.api.authenticate,
None, {'auth': 'abcd'})
{}, {'auth': 'abcd'})
class RemoteUserTest(test.TestCase):
def setUp(self):
super(RemoteUserTest, self).setUp()
self.identity_api = kvs_identity.Identity()
self.load_fixtures(default_fixtures)
self.api = service.TokenController()
def _build_user_auth(self, username, passwd, tenant):
auth_json = {'passwordCredentials': {}}
if username is not None:
auth_json['passwordCredentials']['username'] = username
if passwd is not None:
auth_json['passwordCredentials']['password'] = passwd
if tenant is not None:
auth_json['tenantName'] = tenant
return auth_json
def assertEqualTokens(self, a, b):
def normalize(token):
token['access']['token']['id'] = 'dummy'
# truncate to eliminate timing problems
issued = token['access']['token']['issued_at']
token['access']['token']['issued_at'] = issued[:-8]
# truncate to eliminate timing problems
expires = token['access']['token']['expires']
token['access']['token']['expires'] = expires[:-3]
return token
return self.assertDictEqual(normalize(a), normalize(b))
def test_unscoped_remote_authn(self):
local_token = self.api.authenticate(
{},
self._build_user_auth('FOO', 'foo2', None))
remote_token = self.api.authenticate(
{'REMOTE_USER': 'FOO'},
self._build_user_auth('FOO', 'nosir', None))
self.assertEqualTokens(local_token, remote_token)
def test_unscoped_remote_authn_jsonless(self):
self.assertRaises(
exception.ValidationError,
self.api.authenticate,
{'REMOTE_USER': 'FOO'},
None)
def test_scoped_remote_authn(self):
local_token = self.api.authenticate(
{},
self._build_user_auth('FOO', 'foo2', 'BAR'))
remote_token = self.api.authenticate(
{'REMOTE_USER': 'FOO'},
self._build_user_auth('FOO', 'nosir', 'BAR'))
self.assertEqualTokens(local_token, remote_token)
def test_scoped_remote_authn_invalid_user(self):
self.assertRaises(
exception.Unauthorized,
self.api.authenticate,
{'REMOTE_USER': 'FOOZBALL'},
self._build_user_auth('FOO', 'nosir', 'BAR'))