Ejemplo n.º 1
0
def invite_web_user(request, domain, template="users/invite_web_user.html"):
    role_choices = UserRole.role_choices(domain)
    if request.method == "POST":
        form = AdminInvitesUserForm(request.POST,
            excluded_emails=[user.username for user in WebUser.by_domain(domain)],
            role_choices=role_choices
        )
        if form.is_valid():
            data = form.cleaned_data
            # create invitation record
            data["invited_by"] = request.couch_user.user_id
            data["invited_on"] = datetime.utcnow()
            data["domain"] = domain
            invite = Invitation(**data)
            invite.save()
            invite.send_activation_email()
            messages.success(request, "Invitation sent to %s" % invite.email)
            return HttpResponseRedirect(reverse("web_users", args=[domain]))
    else:
        form = AdminInvitesUserForm(role_choices=role_choices)
    context = _users_context(request, domain)
    context.update(
        registration_form=form
    )
    return render_to_response(request, template, context)
Ejemplo n.º 2
0
 def test_existing_user_invitation_accepted(self):
     """
     SsoBackend should create a new user if the username passed to does
     not exist and the email domain matches an AuthenticatedEmailDomain
     for the given IdentityProvider. It should also ensure that any
     user data from a registration form and/or the samlUserdata are all
     properly saved to the User model.
     """
     admin_role = StaticRole.domain_admin(domain=self.domain.name)
     existing_user = WebUser.create(None, '*****@*****.**', 'testpwd',
                                    None, None)
     invitation = Invitation(
         domain=self.domain.name,
         email=existing_user.username,
         invited_by=self.user.couch_id,
         invited_on=datetime.datetime.utcnow(),
         role=admin_role.get_qualified_id(),
     )
     invitation.save()
     AsyncSignupRequest.create_from_invitation(invitation)
     user = auth.authenticate(
         request=self.request,
         username=invitation.email,
         idp_slug=self.idp.slug,
         is_handshake_successful=True,
     )
     self.assertIsNotNone(user)
     self.assertEqual(user.username, invitation.email)
     self.assertEqual(self.request.sso_new_user_messages['success'], [
         f'You have been added to the "{invitation.domain}" project space.',
     ])
Ejemplo n.º 3
0
 def test_new_user_created_and_expired_invitation_declined(self):
     """
     When SsoBackend creates a new user and an EXPIRED invitation is present,
     a new user should still be created, but the invitation should be declined.
     """
     invitation = Invitation(
         domain=self.domain.name,
         email='*****@*****.**',
         invited_by=self.user.couch_id,
         invited_on=datetime.datetime.utcnow() - relativedelta(months=2),
     )
     invitation.save()
     AsyncSignupRequest.create_from_invitation(invitation)
     generator.store_full_name_in_saml_user_data(self.request, 'Zee', 'Bos')
     user = auth.authenticate(
         request=self.request,
         username=invitation.email,
         idp_slug=self.idp.slug,
         is_handshake_successful=True,
     )
     self.assertIsNotNone(user)
     self.assertEqual(user.username, invitation.email)
     self.assertEqual(user.first_name, 'Zee')
     self.assertEqual(user.last_name, 'Bos')
     self.assertEqual(self.request.sso_new_user_messages['success'], [
         f'User account for {invitation.email} created.',
     ])
     self.assertEqual(self.request.sso_new_user_messages['error'], [
         'Could not accept invitation because it is expired.',
     ])
