diff --git a/data/users/__init__.py b/data/users/__init__.py index ccc9e7f7e..01c5ebe7d 100644 --- a/data/users/__init__.py +++ b/data/users/__init__.py @@ -174,6 +174,12 @@ class UserAuthentication(object): """ return self.state.federated_service + @property + def requires_distinct_cli_password(self): + """ Returns whether this auth system requires a distinct CLI password to be created, + in-system, before the CLI can be used. """ + return self.state.requires_distinct_cli_password + @property def supports_encrypted_credentials(self): """ Returns whether this auth system supports using encrypted credentials. """ diff --git a/data/users/database.py b/data/users/database.py index 09a8ccf7f..6c85db3bc 100644 --- a/data/users/database.py +++ b/data/users/database.py @@ -13,6 +13,11 @@ class DatabaseUsers(object): def supports_encrypted_credentials(self): return True + @property + def requires_distinct_cli_password(self): + # Since the database stores its own password. + return True + def verify_credentials(self, username_or_email, password): """ Simply delegate to the model implementation. """ result = model.user.verify_user(username_or_email, password) diff --git a/data/users/federated.py b/data/users/federated.py index 7a79444e6..047234a65 100644 --- a/data/users/federated.py +++ b/data/users/federated.py @@ -28,6 +28,11 @@ class FederatedUsers(object): def supports_encrypted_credentials(self): return True + @property + def requires_distinct_cli_password(self): + # Since the federated auth provides a password which works on the CLI. + return False + def get_user(self, username_or_email): """ Retrieves the user with the given username or email, returning a tuple containing a UserInformation (if success) and the error message (on failure). diff --git a/data/users/oidc.py b/data/users/oidc.py index 30314f19f..c077a3b21 100644 --- a/data/users/oidc.py +++ b/data/users/oidc.py @@ -29,6 +29,11 @@ class OIDCInternalAuth(object): def federated_service(self): return None + @property + def requires_distinct_cli_password(self): + # Since the "password" is the generated ID token. + return False + @property def supports_encrypted_credentials(self): # Since the "password" is already a signed JWT. diff --git a/endpoints/oauth/login.py b/endpoints/oauth/login.py index 54f0c7dc5..3a0e1cc2b 100644 --- a/endpoints/oauth/login.py +++ b/endpoints/oauth/login.py @@ -105,9 +105,10 @@ def _conduct_oauth_login(auth_system, login_service, lid, lusername, lemail, met new_username = valid break + requires_password = auth_system.requires_distinct_cli_password prompts = model.user.get_default_user_prompts(features) user_obj = model.user.create_federated_user(new_username, lemail, service_id, lid, - set_password_notification=True, + set_password_notification=requires_password, metadata=metadata or {}, prompts=prompts, email_required=features.MAILING) diff --git a/endpoints/oauth/test/test_login.py b/endpoints/oauth/test/test_login.py index 40a51dc91..12a26e7ee 100644 --- a/endpoints/oauth/test/test_login.py +++ b/endpoints/oauth/test/test_login.py @@ -73,6 +73,10 @@ def test_new_account_via_database(login_service): federated_login = model.user.lookup_federated_login(new_user, login_service.service_id()) assert federated_login is not None + # Ensure that a notification was created. + assert list(model.notification.list_notifications(result.user_obj, + kind_name='password_required')) + @pytest.mark.parametrize('open_creation, invite_only, has_invite, expect_success', [ # Open creation -> Success! (True, False, False, True), @@ -173,6 +177,9 @@ def test_new_account_via_ldap(binding_field, lid, lusername, lemail, expected_er else: assert internal_login is None + # Ensure that no notification was created. + assert not list(model.notification.list_notifications(result.user_obj, + kind_name='password_required')) else: # Ensure that no addtional users were created. assert current_user_count == existing_user_count