Пример #1
0
def push_empty_groups_to_cis(sender, instance, **kwargs):
    """Notify CIS about the profile deletion.

    Remove all the access groups and tags from the profile.
    """
    from mozillians.users.tasks import send_userprofile_to_cis

    data = bundle_profile_data(instance.id, delete=True)
    send_userprofile_to_cis.delay(profile_results=data)
Пример #2
0
def push_empty_groups_to_cis(sender, instance, **kwargs):
    """Notify CIS about the profile deletion.

    Remove all the access groups and tags from the profile.
    """
    from mozillians.users.tasks import send_userprofile_to_cis

    data = bundle_profile_data(instance.id, delete=True)
    send_userprofile_to_cis.delay(profile_results=data)
Пример #3
0
    def add_member(self, userprofile, status=GroupMembership.MEMBER, inviter=None):
        """
        Add a user to this group. Optionally specify status other than member.

        If user is already in the group with the given status, this is a no-op.

        If user is already in the group with a different status, their status will
        be updated if the change is a promotion. Otherwise, their status will not change.

        If the group in question is the NDA group, also add the user to the NDA newsletter.
        """
        defaults = dict(status=status, date_joined=now())
        membership, _ = GroupMembership.objects.get_or_create(userprofile=userprofile,
                                                              group=self,
                                                              defaults=defaults)

        send_userprofile_to_cis.delay(membership.userprofile.pk)

        # Remove the need_removal flag in any case
        # We have a renewal, let's save the object.
        if membership.needs_renewal:
            membership.needs_renewal = False
            membership.save()

        if membership.status != status:
            # Status changed
            # The only valid status change states are:
            # PENDING to MEMBER
            # PENDING to PENDING_TERMS
            # PENDING_TERMS to MEMBER

            old_status = membership.status
            membership.status = status
            statuses = [(GroupMembership.PENDING, GroupMembership.MEMBER),
                        (GroupMembership.PENDING, GroupMembership.PENDING_TERMS),
                        (GroupMembership.PENDING_TERMS, GroupMembership.MEMBER)]

            if (old_status, status) in statuses:
                # Status changed
                membership.save()
                if membership.status in [GroupMembership.PENDING, GroupMembership.MEMBER]:
                    email_membership_change.delay(self.pk, userprofile.user.pk, old_status, status)

                # Since there is no demotion, we can check if the new status is MEMBER and
                # subscribe the user to the NDA newsletter if the group is NDA
                if self.name == settings.NDA_GROUP and status == GroupMembership.MEMBER:
                    subscribe_user_to_basket.delay(userprofile.id,
                                                   [settings.BASKET_NDA_NEWSLETTER])

        if inviter:
            # Set the invite to the last person who renewed the membership
            invite = get_object_or_none(Invite, group=membership.group, redeemer=userprofile)
            if invite:
                invite.inviter = inviter
                invite.save()
Пример #4
0
    def add_member(self, userprofile, status=GroupMembership.MEMBER, inviter=None):
        """
        Add a user to this group. Optionally specify status other than member.

        If user is already in the group with the given status, this is a no-op.

        If user is already in the group with a different status, their status will
        be updated if the change is a promotion. Otherwise, their status will not change.

        If the group in question is the NDA group, also add the user to the NDA newsletter.
        """
        defaults = dict(status=status, date_joined=now())
        membership, _ = GroupMembership.objects.get_or_create(userprofile=userprofile,
                                                              group=self,
                                                              defaults=defaults)

        send_userprofile_to_cis.delay(membership.userprofile.pk)

        # Remove the need_removal flag in any case
        # We have a renewal, let's save the object.
        if membership.needs_renewal:
            membership.needs_renewal = False
            membership.save()

        if membership.status != status:
            # Status changed
            # The only valid status change states are:
            # PENDING to MEMBER
            # PENDING to PENDING_TERMS
            # PENDING_TERMS to MEMBER

            old_status = membership.status
            membership.status = status
            statuses = [(GroupMembership.PENDING, GroupMembership.MEMBER),
                        (GroupMembership.PENDING, GroupMembership.PENDING_TERMS),
                        (GroupMembership.PENDING_TERMS, GroupMembership.MEMBER)]

            if (old_status, status) in statuses:
                # Status changed
                membership.save()
                if membership.status in [GroupMembership.PENDING, GroupMembership.MEMBER]:
                    email_membership_change.delay(self.pk, userprofile.user.pk, old_status, status)

                # Since there is no demotion, we can check if the new status is MEMBER and
                # subscribe the user to the NDA newsletter if the group is NDA
                if self.name == settings.NDA_GROUP and status == GroupMembership.MEMBER:
                    subscribe_user_to_basket.delay(userprofile.id,
                                                   [settings.BASKET_NDA_NEWSLETTER])

        if inviter:
            # Set the invite to the last person who renewed the membership
            invite = get_object_or_none(Invite, group=membership.group, redeemer=userprofile)
            if invite:
                invite.inviter = inviter
                invite.save()