Ejemplo n.º 4
0
 def test_new_user_created_and_invitation_accepted(self):
     """
     When SsoBackend creates a new user and an invitation is present, that
     invitation should add the user to the invited project
     space and accept the invitation
     """
     admin_role = StaticRole.domain_admin(self.domain.name)
     invitation = Invitation(
         domain=self.domain.name,
         email='*****@*****.**',
         invited_by=self.user.couch_id,
         invited_on=datetime.datetime.utcnow(),
         role=admin_role.get_qualified_id(),
     )
     invitation.save()
     AsyncSignupRequest.create_from_invitation(invitation)
     generator.store_full_name_in_saml_user_data(self.request, 'Isa',
                                                 'Baas')
     user = auth.authenticate(
         request=self.request,
         username=invitation.email,
         idp_slug=self.idp.slug,
         is_handshake_successful=True,
     )
     self.assertIsNotNone(user)
     self.assertEqual(user.username, invitation.email)
     self.assertEqual(user.first_name, 'Isa')
     self.assertEqual(user.last_name, 'Baas')
     self.assertEqual(self.request.sso_new_user_messages['success'], [
         f'User account for {invitation.email} created.',
         f'You have been added to the "{invitation.domain}" project space.',
     ])
Ejemplo n.º 5
0
 def setUpClass(cls):
     cls.invitations = []
     for kwargs in [
         {'domain': 'domain_1', 'email': '*****@*****.**'},
         {'domain': 'domain_1', 'email': '*****@*****.**', 'is_accepted': True},
         {'domain': 'domain_2', 'email': '*****@*****.**'},
     ]:
         inv = Invitation(**kwargs)
         inv.save()
         cls.invitations.append(inv)
Ejemplo n.º 6
0
 def setUpClass(cls):
     super(InvitationTest, cls).setUpClass()
     cls.invitations = []
     for kwargs in [
         {'domain': 'domain_1', 'email': '*****@*****.**'},
         {'domain': 'domain_1', 'email': '*****@*****.**', 'is_accepted': True},
         {'domain': 'domain_2', 'email': '*****@*****.**'},
     ]:
         inv = Invitation(**kwargs)
         inv.save()
         cls.invitations.append(inv)
Ejemplo n.º 7
0
    def post(self, request, *args, **kwargs):
        if self.invite_web_user_form.is_valid():
            # If user exists and has already requested access, just add them to the project
            # Otherwise, send an invitation
            create_invitation = True
            data = self.invite_web_user_form.cleaned_data
            domain_request = DomainRequest.by_email(self.domain, data["email"])
            if domain_request is not None:
                domain_request.is_approved = True
                domain_request.save()
                user = CouchUser.get_by_username(domain_request.email)
                if user is not None:
                    domain_request.send_approval_email()
                    create_invitation = False
                    user.add_as_web_user(self.domain,
                                         role=data["role"],
                                         location_id=data.get(
                                             "supply_point", None),
                                         program_id=data.get("program", None))
                messages.success(request, "%s added." % data["email"])
            else:
                track_workflow(request.couch_user.get_email(),
                               "Sent a project invitation",
                               {"Sent a project invitation": "yes"})
                meta = get_meta(request)
                track_sent_invite_on_hubspot.delay(request.couch_user,
                                                   request.COOKIES, meta)
                messages.success(request,
                                 "Invitation sent to %s" % data["email"])

            if create_invitation:
                data["invited_by"] = request.couch_user.user_id
                data["invited_on"] = datetime.utcnow()
                data["domain"] = self.domain
                invite = Invitation(**data)
                invite.save()
                invite.send_activation_email()
            return HttpResponseRedirect(
                reverse(ListWebUsersView.urlname, args=[self.domain]))
        return self.get(request, *args, **kwargs)
Ejemplo n.º 8
0
    def post(self, request, *args, **kwargs):
        if self.invite_web_user_form.is_valid():
            # If user exists and has already requested access, just add them to the project
            # Otherwise, send an invitation
            create_invitation = True
            data = self.invite_web_user_form.cleaned_data
            domain_request = DomainRequest.by_email(self.domain, data["email"])
            if domain_request is not None:
                domain_request.is_approved = True
                domain_request.save()
                user = CouchUser.get_by_username(domain_request.email)
                if user is not None:
                    domain_request.send_approval_email()
                    create_invitation = False
                    user.add_as_web_user(self.domain, role=data["role"],
                                         location_id=data.get("supply_point", None),
                                         program_id=data.get("program", None))
                messages.success(request, "%s added." % data["email"])
            else:
                track_workflow(request.couch_user.get_email(),
                               "Sent a project invitation",
                               {"Sent a project invitation": "yes"})
                meta = get_meta(request)
                track_sent_invite_on_hubspot.delay(request.couch_user, request.COOKIES, meta)
                messages.success(request, "Invitation sent to %s" % data["email"])

            if create_invitation:
                data["invited_by"] = request.couch_user.user_id
                data["invited_on"] = datetime.utcnow()
                data["domain"] = self.domain
                invite = Invitation(**data)
                invite.save()
                invite.send_activation_email()
            return HttpResponseRedirect(reverse(
                ListWebUsersView.urlname,
                args=[self.domain]
            ))
        return self.get(request, *args, **kwargs)
