Basic Keystone Auth support

Note: This has been verified as working by the end customer
This commit is contained in:
Joseph Schorr 2015-07-13 12:34:32 +03:00
parent eb612d606c
commit 066637f496
6 changed files with 151 additions and 1 deletions

View file

@ -0,0 +1,26 @@
"""Add keystone login service
Revision ID: 2bf8af5bad95
Revises: 154f2befdfbe
Create Date: 2015-06-29 21:19:13.053165
"""
# revision identifiers, used by Alembic.
revision = '2bf8af5bad95'
down_revision = '154f2befdfbe'
from alembic import op
import sqlalchemy as sa
def upgrade(tables):
op.bulk_insert(tables.loginservice, [{'id': 6, 'name': 'keystone'}])
def downgrade(tables):
op.execute(
tables.loginservice.delete()
.where(tables.loginservice.c.name == op.inline_literal('keystone'))
)

View file

@ -8,6 +8,9 @@ import jwt
from collections import namedtuple
from datetime import datetime, timedelta
from keystoneclient.v2_0 import client as kclient
from keystoneclient.exceptions import AuthorizationFailure as KeystoneAuthorizationFailure
from keystoneclient.exceptions import Unauthorized as KeystoneUnauthorized
import features
@ -53,6 +56,37 @@ def _get_federated_user(username, email, federated_service, create_new_user):
return (db_user, None)
class KeystoneUsers(object):
""" Delegates authentication to OpenStack Keystone. """
def __init__(self, auth_url, admin_username, admin_password, admin_tenant):
self.auth_url = auth_url
self.admin_username = admin_username
self.admin_password = admin_password
self.admin_tenant = admin_tenant
def verify_user(self, username_or_email, password, create_new_user=True):
try:
keystone_client = kclient.Client(username=username_or_email, password=password,
auth_url=self.auth_url)
user_id = keystone_client.user_id
except KeystoneAuthorizationFailure as kaf:
logger.exception('Keystone auth failure for user: %s', username_or_email)
return (None, kaf.message or 'Invalid username or password')
except KeystoneUnauthorized as kut:
logger.exception('Keystone unauthorized for user: %s', username_or_email)
return (None, kut.message or 'Invalid username or password')
try:
admin_client = kclient.Client(username=self.admin_username, password=self.admin_password,
tenant_name=self.admin_tenant, auth_url=self.auth_url)
user = admin_client.users.get(user_id)
except KeystoneUnauthorized as kut:
logger.exception('Keystone unauthorized admin')
return (None, 'Keystone admin credentials are invalid: %s' % kut.message)
return _get_federated_user(username_or_email, user.email, 'keystone', create_new_user)
class ExternalJWTAuthN(object):
""" Delegates authentication to a REST endpoint that returns JWTs. """
PUBLIC_KEY_FILENAME = 'jwt-authn.cert'
@ -336,6 +370,13 @@ class UserAuthentication(object):
max_fresh_s = app.config.get('JWT_AUTH_MAX_FRESH_S', 300)
users = ExternalJWTAuthN(verify_url, issuer, override_config_dir, max_fresh_s,
app.config['HTTPCLIENT'])
elif authentication_type == 'Keystone':
auth_url = app.config.get('KEYSTONE_AUTH_URL')
keystone_admin_username = app.config.get('KEYSTONE_ADMIN_USERNAME')
keystone_admin_password = app.config.get('KEYSTONE_ADMIN_PASSWORD')
keystone_admin_tenant = app.config.get('KEYSTONE_ADMIN_TENANT')
users = KeystoneUsers(auth_url, keystone_admin_username, keystone_admin_password,
keystone_admin_tenant)
else:
raise RuntimeError('Unknown authentication type: %s' % authentication_type)