Пример #5
0
    def check_authentication_method(self, user):
        """Check which Identity is used to login.

        This method, depending on the current status of the IdpProfile
        of a user, enforces MFA logins and creates the IdpProfiles.
        Returns the object (user) it was passed unchanged.
        """
        if not user:
            return None

        profile = user.userprofile
        # Ensure compatibility with OIDC conformant mode
        auth0_user_id = self.claims.get('user_id') or self.claims.get('sub')
        email = self.claims.get('email')

        # 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,
                                                  email=email,
                                                  auth0_user_id=auth0_user_id)

        # Update/Save the Github username
        if 'github|' in auth0_user_id:
            obj.username = self.claims.get('nickname', '')
            obj.save()

        # Do not allow downgrades.
        # Round the current type to the floor.
        # This way 30 and 39 will have always the same priority.
        if current_idp and obj.type < int(
                math.floor(current_idp.type / 10) * 10):
            msg = u'Please use one of the following authentication methods: {}'
            # convert the tuple to a dict to easily get the values
            provider_types = dict(IdpProfile.PROVIDER_TYPES)
            methods = ', '.join(provider_types[x]
                                for x in ALLOWED_IDP_FLOWS[current_idp.type])
            messages.error(self.request, msg.format(methods))
            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)
        return user
Пример #6
0
    def check_authentication_method(self, user):
        """Check which Identity is used to login.

        This method, depending on the current status of the IdpProfile
        of a user, enforces MFA logins and creates the IdpProfiles.
        Returns the object (user) it was passed unchanged.
        """
        if not user:
            return None

        profile = user.userprofile
        # Ensure compatibility with OIDC conformant mode
        auth0_user_id = self.claims.get('user_id') or self.claims.get('sub')
        email = self.claims.get('email')
        # Grant an employee vouch if the user has the 'hris_is_staff' group
        groups = self.claims.get('https://sso.mozilla.com/claim/groups')
        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,
                                                  email=email,
                                                  auth0_user_id=auth0_user_id)

        # Update/Save the Github username
        if 'github|' in auth0_user_id:
            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)
        return user
Пример #7
0
    def save(self, *args, **kwargs):
        self._privacy_level = None
        autovouch = kwargs.pop('autovouch', True)

        super(UserProfile, self).save(*args, **kwargs)
        # Auto_vouch follows the first save, because you can't
        # create foreign keys without a database id.

        if self.is_complete:
            send_userprofile_to_cis.delay(self.pk)

        if autovouch:
            self.auto_vouch()
Пример #8
0
    def save(self, *args, **kwargs):
        self._privacy_level = None
        autovouch = kwargs.pop('autovouch', False)

        super(UserProfile, self).save(*args, **kwargs)
        # Auto_vouch follows the first save, because you can't
        # create foreign keys without a database id.

        if self.is_complete:
            send_userprofile_to_cis.delay(self.pk)

        if autovouch:
            self.auto_vouch()
Пример #9
0
    def check_authentication_method(self, user):
        """Check which Identity is used to login.

        This method, depending on the current status of the IdpProfile
        of a user, enforces MFA logins and creates the IdpProfiles.
        Returns the object (user) it was passed unchanged.
        """
        if not user:
            return None

        profile = user.userprofile
        # Ensure compatibility with OIDC conformant mode
        auth0_user_id = self.claims.get('user_id') or self.claims.get('sub')
        email = self.claims.get('email')
        aal_scope = self.claims.get(SSO_AAL_SCOPE)
        is_mfa = True
        if not aal_scope or aal_scope != 'MEDIUM':
            is_mfa = False

        # Grant an employee vouch if the user has the 'hris_is_staff' group
        groups = self.claims.get('https://sso.mozilla.com/claim/groups')
        if groups and 'hris_is_staff' in groups:
            profile.auto_vouch()

        # Get or create new `user_id`
        obj, _ = IdpProfile.objects.get_or_create(
            profile=profile,
            email=email,
            auth0_user_id=auth0_user_id)

        if profile.groups.filter(is_access_group=True).exists() and not is_mfa:
            msg = ('Members and Curators of Access Groups need to use a 2FA'
                   ' authentication method to login.')
            messages.error(self.request, msg)
            return None

        # With account deracheting we will always get the same Auth0 user id. Mark it as primary
        if not obj.primary:
            obj.primary = True
            IdpProfile.objects.filter(profile=profile).exclude(id=obj.id).update(primary=False)

        # Update/Save the Github username
        if 'github|' in auth0_user_id:
            obj.username = self.claims.get('nickname', '')
        # Save once
        obj.save()

        # Update CIS
        send_userprofile_to_cis.delay(profile.pk)
        return user
