diff --git a/data/migrations/versions/2bf8af5bad95_add_keystone_login_service.py b/data/migrations/versions/2bf8af5bad95_add_keystone_login_service.py new file mode 100644 index 000000000..440e7ec98 --- /dev/null +++ b/data/migrations/versions/2bf8af5bad95_add_keystone_login_service.py @@ -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')) + ) + diff --git a/data/users.py b/data/users.py index 4b917162e..83b003401 100644 --- a/data/users.py +++ b/data/users.py @@ -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) diff --git a/initdb.py b/initdb.py index bb9a3c141..d0181ebca 100644 --- a/initdb.py +++ b/initdb.py @@ -205,6 +205,7 @@ def initialize_database(): LoginService.create(name='quayrobot') LoginService.create(name='ldap') LoginService.create(name='jwtauthn') + LoginService.create(name='keystone') BuildTriggerService.create(name='github') BuildTriggerService.create(name='custom-git') diff --git a/static/directives/config/config-setup-tool.html b/static/directives/config/config-setup-tool.html index b1c0c7da4..43bff96f3 100644 --- a/static/directives/config/config-setup-tool.html +++ b/static/directives/config/config-setup-tool.html @@ -353,12 +353,55 @@ + +
Keystone Authentication URL: | +
+
+
+ The URL (starting with http or https) of the Keystone Server endpoint for auth.
+
+ |
+
Keystone Administrator Username: | +
+
+
+ The username for the Keystone admin.
+
+ |
+
Keystone Administrator Password: | +
+
+
+ The password for the Keystone admin.
+
+ |
+
Keystone Administrator Tenant: | +
+
+
+ The tenant (project/group) that contains the administrator user.
+
+ |
+