Ejemplo n.º 9
0
def create_or_update_users_and_groups(upload_domain, user_specs, upload_user, group_memoizer=None, update_progress=None):
    domain_info_by_domain = {}

    def _get_domain_info(domain):
        domain_info = domain_info_by_domain.get(domain)
        if domain_info:
            return domain_info
        if domain == upload_domain:
            domain_group_memoizer = group_memoizer or GroupMemoizer(domain)
        else:
            domain_group_memoizer = GroupMemoizer(domain)
        domain_group_memoizer.load_all()
        can_assign_locations = domain_has_privilege(domain, privileges.LOCATIONS)
        location_cache = None
        if can_assign_locations:
            location_cache = SiteCodeToLocationCache(domain)

        domain_obj = Domain.get_by_name(domain)
        allowed_group_names = [group.name for group in domain_group_memoizer.groups]
        roles_by_name = {role.name: role for role in UserRole.by_domain(domain)}
        domain_user_specs = [spec for spec in user_specs if spec.get('domain', upload_domain) == domain]
        validators = get_user_import_validators(
            domain_obj,
            domain_user_specs,
            allowed_group_names,
            list(roles_by_name),
            upload_domain
        )

        domain_info = DomainInfo(
            validators,
            can_assign_locations,
            location_cache,
            roles_by_name,
            domain_group_memoizer
        )
        domain_info_by_domain[domain] = domain_info
        return domain_info

    ret = {"errors": [], "rows": []}

    current = 0

    try:
        for row in user_specs:
            if update_progress:
                update_progress(current)
                current += 1

            username = row.get('username')
            domain = row.get('domain') or upload_domain
            username = normalize_username(str(username), domain) if username else None
            status_row = {
                'username': username,
                'row': row,
            }

            domain_info = _get_domain_info(domain)

            try:
                for validator in domain_info.validators:
                    validator(row)
            except UserUploadError as e:
                status_row['flag'] = str(e)
                ret['rows'].append(status_row)
                continue

            data = row.get('data')
            email = row.get('email')
            group_names = list(map(str, row.get('group') or []))
            language = row.get('language')
            name = row.get('name')
            password = row.get('password')
            phone_number = row.get('phone-number')
            uncategorized_data = row.get('uncategorized_data')
            user_id = row.get('user_id')
            location_codes = row.get('location_code') or []
            if location_codes and not isinstance(location_codes, list):
                location_codes = [location_codes]
            # ignore empty
            location_codes = [code for code in location_codes if code]
            role = row.get('role', None)
            web_user = row.get('web_user')

            try:
                password = str(password) if password else None

                is_active = spec_value_to_boolean_or_none(row, 'is_active')
                is_account_confirmed = spec_value_to_boolean_or_none(row, 'is_account_confirmed')
                send_account_confirmation_email = spec_value_to_boolean_or_none(row, 'send_confirmation_email')
                remove_web_user = spec_value_to_boolean_or_none(row, 'remove_web_user')

                if user_id:
                    user = CommCareUser.get_by_user_id(user_id, domain)
                    if not user:
                        raise UserUploadError(_(
                            "User with ID '{user_id}' not found"
                        ).format(user_id=user_id, domain=domain))

                    if username and user.username != username:
                        raise UserUploadError(_(
                            'Changing usernames is not supported: %(username)r to %(new_username)r'
                        ) % {'username': user.username, 'new_username': username})

                    # note: explicitly not including "None" here because that's the default value if not set.
                    # False means it was set explicitly to that value
                    if is_account_confirmed is False and not web_user:
                        raise UserUploadError(_(
                            "You can only set 'Is Account Confirmed' to 'False' on a new User."
                        ))

                    if is_password(password):
                        user.set_password(password)
                        # overwrite password in results so we do not save it to the db
                        status_row['row']['password'] = '******'
                    status_row['flag'] = 'updated'
                else:
                    kwargs = {}
                    if is_account_confirmed is not None and not web_user:
                        kwargs['is_account_confirmed'] = is_account_confirmed
                    user = CommCareUser.create(domain, username, password, created_by=upload_user,
                                               created_via=USER_CHANGE_VIA_BULK_IMPORTER, commit=False, **kwargs)
                    status_row['flag'] = 'created'

                if phone_number:
                    user.add_phone_number(_fmt_phone(phone_number), default=True)
                if name:
                    user.set_full_name(str(name))
                if data:
                    user.user_data.update(data)
                if uncategorized_data:
                    user.user_data.update(uncategorized_data)
                if language:
                    user.language = language
                if email:
                    user.email = email.lower()
                if is_active is not None:
                    user.is_active = is_active

                if domain_info.can_assign_locations:
                    # Do this here so that we validate the location code before we
                    # save any other information to the user, this way either all of
                    # the user's information is updated, or none of it
                    location_ids = []
                    for code in location_codes:
                        loc = get_location_from_site_code(code, domain_info.location_cache)
                        location_ids.append(loc.location_id)

                    locations_updated = set(user.assigned_location_ids) != set(location_ids)
                    primary_location_removed = (user.location_id and not location_ids or
                                                user.location_id not in location_ids)

                    if primary_location_removed:
                        user.unset_location(commit=False)
                    if locations_updated:
                        user.reset_locations(location_ids, commit=False)

                if role:
                    role_qualified_id = domain_info.roles_by_name[role].get_qualified_id()
                    user.set_role(domain, role_qualified_id)

                if web_user:
                    user.user_data.update({'login_as_user': web_user})

                user.save()

                if web_user:
                    if not upload_user.can_edit_web_users():
                        raise UserUploadError(_(
                            "Only users with the edit web users permission can upload web users"
                        ))
                    current_user = CouchUser.get_by_username(web_user)
                    if remove_web_user:
                        if not current_user or not current_user.is_member_of(domain):
                            raise UserUploadError(_(
                                "You cannot remove a web user that is not a member of this project. {web_user} is not a member.").format(web_user=web_user)
                            )
                        else:
                            current_user.delete_domain_membership(domain)
                            current_user.save()
                    else:
                        if not role:
                            raise UserUploadError(_(
                                "You cannot upload a web user without a role. {web_user} does not have a role").format(web_user=web_user)
                            )
                        if not current_user and is_account_confirmed:
                            raise UserUploadError(_(
                                "You can only set 'Is Account Confirmed' to 'True' on an existing Web User. {web_user} is a new username.").format(web_user=web_user)
                            )
                        if current_user and not current_user.is_member_of(domain) and is_account_confirmed:
                            current_user.add_as_web_user(domain, role=role_qualified_id, location_id=user.location_id)
                        elif not current_user or not current_user.is_member_of(domain):
                            invite_data = {
                                'email': web_user,
                                'invited_by': upload_user.user_id,
                                'invited_on': datetime.utcnow(),
                                'domain': domain,
                                'role': role_qualified_id,
                                'supply_point': user.location_id
                            }
                            invite = Invitation(**invite_data)
                            invite.save()
                            if send_account_confirmation_email:
                                invite.send_activation_email()

                        elif current_user.is_member_of(domain):
                            # edit existing user in the domain
                            current_user.set_role(domain, role_qualified_id)
                            if user.location_id:
                                current_user.set_location(domain, user.location_id)
                            else:
                                current_user.unset_location(domain)
                            current_user.save()

                if send_account_confirmation_email and not web_user:
                    send_account_confirmation_if_necessary(user)

                if is_password(password):
                    # Without this line, digest auth doesn't work.
                    # With this line, digest auth works.
                    # Other than that, I'm not sure what's going on
                    # Passing use_primary_db=True because of https://dimagi-dev.atlassian.net/browse/ICDS-465
                    user.get_django_user(use_primary_db=True).check_password(password)

                for group in domain_info.group_memoizer.by_user_id(user.user_id):
                    if group.name not in group_names:
                        group.remove_user(user)

                for group_name in group_names:
                    domain_info.group_memoizer.by_name(group_name).add_user(user, save=False)

            except (UserUploadError, CouchUser.Inconsistent) as e:
                status_row['flag'] = str(e)

            ret["rows"].append(status_row)
    finally:
        try:
            for domain_info in domain_info_by_domain.values():
                domain_info.group_memoizer.save_all()
        except BulkSaveError as e:
            _error_message = (
                "Oops! We were not able to save some of your group changes. "
                "Please make sure no one else is editing your groups "
                "and try again."
            )
            logging.exception((
                'BulkSaveError saving groups. '
                'User saw error message "%s". Errors: %s'
            ) % (_error_message, e.errors))
            ret['errors'].append(_error_message)

    return ret