Пример #10
0
    def check_authentication_method(self, user):
        """Check which Identity is used to login.

        This method, depending on the current status of the IdpProfile
        of a user, enforces MFA logins and creates the IdpProfiles.
        Returns the object (user) it was passed unchanged.
        """
        if not user:
            return None

        profile = user.userprofile
        # Ensure compatibility with OIDC conformant mode
        auth0_user_id = self.claims.get('user_id') or self.claims.get('sub')
        email = self.claims.get('email')
        aal_scope = self.claims.get(SSO_AAL_SCOPE)
        is_mfa = True
        if not aal_scope or aal_scope != ['2FA']:
            is_mfa = False

        # Grant an employee vouch if the user has the 'hris_is_staff' group
        groups = self.claims.get('https://sso.mozilla.com/claim/groups')
        if groups and 'hris_is_staff' in groups:
            profile.auto_vouch()

        # Get or create new `user_id`
        obj, _ = IdpProfile.objects.get_or_create(profile=profile,
                                                  email=email,
                                                  auth0_user_id=auth0_user_id)

        if profile.groups.filter(is_access_group=True).exists() and not is_mfa:
            msg = ('Members and Curators of Access Groups need to use a 2FA'
                   ' authentication method to login.')
            messages.error(self.request, msg)
            return None

        # With account deracheting we will always get the same Auth0 user id. Mark it as primary
        if not obj.primary:
            obj.primary = True
            IdpProfile.objects.filter(profile=profile).exclude(
                id=obj.id).update(primary=False)

        # Update/Save the Github username
        if 'github|' in auth0_user_id:
            obj.username = self.claims.get('nickname', '')
        # Save once
        obj.save()

        # Update CIS
        send_userprofile_to_cis.delay(profile.pk)
        return user
Пример #11
0
    def check_authentication_method(self, user):
        """Check which Identity is used to login.

        This method, depending on the current status of the IdpProfile
        of a user, enforces MFA logins and creates the IdpProfiles.
        Returns the object (user) it was passed unchanged.
        """
        if not user:
            return None

        profile = user.userprofile
        # Ensure compatibility with OIDC conformant mode
        auth0_user_id = self.claims.get('user_id') or self.claims.get('sub')
        email = self.claims.get('email')
        # Grant an employee vouch if the user has the 'hris_is_staff' group
        groups = self.claims.get('https://sso.mozilla.com/claim/groups')
        if groups and 'hris_is_staff' in groups:
            profile.auto_vouch()

        # Get or create new `user_id`
        obj, _ = IdpProfile.objects.get_or_create(
            profile=profile,
            email=email,
            auth0_user_id=auth0_user_id)

        # Check if a user with passwordless login curates an access group and block it.
        if obj.type <= IdpProfile.PROVIDER_PASSWORDLESS:
            # LDAP is excluded since is checked at the Auth0 level.
            if not profile.idp_profiles.filter(type=IdpProfile.PROVIDER_LDAP).exists():
                if profile.groups.filter(is_access_group=True, curators=profile).exists():
                    msg = 'Access group curators cannot use Passwordless as the login method.'
                    messages.error(self.request, msg)
                    return None

        # With account deracheting we will always get the same Auth0 user id. Mark it as primary
        if not obj.primary:
            obj.primary = True
            IdpProfile.objects.filter(profile=profile).exclude(id=obj.id).update(primary=False)

        # Update/Save the Github username
        if 'github|' in auth0_user_id:
            obj.username = self.claims.get('nickname', '')
        # Save once
        obj.save()

        # Update CIS
        send_userprofile_to_cis.delay(profile.pk)
        return user
Пример #12
0
    def check_authentication_method(self, user):
        """Check which Identity is used to login.

        This method, depending on the current status of the IdpProfile
        of a user, enforces MFA logins and creates the IdpProfiles.
        Returns the object (user) it was passed unchanged.
        """
        if not user:
            return None

        profile = user.userprofile
        auth0_user_id = self.claims.get('user_id')
        email = self.claims.get('email')

        # 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,
                                                  email=email,
                                                  auth0_user_id=auth0_user_id)

        # Do not allow downgrades.
        if current_idp and obj.type < current_idp.type:
            msg = u'Please use one of the following authentication methods: {}'
            # convert the tuple to a dict to easily get the values
            provider_types = dict(IdpProfile.PROVIDER_TYPES)
            methods = ', '.join(provider_types[x]
                                for x in ALLOWED_IDP_FLOWS[current_idp.type])
            messages.error(self.request, msg.format(methods))
            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)
        return user
Пример #13
0
    def check_authentication_method(self, user):
        """Check which Identity is used to login.

        This method, depending on the current status of the IdpProfile
        of a user, enforces MFA logins and creates the IdpProfiles.
        Returns the object (user) it was passed unchanged.
        """
        if not user:
            return None

        profile = user.userprofile
        # Ensure compatibility with OIDC conformant mode
        auth0_user_id = self.claims.get('user_id') or self.claims.get('sub')
        email = self.claims.get('email')

        # 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,
            email=email,
            auth0_user_id=auth0_user_id)

        # Do not allow downgrades.
        # Round the current type to the floor. This way 30 and 39 will have always the same
        # priority.
        if current_idp and obj.type < int(math.floor(current_idp.type / 10) * 10):
            msg = u'Please use one of the following authentication methods: {}'
            # convert the tuple to a dict to easily get the values
            provider_types = dict(IdpProfile.PROVIDER_TYPES)
            methods = ', '.join(provider_types[x] for x in ALLOWED_IDP_FLOWS[current_idp.type])
            messages.error(self.request, msg.format(methods))
            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)
        return user
Пример #14
0
    def check_authentication_method(self, user):
        """Check which Identity is used to login.

        This method, depending on the current status of the IdpProfile
        of a user, enforces MFA logins and creates the IdpProfiles.
        Returns the object (user) it was passed unchanged.
        """
        if not user:
            return None

        profile = user.userprofile
        # Ensure compatibility with OIDC conformant mode
        auth0_user_id = self.claims.get('user_id') or self.claims.get('sub')
        email = self.claims.get('email')

        # 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,
            email=email,
            auth0_user_id=auth0_user_id)

        # Update/Save the Github username
        if 'github|' in auth0_user_id:
            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)
        return user
Пример #15
0
    def remove_member(self, userprofile, status=None, send_email=False):
        """Change membership status for a group.

        If user is a member of an open group, then the user is removed.

        If a user is a member of a reviewed or closed group,
        then the membership is in a pending state.
        """
        try:
            membership = GroupMembership.objects.get(group=self,
                                                     userprofile=userprofile)
        except GroupMembership.DoesNotExist:
            return
        old_status = membership.status

        # If the group is of type Group.OPEN, delete membership
        # If no status is given, delete membership,
        # If the current membership is PENDING*, delete membership
        if (not status or self.accepting_new_members == Group.OPEN
                or old_status != GroupMembership.MEMBER):
            # We have either an open group or the request to join a reviewed group is denied
            # or the curator manually declined a user in a pending state.
            membership.delete()
            send_userprofile_to_cis.delay(membership.userprofile.pk)
            # delete the invitation to the group if exists
            Invite.objects.filter(group=self, redeemer=userprofile).delete()
            send_email = True

        # Group is either of Group.REVIEWED or Group.CLOSED, change membership to `status`
        else:
            # if we are here, there is a new status for our user
            membership.status = status
            membership.needs_renewal = False
            membership.save()
            send_email = True

        # If group is the NDA group, unsubscribe user from the newsletter.
        if self.name == settings.NDA_GROUP:
            unsubscribe_from_basket_task.delay(
                userprofile.email, [settings.BASKET_NDA_NEWSLETTER])

        if send_email:
            email_membership_change.delay(self.pk, userprofile.user.pk,
                                          old_status, status)
Пример #16
0
def push_empty_groups_to_cis(sender, instance, **kwargs):
    """Notify CIS about the profile deletion.

    Remove all the access groups and tags from the profile.
    """
    from mozillians.users.tasks import send_userprofile_to_cis
    data = bundle_profile_data(instance.id, delete=True)

    for d in data:
        log_name = 'CIS group deletion - {}'.format(d['user_id'])
        log_data = {
            'level': logging.DEBUG,
            'logger': 'mozillians.cis_transaction'
        }
        log_extra = {
            'cis_transaction_data': json.dumps(d)
        }

        sentry_client.captureMessage(log_name, data=log_data, stack=True, extra=log_extra)

    send_userprofile_to_cis.delay(profile_results=data)
def push_empty_groups_to_cis(sender, instance, **kwargs):
    """Notify CIS about the profile deletion.

    Remove all the access groups and tags from the profile.
    """
    from mozillians.users.tasks import send_userprofile_to_cis
    data = bundle_profile_data(instance.id, delete=True)

    for d in data:
        log_name = 'CIS group deletion - {}'.format(d['user_id'])
        log_data = {
            'level': logging.DEBUG,
            'logger': 'mozillians.cis_transaction'
        }
        log_extra = {'cis_transaction_data': json.dumps(d)}

        sentry_client.captureMessage(log_name,
                                     data=log_data,
                                     stack=True,
                                     extra=log_extra)

    send_userprofile_to_cis.delay(profile_results=data)
