зеркало из https://github.com/mozilla/mozillians.git
Merge pull request #3175 from akatsoulas/group-propagation
Always propagate access groups.
This commit is contained in:
Коммит
28bc2bede6
|
@ -5,39 +5,17 @@ import re
|
|||
|
||||
from django.db import transaction
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from cities_light.models import Country
|
||||
from mozilla_django_oidc.auth import OIDCAuthenticationBackend
|
||||
from waffle import switch_is_active
|
||||
|
||||
from mozillians.common.templatetags.helpers import get_object_or_none
|
||||
from mozillians.dino_park.utils import _dino_park_get_profile_by_userid
|
||||
from mozillians.users.models import IdpProfile
|
||||
from mozillians.users.tasks import send_userprofile_to_cis
|
||||
|
||||
|
||||
# Only allow the following login flows
|
||||
# Passwordless > Google > Github, FxA > LDAP
|
||||
# There is no way to downgrade
|
||||
ALLOWED_IDP_FLOWS = {
|
||||
IdpProfile.PROVIDER_PASSWORDLESS: IdpProfile.MFA_ACCOUNTS + [
|
||||
IdpProfile.PROVIDER_PASSWORDLESS,
|
||||
IdpProfile.PROVIDER_GOOGLE,
|
||||
],
|
||||
IdpProfile.PROVIDER_GOOGLE: IdpProfile.MFA_ACCOUNTS + [
|
||||
IdpProfile.PROVIDER_PASSWORDLESS,
|
||||
IdpProfile.PROVIDER_GOOGLE,
|
||||
],
|
||||
IdpProfile.PROVIDER_GITHUB: IdpProfile.MFA_ACCOUNTS,
|
||||
IdpProfile.PROVIDER_FIREFOX_ACCOUNTS: IdpProfile.MFA_ACCOUNTS,
|
||||
IdpProfile.PROVIDER_LDAP: [
|
||||
IdpProfile.PROVIDER_LDAP
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def calculate_username(email):
|
||||
"""Calculate username from email address."""
|
||||
|
||||
|
@ -174,9 +152,6 @@ class MozilliansAuthBackend(OIDCAuthenticationBackend):
|
|||
if groups and 'hris_is_staff' in groups:
|
||||
profile.auto_vouch()
|
||||
|
||||
# Get current_idp
|
||||
current_idp = get_object_or_none(IdpProfile, profile=profile, primary=True)
|
||||
|
||||
# Get or create new `user_id`
|
||||
obj, _ = IdpProfile.objects.get_or_create(
|
||||
profile=profile,
|
||||
|
@ -188,20 +163,13 @@ class MozilliansAuthBackend(OIDCAuthenticationBackend):
|
|||
obj.username = self.claims.get('nickname', '')
|
||||
obj.save()
|
||||
|
||||
# Do not allow downgrades.
|
||||
if current_idp and obj.type < current_idp.type:
|
||||
msg = u'Please use {0} as the login method to authenticate'
|
||||
messages.error(self.request, msg.format(current_idp.get_type_display()))
|
||||
return None
|
||||
|
||||
# Mark other `user_id` as `primary=False`
|
||||
idp_q = IdpProfile.objects.filter(profile=profile)
|
||||
with transaction.atomic():
|
||||
idp_q.exclude(auth0_user_id=auth0_user_id, email=email).update(primary=False)
|
||||
# Mark current `user_id` as `primary=True`
|
||||
idp_q.filter(auth0_user_id=auth0_user_id, email=email).update(primary=True)
|
||||
# Update CIS
|
||||
send_userprofile_to_cis.delay(profile.pk)
|
||||
# This is happening only the first time a user logs into mozillians.org
|
||||
if not idp_q.filter(primary=True).exists():
|
||||
with transaction.atomic():
|
||||
idp_q.filter(auth0_user_id=auth0_user_id, email=email).update(primary=True)
|
||||
# Update CIS
|
||||
send_userprofile_to_cis.delay(profile.pk)
|
||||
return user
|
||||
|
||||
def authenticate(self, **kwargs):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.http import HttpRequest
|
||||
from django.test import override_settings
|
||||
|
||||
from mock import Mock, patch
|
||||
from mock import Mock
|
||||
from nose.tools import eq_, ok_
|
||||
|
||||
from mozillians.common.tests import TestCase
|
||||
|
@ -23,8 +23,7 @@ class MozilliansAuthBackendTests(TestCase):
|
|||
from mozillians.common.authbackend import MozilliansAuthBackend
|
||||
self.backend = MozilliansAuthBackend()
|
||||
|
||||
@patch('mozillians.common.authbackend.messages')
|
||||
def test_add_a_new_email_identity(self, mocked_message):
|
||||
def test_add_a_new_email_identity(self):
|
||||
"""Test to add a new email in an authenticated user."""
|
||||
user = UserFactory.create(email='foo@example.com')
|
||||
IdpProfile.objects.create(
|
||||
|
@ -51,10 +50,8 @@ class MozilliansAuthBackendTests(TestCase):
|
|||
email='bar@example.com')
|
||||
ok_(email_q.exists())
|
||||
eq_(returned_user, user)
|
||||
ok_(not mocked_message.called)
|
||||
|
||||
@patch('mozillians.common.authbackend.messages')
|
||||
def test_identity_already_exists(self, mocked_message):
|
||||
def test_identity_already_exists(self):
|
||||
"""Test to add an email that already exists."""
|
||||
|
||||
user = UserFactory.create(email='foo@example.com')
|
||||
|
@ -78,10 +75,8 @@ class MozilliansAuthBackendTests(TestCase):
|
|||
email=user.email,
|
||||
profile=user.userprofile)
|
||||
eq_(idp_q.count(), 1)
|
||||
ok_(not mocked_message.called)
|
||||
|
||||
@patch('mozillians.common.authbackend.messages')
|
||||
def test_identity_single_auth0_id_multiple_emails(self, mocked_message):
|
||||
def test_identity_single_auth0_id_multiple_emails(self):
|
||||
"""Test to add an email that already exists."""
|
||||
|
||||
user = UserFactory.create(email='foo@example.com')
|
||||
|
@ -105,39 +100,11 @@ class MozilliansAuthBackendTests(TestCase):
|
|||
|
||||
eq_(IdpProfile.objects.filter(
|
||||
profile=user.userprofile, primary=True,
|
||||
username='foo', email='foo@example.com').count(), 1)
|
||||
username='foo', email='foo@example.com').count(), 0)
|
||||
eq_(IdpProfile.objects.filter(
|
||||
profile=user.userprofile, primary=False,
|
||||
profile=user.userprofile, primary=True,
|
||||
email='foo@bar.com').count(), 1)
|
||||
|
||||
@patch('mozillians.common.authbackend.messages')
|
||||
def test_add_idp_wrong_flow(self, mocked_message):
|
||||
"""Test logging in with a weaker provider compared to the current one"""
|
||||
|
||||
user = UserFactory.create(email='foo@example.com')
|
||||
IdpProfile.objects.create(
|
||||
profile=user.userprofile,
|
||||
auth0_user_id='ad|foobar',
|
||||
primary=True,
|
||||
email='foobar@example.com'
|
||||
)
|
||||
|
||||
claims = {
|
||||
'email': 'bar@example.com',
|
||||
'user_id': 'foobar'
|
||||
}
|
||||
|
||||
request_mock = Mock(spec=HttpRequest)
|
||||
request_mock.user = user
|
||||
self.backend.claims = claims
|
||||
self.backend.request = request_mock
|
||||
|
||||
returned_user = self.backend.check_authentication_method(user)
|
||||
msg = 'Please use LDAP Provider as the login method to authenticate'
|
||||
mocked_message.error.assert_called_once_with(request_mock, msg)
|
||||
|
||||
eq_(returned_user, None)
|
||||
|
||||
def test_filter_users_with_email_belonging_to_non_primary_identity(self):
|
||||
"""Test filter users with a non primary identity."""
|
||||
|
||||
|
|
|
@ -139,13 +139,11 @@ class HasMFAEnabled(SimpleListFilter):
|
|||
|
||||
def queryset(self, request, queryset):
|
||||
if self.value() == 'True':
|
||||
return queryset.filter(userprofile__idp_profiles__type__in=[
|
||||
IdpProfile.PROVIDER_GITHUB, IdpProfile.PROVIDER_LDAP]
|
||||
).distinct()
|
||||
return queryset.filter(
|
||||
userprofile__idp_profiles__type__in=IdpProfile.HIGH_AAL_ACCOUNTS).distinct()
|
||||
elif self.value() == 'False':
|
||||
return queryset.exclude(userprofile__idp_profiles__type__in=[
|
||||
IdpProfile.PROVIDER_GITHUB, IdpProfile.PROVIDER_LDAP]
|
||||
).distinct()
|
||||
return queryset.exclude(
|
||||
userprofile__idp_profiles__type__in=IdpProfile.HIGH_AAL_ACCOUNTS).distinct()
|
||||
else:
|
||||
return queryset
|
||||
|
||||
|
|
|
@ -736,15 +736,6 @@ class UserProfile(UserProfilePrivacyModel):
|
|||
def get_cis_groups(self, idp):
|
||||
"""Prepares the entry for profile groups in the CIS format."""
|
||||
|
||||
# Update strategy: send groups for higher MFA idp
|
||||
# Wipe groups from the rest
|
||||
idps = list(self.idp_profiles.all().values_list('type', flat=True))
|
||||
|
||||
# if the current idp does not match
|
||||
# the greatest number in the list, wipe the groups
|
||||
if not idps or idp.type != max(idps) or not idp.is_mfa():
|
||||
return []
|
||||
|
||||
memberships = GroupMembership.objects.filter(
|
||||
userprofile=self,
|
||||
status=GroupMembership.MEMBER,
|
||||
|
@ -807,10 +798,12 @@ class IdpProfile(models.Model):
|
|||
(PROVIDER_LDAP, 'LDAP Provider',),
|
||||
|
||||
)
|
||||
# MFA_ACCOUNTS
|
||||
MFA_ACCOUNTS = [PROVIDER_LDAP,
|
||||
PROVIDER_FIREFOX_ACCOUNTS,
|
||||
PROVIDER_GITHUB]
|
||||
# High Security OPs
|
||||
HIGH_AAL_ACCOUNTS = [PROVIDER_LDAP,
|
||||
PROVIDER_FIREFOX_ACCOUNTS,
|
||||
PROVIDER_GITHUB,
|
||||
PROVIDER_GOOGLE]
|
||||
|
||||
profile = models.ForeignKey(UserProfile, related_name='idp_profiles')
|
||||
type = models.IntegerField(choices=PROVIDER_TYPES,
|
||||
default=None,
|
||||
|
@ -845,11 +838,6 @@ class IdpProfile(models.Model):
|
|||
|
||||
return self.PROVIDER_UNKNOWN
|
||||
|
||||
def is_mfa(self):
|
||||
"""Helper method to check if IdpProfile is MFA-ed"""
|
||||
|
||||
return self.type in self.MFA_ACCOUNTS
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Custom save method.
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче