Change OIDC engine to not be federated
We don't need linking, just the ability to perform lookup
This commit is contained in:
parent
bc82edb2d1
commit
ed897c7cb0
8 changed files with 78 additions and 25 deletions
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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']);
|
||||||
|
|
Reference in a new issue