Пример #18
0
    def check_authentication_method(self, user):
        """Check which Identity is used to login.

        This method, depending on the current status of the IdpProfile
        of a user, enforces MFA logins and creates the IdpProfiles.
        Returns the object (user) it was passed unchanged.
        """
        if not user:
            return None

        profile = user.userprofile
        # Ensure compatibility with OIDC conformant mode
        auth0_user_id = self.claims.get('user_id') or self.claims.get('sub')
        email = self.claims.get('email')
        # Grant an employee vouch if the user has the 'hris_is_staff' group
        groups = self.claims.get('https://sso.mozilla.com/claim/groups')
        if groups and 'hris_is_staff' in groups:
            profile.auto_vouch()

        # Get or create new `user_id`
        obj, _ = IdpProfile.objects.get_or_create(profile=profile,
                                                  email=email,
                                                  auth0_user_id=auth0_user_id)

        # Update/Save the Github username
        if 'github|' in auth0_user_id:
            obj.username = self.claims.get('nickname', '')
            obj.save()

        idp_q = IdpProfile.objects.filter(profile=profile)
        # 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
Пример #19
0
def delete_identity(request, identity_pk):
    """Delete alternate email address."""
    user = User.objects.get(pk=request.user.id)
    profile = user.userprofile

    # Only email owner can delete emails
    idp_query = IdpProfile.objects.filter(profile=profile, pk=identity_pk)
    if not idp_query.exists():
        raise Http404()

    idp_query = idp_query.filter(primary=False, primary_contact_identity=False)
    if idp_query.exists():
        idp_type = idp_query[0].get_type_display()
        idp_query.delete()
        send_userprofile_to_cis.delay(profile.pk)
        msg = _(u'Identity {0} successfully deleted.'.format(idp_type))
        messages.success(request, msg)
        return redirect('phonebook:profile_edit')

    # We are trying to delete the primary identity, politely ignore the request
    msg = _(u'Sorry the requested Identity cannot be deleted.')
    messages.error(request, msg)
    return redirect('phonebook:profile_edit')
Пример #20
0
def delete_identity(request, identity_pk):
    """Delete alternate email address."""
    user = User.objects.get(pk=request.user.id)
    profile = user.userprofile

    # Only email owner can delete emails
    idp_query = IdpProfile.objects.filter(profile=profile, pk=identity_pk)
    if not idp_query.exists():
        raise Http404()

    idp_query = idp_query.filter(primary=False, primary_contact_identity=False)
    if idp_query.exists():
        idp_type = idp_query[0].get_type_display()
        idp_query.delete()
        send_userprofile_to_cis.delay(profile.pk)
        msg = _(u'Identity {0} successfully deleted.'.format(idp_type))
        messages.success(request, msg)
        return redirect('phonebook:profile_edit')

    # We are trying to delete the primary identity, politely ignore the request
    msg = _(u'Sorry the requested Identity cannot be deleted.')
    messages.error(request, msg)
    return redirect('phonebook:profile_edit')
Пример #21
0
def send_profile_to_cis_action(modeladmin, request, queryset):
    for obj in queryset:
        send_userprofile_to_cis.delay(obj.pk)
Пример #22
0
    def remove_member(self, userprofile, status=None, send_email=False):
        """Change membership status for a group.

        If user is a member of an open group, then the user is removed.

        If a user is a member of a reviewed or closed group,
        then the membership is in a pending state.
        """
        # Avoid circular dependencies
        from mozillians.users.models import UserProfile

        try:
            membership = GroupMembership.objects.get(group=self, userprofile=userprofile)
        except GroupMembership.DoesNotExist:
            return
        old_status = membership.status

        # If the group is of type Group.OPEN, delete membership
        # If no status is given, delete membership,
        # If the current membership is PENDING*, delete membership
        if (not status or self.accepting_new_members == Group.OPEN or
                old_status != GroupMembership.MEMBER):
            # We have either an open group or the request to join a reviewed group is denied
            # or the curator manually declined a user in a pending state.
            membership.delete()
            # delete the invitation to the group if exists
            Invite.objects.filter(group=self, redeemer=userprofile).delete()
            send_email = True
            # Remove all the access groups the user is a member of
            # if the group to remove is the NDA
            if self.name == settings.NDA_GROUP:
                group_memberships = GroupMembership.objects.none()
                # If the user is not staff, we need to delete the memberships to any access group
                if not userprofile.can_create_access_groups:
                    group_memberships = GroupMembership.objects.filter(userprofile=userprofile,
                                                                       group__is_access_group=True)

                for access_membership in group_memberships:
                    group = access_membership.group
                    if not group.curator_can_leave(userprofile):
                        # If the user is the only curator, let's add the superusers as curators
                        # as a fallback option
                        for super_user in UserProfile.objects.filter(user__is_superuser=True):
                            group.curators.add(super_user)
                            if not group.has_member(super_user):
                                group.add_member(super_user)
                    group.curators.remove(userprofile)
                    access_membership.delete()
                    # Notify CIS about this change
                    send_userprofile_to_cis.delay(access_membership.userprofile.pk)

            # Notify CIS about this change
            send_userprofile_to_cis.delay(membership.userprofile.pk)

        # Group is either of Group.REVIEWED or Group.CLOSED, change membership to `status`
        else:
            # if we are here, there is a new status for our user
            membership.status = status
            membership.needs_renewal = False
            membership.save()
            send_email = True

        # If group is the NDA group, unsubscribe user from the newsletter.
        if self.name == settings.NDA_GROUP:
            unsubscribe_from_basket_task.delay(userprofile.email, [settings.BASKET_NDA_NEWSLETTER])

        if send_email:
            email_membership_change.delay(self.pk, userprofile.user.pk, old_status, status)
