Change OIDC engine to not be federated

We don't need linking, just the ability to perform lookup
This commit is contained in:
Joseph Schorr 2017-06-09 17:21:37 -04:00
parent bc82edb2d1
commit ed897c7cb0
8 changed files with 78 additions and 25 deletions

View file

@ -2,8 +2,8 @@ import sys
import os import os
sys.path.append(os.path.join(os.path.dirname(__file__), "../")) sys.path.append(os.path.join(os.path.dirname(__file__), "../"))
from util.log import logfile_path
from Crypto import Random from Crypto import Random
from util.log import logfile_path
logconfig = logfile_path(debug=True) logconfig = logfile_path(debug=True)

View file

@ -2,8 +2,8 @@ import sys
import os import os
sys.path.append(os.path.join(os.path.dirname(__file__), "../")) sys.path.append(os.path.join(os.path.dirname(__file__), "../"))
from util.log import logfile_path
from Crypto import Random from Crypto import Random
from util.log import logfile_path
logconfig = logfile_path(debug=False) logconfig = logfile_path(debug=False)

View file

@ -2,8 +2,8 @@ import sys
import os import os
sys.path.append(os.path.join(os.path.dirname(__file__), "../")) sys.path.append(os.path.join(os.path.dirname(__file__), "../"))
from util.log import logfile_path
from Crypto import Random from Crypto import Random
from util.log import logfile_path
logconfig = logfile_path(debug=False) logconfig = logfile_path(debug=False)

View file

@ -2,9 +2,8 @@ import sys
import os import os
sys.path.append(os.path.join(os.path.dirname(__file__), "../")) sys.path.append(os.path.join(os.path.dirname(__file__), "../"))
from util.log import logfile_path
from Crypto import Random from Crypto import Random
from util.log import logfile_path
logconfig = logfile_path(debug=False) logconfig = logfile_path(debug=False)

View file

@ -2,8 +2,8 @@ import sys
import os import os
sys.path.append(os.path.join(os.path.dirname(__file__), "../")) sys.path.append(os.path.join(os.path.dirname(__file__), "../"))
from util.log import logfile_path
from Crypto import Random from Crypto import Random
from util.log import logfile_path
logconfig = logfile_path(debug=False) logconfig = logfile_path(debug=False)

View file

@ -1,6 +1,6 @@
import logging import logging
from data.users.federated import FederatedUsers, UserInformation from data import model
from oauth.loginmanager import OAuthLoginManager from oauth.loginmanager import OAuthLoginManager
from oauth.oidc import PublicKeyLoadException from oauth.oidc import PublicKeyLoadException
from util.security.jwtutil import InvalidTokenError from util.security.jwtutil import InvalidTokenError
@ -13,29 +13,29 @@ class UnknownServiceException(Exception):
pass pass
class OIDCInternalAuth(FederatedUsers): class OIDCInternalAuth(object):
""" Handles authentication by delegating authentication to a signed OIDC JWT produced by the """ Handles authentication by delegating authentication to a signed OIDC JWT produced by the
configured OIDC service. configured OIDC service.
""" """
def __init__(self, config, login_service_id, requires_email): def __init__(self, config, login_service_id, requires_email):
super(OIDCInternalAuth, self).__init__('oidc', requires_email)
login_manager = OAuthLoginManager(config) login_manager = OAuthLoginManager(config)
self.login_service_id = login_service_id
self.login_service = login_manager.get_service(login_service_id) self.login_service = login_manager.get_service(login_service_id)
if self.login_service is None: if self.login_service is None:
raise UnknownServiceException('Unknown OIDC login service %s' % login_service_id) raise UnknownServiceException('Unknown OIDC login service %s' % login_service_id)
@property
def federated_service(self):
return None
@property @property
def supports_encrypted_credentials(self): def supports_encrypted_credentials(self):
# Since the "password" is already a signed JWT. # Since the "password" is already a signed JWT.
return False return False
def get_user(self, username_or_email):
return (None, 'Cannot retrieve users for OIDC')
def query_users(self, query, limit=20):
return (None, 'Cannot query users for OIDC')
def verify_credentials(self, username_or_email, id_token): def verify_credentials(self, username_or_email, id_token):
# Parse the ID token.
try: try:
payload = self.login_service.decode_user_jwt(id_token) payload = self.login_service.decode_user_jwt(id_token)
except InvalidTokenError as ite: except InvalidTokenError as ite:
@ -45,5 +45,39 @@ class OIDCInternalAuth(FederatedUsers):
logger.exception('Could not load public key during OIDC decode: %s', pke.message) logger.exception('Could not load public key during OIDC decode: %s', pke.message)
return (None, 'Could not validate OIDC token') return (None, 'Could not validate OIDC token')
user_info = UserInformation(username=payload['sub'], id=payload['sub'], email=None) # Find the user ID.
return (user_info, None) user_id = payload['sub']
# Lookup the federated login and user record with that matching ID and service.
user_found = model.user.verify_federated_login(self.login_service_id, user_id)
if user_found is None:
return (None, 'User does not exist')
if not user_found.enabled:
return (None, 'User account is disabled. Please contact your administrator.')
return (user_found, None)
def verify_and_link_user(self, username_or_email, password):
return self.verify_credentials(username_or_email, password)
def confirm_existing_user(self, username, password):
return self.verify_credentials(username, password)
def link_user(self, username_or_email):
return (None, 'Unsupported for this authentication system')
def get_and_link_federated_user_info(self, user_info):
return (None, 'Unsupported for this authentication system')
def query_users(self, query, limit):
return (None, '', '')
def check_group_lookup_args(self, group_lookup_args):
return (False, 'Not supported')
def iterate_group_members(self, group_lookup_args, page_size=None, disable_pagination=False):
return (None, 'Not supported')
def service_metadata(self):
return {}

