Merge pull request #3089 from quay/joseph.schorr/QUAY-910/team-sync-disabled

Allow team syncing if user creation is disabled
This commit is contained in:
Joseph Schorr 2018-05-22 15:24:47 -04:00 committed by GitHub
commit a875eac350
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 17 deletions

View file

@ -211,11 +211,14 @@ class UserAuthentication(object):
""" """
return self.state.link_user(username_or_email) return self.state.link_user(username_or_email)
def get_and_link_federated_user_info(self, user_info): def get_and_link_federated_user_info(self, user_info, internal_create=False):
""" Returns a tuple containing the database user record linked to the given UserInformation """ Returns a tuple containing the database user record linked to the given UserInformation
pair and any error that occurred when trying to link the user. pair and any error that occurred when trying to link the user.
If `internal_create` is True, the caller is an internal user creation process (such
as team syncing), and the "can a user be created" check will be bypassed.
""" """
return self.state.get_and_link_federated_user_info(user_info) return self.state.get_and_link_federated_user_info(user_info, internal_create=internal_create)
def confirm_existing_user(self, username, password): def confirm_existing_user(self, username, password):
""" Verifies that the given password matches to the given DB username. Unlike """ Verifies that the given password matches to the given DB username. Unlike

View file

@ -41,7 +41,7 @@ class DatabaseUsers(object):
""" Never used since all users being added are already, by definition, in the database. """ """ Never used since all users being added are already, by definition, in the database. """
return (None, 'Unsupported for this authentication system') return (None, 'Unsupported for this authentication system')
def get_and_link_federated_user_info(self, user_info): def get_and_link_federated_user_info(self, user_info, internal_create=False):
""" Never used since all users being added are already, by definition, in the database. """ """ Never used since all users being added are already, by definition, in the database. """
return (None, 'Unsupported for this authentication system') return (None, 'Unsupported for this authentication system')

View file

@ -60,8 +60,9 @@ class FederatedUsers(object):
return self.get_and_link_federated_user_info(user_info) return self.get_and_link_federated_user_info(user_info)
def get_and_link_federated_user_info(self, user_info): def get_and_link_federated_user_info(self, user_info, internal_create=False):
return self._get_and_link_federated_user_info(user_info.username, user_info.email) return self._get_and_link_federated_user_info(user_info.username, user_info.email,
internal_create=internal_create)
def verify_and_link_user(self, username_or_email, password): def verify_and_link_user(self, username_or_email, password):
""" Verifies the given credentials and, if valid, creates/links a database user to the """ Verifies the given credentials and, if valid, creates/links a database user to the
@ -109,11 +110,12 @@ class FederatedUsers(object):
""" """
return (None, 'Not supported') return (None, 'Not supported')
def _get_and_link_federated_user_info(self, username, email): def _get_and_link_federated_user_info(self, username, email, internal_create=False):
db_user = model.user.verify_federated_login(self._federated_service, username) db_user = model.user.verify_federated_login(self._federated_service, username)
if not db_user: if not db_user:
# We must create the user in our db. Check to see if this is allowed. # We must create the user in our db. Check to see if this is allowed (except for internal
if not can_create_user(email): # creation, which is always allowed).
if not internal_create and not can_create_user(email):
return (None, DISABLED_MESSAGE) return (None, DISABLED_MESSAGE)
valid_username = None valid_username = None

View file