Пример #23
0
 def process_cis_profile(self, request, user_id, *args, **kwargs):
     messages.success(request,
                      'Profile with id {0} sent to CIS.'.format(user_id))
     send_userprofile_to_cis.delay(user_id)
     return HttpResponseRedirect(
         reverse('admin:users_userprofile_changelist'))
Пример #24
0
def send_profile_to_cis_action(modeladmin, request, queryset):
    for obj in queryset:
        send_userprofile_to_cis.delay(obj.pk)
Пример #25
0
    def get(self, request):
        """Callback handler for OIDC authorization code flow.

        This is based on the mozilla-django-oidc library.
        This callback is used to verify the identity added by the user.
        Users are already logged in and we do not care about authentication.
        The JWT token is used to prove the identity of the user.
        """

        profile = request.user.userprofile
        # This is a difference nonce than the one used to login!
        nonce = request.session.get('oidc_verify_nonce')
        if nonce:
            # Make sure that nonce is not used twice
            del request.session['oidc_verify_nonce']

        # Check for all possible errors and display a message to the user.
        errors = [
            'code' not in request.GET, 'state' not in request.GET,
            'oidc_verify_state' not in request.session,
            request.GET['state'] != request.session['oidc_verify_state']
        ]
        if any(errors):
            msg = 'Something went wrong, account verification failed.'
            messages.error(request, msg)
            return redirect('phonebook:profile_edit')

        token_payload = {
            'client_id':
            self.OIDC_RP_VERIFICATION_CLIENT_ID,
            'client_secret':
            self.OIDC_RP_VERIFICATION_CLIENT_SECRET,
            'grant_type':
            'authorization_code',
            'code':
            request.GET['code'],
            'redirect_uri':
            absolutify(self.request,
                       nonprefixed_url('phonebook:verify_identity_callback')),
        }
        response = requests.post(self.OIDC_OP_TOKEN_ENDPOINT,
                                 data=token_payload,
                                 verify=import_from_settings(
                                     'OIDC_VERIFY_SSL', True))
        response.raise_for_status()
        token_response = response.json()
        id_token = token_response.get('id_token')

        # Verify JWT
        jws = JWS.from_compact(force_bytes(id_token))
        jwk = JWK.load(smart_bytes(self.OIDC_RP_VERIFICATION_CLIENT_SECRET))
        verified_token = None
        if jws.verify(jwk):
            verified_token = jws.payload

        # Create the new Identity Profile.
        if verified_token:
            user_info = json.loads(verified_token)
            email = user_info['email']

            if not user_info.get('email_verified'):
                msg = 'Account verification failed: Email is not verified.'
                messages.error(request, msg)
                return redirect('phonebook:profile_edit')

            user_q = {'auth0_user_id': user_info['sub'], 'email': email}

            # If we are linking GitHub we need to save
            # the username too.
            if 'github|' in user_info['sub']:
                user_q['username'] = user_info['nickname']

            # Check that the identity doesn't exist in another Identity profile
            # or in another mozillians profile
            error_msg = ''
            if IdpProfile.objects.filter(**user_q).exists():
                error_msg = 'Account verification failed: Identity already exists.'
            elif User.objects.filter(email__iexact=email).exclude(
                    pk=profile.user.pk).exists():
                error_msg = 'The email in this identity is used by another user.'
            if error_msg:
                messages.error(request, error_msg)
                next_url = self.request.session.get('oidc_login_next', None)
                return HttpResponseRedirect(
                    next_url or reverse('phonebook:profile_edit'))

            # Save the new identity to the IdpProfile
            user_q['profile'] = profile
            idp, created = IdpProfile.objects.get_or_create(**user_q)

            current_idp = get_object_or_none(IdpProfile,
                                             profile=profile,
                                             primary=True)
            # The new identity is stronger than the one currently used. Let's swap
            append_msg = ''
            # We need to check for equality too in the case a user updates the primary email in
            # the same identity (matching auth0_user_id). If there is an addition of the same type
            # we are not swapping login identities
            if ((current_idp and current_idp.type < idp.type)
                    or (current_idp
                        and current_idp.auth0_user_id == idp.auth0_user_id)
                    or (not current_idp and created
                        and idp.type >= IdpProfile.PROVIDER_GITHUB)):
                IdpProfile.objects.filter(profile=profile).exclude(
                    pk=idp.pk).update(primary=False)
                idp.primary = True
                idp.save()
                # Also update the primary email of the user
                update_email_in_basket(profile.user.email, idp.email)
                User.objects.filter(pk=profile.user.id).update(email=idp.email)
                append_msg = ' You need to use this identity the next time you will login.'

            send_userprofile_to_cis.delay(profile.pk)
            if created:
                msg = 'Account successfully verified.'
                if append_msg:
                    msg += append_msg
                messages.success(request, msg)
            else:
                msg = 'Account verification failed: Identity already exists.'
                messages.error(request, msg)

        next_url = self.request.session.get('oidc_login_next', None)
        return HttpResponseRedirect(next_url
                                    or reverse('phonebook:profile_edit'))