View file

@ -1,19 +1,40 @@
import pytest
from httmock import HTTMock from httmock import HTTMock
from data import model
from data.users.oidc import OIDCInternalAuth from data.users.oidc import OIDCInternalAuth
from oauth.test.test_oidc import (id_token, oidc_service, signing_key, jwks_handler, from oauth.test.test_oidc import (id_token, oidc_service, signing_key, jwks_handler,
discovery_handler, app_config, http_client, discovery_handler, app_config, http_client,
discovery_content) discovery_content)
from test.fixtures import *
def test_oidc_login(app_config, id_token, jwks_handler, discovery_handler): @pytest.mark.parametrize('username, expect_success', [
('devtable', True),
('disabled', False)
])
def test_oidc_login(username, expect_success, app_config, id_token, jwks_handler,
discovery_handler, app):
internal_auth = OIDCInternalAuth(app_config, 'someoidc', False) internal_auth = OIDCInternalAuth(app_config, 'someoidc', False)
with HTTMock(jwks_handler, discovery_handler): with HTTMock(jwks_handler, discovery_handler):
# Try a valid token.
(user, err) = internal_auth.verify_credentials('someusername', id_token)
assert err is None
assert user.username == 'cooluser'
# Try an invalid token. # Try an invalid token.
(user, err) = internal_auth.verify_credentials('someusername', 'invalidtoken') (user, err) = internal_auth.verify_credentials('someusername', 'invalidtoken')
assert err is not None assert err is not None
assert user is None assert user is None
# Try a valid token for an unlinked user.
(user, err) = internal_auth.verify_credentials('someusername', id_token)
assert err is not None
assert user is None
# Link the user to the service.
model.user.attach_federated_login(model.user.get_user(username), 'someoidc', 'cooluser')
# Try a valid token for a linked user.
(user, err) = internal_auth.verify_credentials('someusername', id_token)
if expect_success:
assert err is None
assert user.username == username
else:
assert err is not None
assert user is None

View file

@ -237,11 +237,10 @@ import * as URI from 'urijs';
$scope.serializeDbUri = function(fields) { $scope.serializeDbUri = function(fields) {
if (!fields['server']) { return ''; } if (!fields['server']) { return ''; }
if (!fields['database']) { return ''; }
var uri = URI(); var uri = URI();
try { try {
if (!fields['server']) { return ''; }
if (!fields['database']) { return ''; }
uri = uri && uri.host(fields['server']); uri = uri && uri.host(fields['server']);
uri = uri && uri.protocol(fields['kind']); uri = uri && uri.protocol(fields['kind']);
uri = uri && uri.username(fields['username']); uri = uri && uri.username(fields['username']);