@ -82,7 +82,8 @@ def sync_team(authentication, stale_team_sync):
continue continue
# Retrieve the Quay user associated with the member info. # Retrieve the Quay user associated with the member info.
(quay_user, err) = authentication.get_and_link_federated_user_info(member_info) (quay_user, err) = authentication.get_and_link_federated_user_info(member_info,
internal_create=True)
if err is not None: if err is not None:
logger.error('Could not link external user %s to an internal user: %s', logger.error('Could not link external user %s to an internal user: %s',
member_info.username, err, member_info.username, err,

View file

@ -1,8 +1,11 @@
import os
from datetime import datetime, timedelta from datetime import datetime, timedelta
import os
import pytest import pytest
from mock import patch
from data import model, database from data import model, database
from data.users.federated import FederatedUsers, UserInformation from data.users.federated import FederatedUsers, UserInformation
from data.users.teamsync import sync_team, sync_teams_to_groups from data.users.teamsync import sync_team, sync_teams_to_groups
@ -23,6 +26,18 @@ class FakeUsers(FederatedUsers):
return (self.group_tuples, None) return (self.group_tuples, None)
@pytest.fixture(params=[True, False])
def user_creation(request):
with patch('features.USER_CREATION', request.param):
yield
@pytest.fixture(params=[True, False])
def invite_only_user_creation(request):
with patch('features.INVITE_ONLY_USER_CREATION', request.param):
yield
@pytest.mark.skipif(os.environ.get('TEST_DATABASE_URI', '').find('postgres') >= 0, @pytest.mark.skipif(os.environ.get('TEST_DATABASE_URI', '').find('postgres') >= 0,
reason="Postgres fails when existing members are added under the savepoint") reason="Postgres fails when existing members are added under the savepoint")
@pytest.mark.parametrize('starting_membership,group_membership,expected_membership', [ @pytest.mark.parametrize('starting_membership,group_membership,expected_membership', [
@ -144,7 +159,8 @@ class FakeUsers(FederatedUsers):
], ],
['anotheruser', 'someuser']), ['anotheruser', 'someuser']),
]) ])
def test_syncing(starting_membership, group_membership, expected_membership, app): def test_syncing(user_creation, invite_only_user_creation, starting_membership, group_membership,
expected_membership, app):
org = model.organization.get_organization('buynlarge') org = model.organization.get_organization('buynlarge')
# Necessary for the fake auth entries to be created in FederatedLogin. # Necessary for the fake auth entries to be created in FederatedLogin.
@ -169,7 +185,7 @@ def test_syncing(starting_membership, group_membership, expected_membership, app
quay_user = model.user.create_user_noverify(quay_username, email) quay_user = model.user.create_user_noverify(quay_username, email)
else: else:
quay_user = model.user.create_federated_user(quay_username, email, _FAKE_AUTH, quay_user = model.user.create_federated_user(quay_username, email, _FAKE_AUTH,
fakeauth_username, False) fakeauth_username, False)
model.team.add_user_to_team(quay_user, sync_team_info.team) model.team.add_user_to_team(quay_user, sync_team_info.team)
@ -187,7 +203,8 @@ def test_syncing(starting_membership, group_membership, expected_membership, app
assert len(users_expected) + len(robots_expected) == len(expected_membership) assert len(users_expected) + len(robots_expected) == len(expected_membership)
# Check that the team's users match those expected. # Check that the team's users match those expected.
service_user_map = model.team.get_federated_team_member_mapping(sync_team_info.team, _FAKE_AUTH) service_user_map = model.team.get_federated_team_member_mapping(sync_team_info.team,
_FAKE_AUTH)
assert set(service_user_map.keys()) == users_expected assert set(service_user_map.keys()) == users_expected
quay_users = model.team.list_team_users(sync_team_info.team) quay_users = model.team.list_team_users(sync_team_info.team)
@ -204,7 +221,7 @@ def test_syncing(starting_membership, group_membership, expected_membership, app
assert robots_expected == robots_found assert robots_expected == robots_found
def test_sync_teams_to_groups(app): def test_sync_teams_to_groups(user_creation, invite_only_user_creation, app):
# Necessary for the fake auth entries to be created in FederatedLogin. # Necessary for the fake auth entries to be created in FederatedLogin.
database.LoginService.create(name=_FAKE_AUTH) database.LoginService.create(name=_FAKE_AUTH)
@ -247,7 +264,8 @@ def test_sync_teams_to_groups(app):
(mock_ldap, {'group_dn': 'cn=AwesomeFolk'}), (mock_ldap, {'group_dn': 'cn=AwesomeFolk'}),
(fake_keystone, {'group_id': 'somegroupid'}), (fake_keystone, {'group_id': 'somegroupid'}),
]) ])
def test_teamsync_end_to_end(auth_system_builder, config, app): def test_teamsync_end_to_end(user_creation, invite_only_user_creation, auth_system_builder, config,
app):
with auth_system_builder() as auth: with auth_system_builder() as auth:
# Create an new team to sync. # Create an new team to sync.
org = model.organization.get_organization('buynlarge') org = model.organization.get_organization('buynlarge')
@ -286,7 +304,8 @@ def test_teamsync_end_to_end(auth_system_builder, config, app):
(mock_ldap, {'group_dn': 'cn=AwesomeFolk'}), (mock_ldap, {'group_dn': 'cn=AwesomeFolk'}),
(fake_keystone, {'group_id': 'somegroupid'}), (fake_keystone, {'group_id': 'somegroupid'}),
]) ])
def test_teamsync_existing_email(auth_system_builder, config, app): def test_teamsync_existing_email(user_creation, invite_only_user_creation, auth_system_builder,
config, app):
with auth_system_builder() as auth: with auth_system_builder() as auth:
# Create an new team to sync. # Create an new team to sync.
org = model.organization.get_organization('buynlarge') org = model.organization.get_organization('buynlarge')
@ -303,4 +322,3 @@ def test_teamsync_existing_email(auth_system_builder, config, app):
team_members = list(model.team.list_team_users(sync_team_info.team)) team_members = list(model.team.list_team_users(sync_team_info.team))
assert len(team_members) > 0 assert len(team_members) > 0