Пример #26
0
 def process_cis_profile(self, request, user_id, *args, **kwargs):
     messages.success(request, 'Profile with id {0} sent to CIS.'.format(user_id))
     send_userprofile_to_cis.delay(user_id)
     return HttpResponseRedirect(reverse('admin:users_userprofile_changelist'))
def delete_groupmembership(sender, instance, **kwargs):
    from mozillians.users.tasks import send_userprofile_to_cis

    send_userprofile_to_cis.delay(instance.userprofile.pk)
Пример #28
0
    def remove_member(self, userprofile, status=None, send_email=False):
        """Change membership status for a group.

        If user is a member of an open group, then the user is removed.

        If a user is a member of a reviewed or closed group,
        then the membership is in a pending state.
        """
        # Avoid circular dependencies
        from mozillians.users.models import UserProfile

        try:
            membership = GroupMembership.objects.get(group=self,
                                                     userprofile=userprofile)
        except GroupMembership.DoesNotExist:
            return
        old_status = membership.status

        # If the group is of type Group.OPEN, delete membership
        # If no status is given, delete membership,
        # If the current membership is PENDING*, delete membership
        if (not status or self.accepting_new_members == Group.OPEN
                or old_status != GroupMembership.MEMBER):
            # We have either an open group or the request to join a reviewed group is denied
            # or the curator manually declined a user in a pending state.
            membership.delete()
            # delete the invitation to the group if exists
            Invite.objects.filter(group=self, redeemer=userprofile).delete()
            send_email = True
            # Remove all the access groups the user is a member of
            # if the group to remove is the NDA
            if self.name == settings.NDA_GROUP:
                group_memberships = GroupMembership.objects.none()
                # If the user is not staff, we need to delete the memberships to any access group
                if not userprofile.can_create_access_groups:
                    group_memberships = GroupMembership.objects.filter(
                        userprofile=userprofile, group__is_access_group=True)

                for access_membership in group_memberships:
                    group = access_membership.group
                    if not group.curator_can_leave(userprofile):
                        # If the user is the only curator, let's add the superusers as curators
                        # as a fallback option
                        for super_user in UserProfile.objects.filter(
                                user__is_superuser=True):
                            group.curators.add(super_user)
                            if not group.has_member(super_user):
                                group.add_member(super_user)
                    group.curators.remove(userprofile)
                    access_membership.delete()
                    # Notify CIS about this change
                    send_userprofile_to_cis.delay(
                        access_membership.userprofile.pk)

            # Notify CIS about this change
            send_userprofile_to_cis.delay(membership.userprofile.pk)

        # Group is either of Group.REVIEWED or Group.CLOSED, change membership to `status`
        else:
            # if we are here, there is a new status for our user
            membership.status = status
            membership.needs_renewal = False
            membership.save()
            send_email = True

        # If group is the NDA group, unsubscribe user from the newsletter.
        if self.name == settings.NDA_GROUP:
            unsubscribe_from_basket_task.delay(
                userprofile.email, [settings.BASKET_NDA_NEWSLETTER])

        if send_email:
            email_membership_change.delay(self.pk, userprofile.user.pk,
                                          old_status, status)