Ejemplo n.º 10
0
    def setUpClass(cls):
        super().setUpClass()

        plan = DefaultProductPlan.get_default_plan_version(
            edition=SoftwarePlanEdition.ADVANCED)

        cls.blocked_account = generator.billing_account(
            '*****@*****.**', '*****@*****.**')
        cls.blocked_account.block_hubspot_data_for_all_users = True
        cls.blocked_account.save()

        # this is one domain linked to the billing account that blocks hubspot
        cls.blocked_domain = create_domain('block-domain-hubspot')
        first_blocked_sub = Subscription.new_domain_subscription(
            cls.blocked_account, cls.blocked_domain.name, plan)
        first_blocked_sub.is_active = True
        first_blocked_sub.save()

        # this is another domain linked to the billing account that blocks hubspot
        cls.second_blocked_domain = create_domain('block-domain-hubspot-002')
        second_blocked_sub = Subscription.new_domain_subscription(
            cls.blocked_account, cls.second_blocked_domain.name, plan)
        second_blocked_sub.is_active = True
        second_blocked_sub.save()

        # this domain is not linked to an account that is blocking hubspot
        cls.allowed_domain = create_domain('allow-domain-hubspot')
        allowed_account = generator.billing_account('*****@*****.**',
                                                    '*****@*****.**')
        allowed_sub = Subscription.new_domain_subscription(
            allowed_account, cls.allowed_domain.name, plan)
        allowed_sub.is_active = True
        allowed_sub.save()

        cls.allowed_user = WebUser.create(cls.allowed_domain.name,
                                          '*****@*****.**', '*****', None,
                                          None)
        cls.allowed_user.save()

        cls.blocked_user = WebUser.create(cls.blocked_domain.name,
                                          '*****@*****.**', '*****', None,
                                          None)
        cls.blocked_user.save()
        cls.blocked_couch_user = CouchUser.get_by_username(
            cls.blocked_user.username)

        cls.second_blocked_user = WebUser.create(
            cls.second_blocked_domain.name, '*****@*****.**', '*****',
            None, None)
        cls.second_blocked_user.save()
        cls.second_blocked_couch_user = CouchUser.get_by_username(
            cls.second_blocked_user.username)

        cls.blocked_invitation_user = WebUser.create(
            cls.blocked_domain.name, '*****@*****.**',
            '*****', None, None)
        invite_to_blocked_domain = Invitation(
            email=cls.blocked_invitation_user.username,
            is_accepted=True,
            domain=cls.blocked_domain.name,
            invited_on=datetime.now(),
            invited_by="*****@*****.**",
        )
        invite_to_blocked_domain.save()

        cls.blocked_commcare_user = CommCareUser.create(
            cls.blocked_domain.name, 'testuser', '****', None, None)
        cls.blocked_commcare_user.save()