Пример #29
0
def delete_groupmembership(sender, instance, **kwargs):
    from mozillians.users.tasks import send_userprofile_to_cis

    send_userprofile_to_cis.delay(instance.userprofile.pk)
Пример #30
0
    def get(self, request):
        """Callback handler for OIDC authorization code flow.

        This is based on the mozilla-django-oidc library.
        This callback is used to verify the identity added by the user.
        Users are already logged in and we do not care about authentication.
        The JWT token is used to prove the identity of the user.
        """

        profile = request.user.userprofile
        # This is a difference nonce than the one used to login!
        nonce = request.session.get('oidc_verify_nonce')
        if nonce:
            # Make sure that nonce is not used twice
            del request.session['oidc_verify_nonce']

        # Check for all possible errors and display a message to the user.
        errors = [
            'code' not in request.GET,
            'state' not in request.GET,
            'oidc_verify_state' not in request.session,
            request.GET['state'] != request.session['oidc_verify_state']
        ]
        if any(errors):
            msg = 'Something went wrong, account verification failed.'
            messages.error(request, msg)
            return redirect('phonebook:profile_edit')

        token_payload = {
            'client_id': self.OIDC_RP_VERIFICATION_CLIENT_ID,
            'client_secret': self.OIDC_RP_VERIFICATION_CLIENT_SECRET,
            'grant_type': 'authorization_code',
            'code': request.GET['code'],
            'redirect_uri': absolutify(
                self.request,
                nonprefixed_url('phonebook:verify_identity_callback')
            ),
        }
        response = requests.post(self.OIDC_OP_TOKEN_ENDPOINT,
                                 data=token_payload,
                                 verify=import_from_settings('OIDC_VERIFY_SSL', True))
        response.raise_for_status()
        token_response = response.json()
        id_token = token_response.get('id_token')

        # Verify JWT
        jws = JWS.from_compact(force_bytes(id_token))
        jwk = JWK.load(smart_bytes(self.OIDC_RP_VERIFICATION_CLIENT_SECRET))
        verified_token = None
        if jws.verify(jwk):
            verified_token = jws.payload

        # Create the new Identity Profile.
        if verified_token:
            user_info = json.loads(verified_token)
            email = user_info['email']

            if not user_info.get('email_verified'):
                msg = 'Account verification failed: Email is not verified.'
                messages.error(request, msg)
                return redirect('phonebook:profile_edit')

            user_q = {
                'auth0_user_id': user_info['sub'],
                'email': email
            }

            # If we are linking GitHub we need to save
            # the username too.
            if 'github|' in user_info['sub']:
                user_q['username'] = user_info['nickname']

            # Check that the identity doesn't exist in another Identity profile
            # or in another mozillians profile
            error_msg = ''
            if IdpProfile.objects.filter(**user_q).exists():
                error_msg = 'Account verification failed: Identity already exists.'
            elif User.objects.filter(email__iexact=email).exclude(pk=profile.user.pk).exists():
                error_msg = 'The email in this identity is used by another user.'
            if error_msg:
                messages.error(request, error_msg)
                next_url = self.request.session.get('oidc_login_next', None)
                return HttpResponseRedirect(next_url or reverse('phonebook:profile_edit'))

            # Save the new identity to the IdpProfile
            user_q['profile'] = profile
            idp, created = IdpProfile.objects.get_or_create(**user_q)

            current_idp = get_object_or_none(IdpProfile, profile=profile, primary=True)
            # The new identity is stronger than the one currently used. Let's swap
            append_msg = ''
            # We need to check for equality too in the case a user updates the primary email in
            # the same identity (matching auth0_user_id). If there is an addition of the same type
            # we are not swapping login identities
            if ((current_idp and current_idp.type < idp.type) or
                (current_idp and current_idp.auth0_user_id == idp.auth0_user_id) or
                    (not current_idp and created and idp.type >= IdpProfile.PROVIDER_GITHUB)):
                IdpProfile.objects.filter(profile=profile).exclude(pk=idp.pk).update(primary=False)
                idp.primary = True
                idp.save()
                # Also update the primary email of the user
                update_email_in_basket(profile.user.email, idp.email)
                User.objects.filter(pk=profile.user.id).update(email=idp.email)
                append_msg = ' You need to use this identity the next time you will login.'

            send_userprofile_to_cis.delay(profile.pk)
            if created:
                msg = 'Account successfully verified.'
                if append_msg:
                    msg += append_msg
                messages.success(request, msg)
            else:
                msg = 'Account verification failed: Identity already exists.'
                messages.error(request, msg)

        next_url = self.request.session.get('oidc_login_next', None)
        return HttpResponseRedirect(next_url or reverse('phonebook:profile_edit'))