Esempio n. 1
0
def modify_existing_user_in_domain(domain,
                                   domain_info,
                                   location_codes,
                                   membership,
                                   role_qualified_id,
                                   upload_user,
                                   current_user,
                                   max_tries=3):
    if domain_info.can_assign_locations and location_codes is not None:
        location_ids = find_location_id(location_codes,
                                        domain_info.location_cache)
        locations_updated, primary_loc_removed = check_modified_user_loc(
            location_ids, membership.location_id,
            membership.assigned_location_ids)
        if primary_loc_removed:
            current_user.unset_location(domain, commit=False)
        if locations_updated:
            current_user.reset_locations(domain, location_ids, commit=False)
    user_current_role = current_user.get_role(domain=domain)
    role_updated = not (user_current_role
                        and user_current_role.get_qualified_id()
                        == role_qualified_id)
    if role_updated:
        current_user.set_role(domain, role_qualified_id)
        log_user_role_update(domain, current_user, upload_user,
                             USER_CHANGE_VIA_BULK_IMPORTER)
    try:
        current_user.save()
    except ResourceConflict:
        notify_exception(None,
                         message="ResouceConflict during web user import",
                         details={
                             'domain': domain,
                             'username': current_user.username
                         })
        if max_tries > 0:
            current_user.clear_quickcache_for_user()
            updated_user = CouchUser.get_by_username(current_user.username,
                                                     strict=True)
            modify_existing_user_in_domain(domain,
                                           domain_info,
                                           location_codes,
                                           membership,
                                           role_qualified_id,
                                           upload_user,
                                           updated_user,
                                           max_tries=max_tries - 1)
        else:
            raise
Esempio n. 2
0
    def update_user(self):
        is_update_successful = super(UpdateUserRoleForm, self).update_user(save=False)

        if self.domain and 'role' in self.cleaned_data:
            role = self.cleaned_data['role']
            try:
                self.existing_user.set_role(self.domain, role)
                if self.existing_user.is_commcare_user():
                    self.existing_user.save(spawn_task=True)
                else:
                    self.existing_user.save()
                is_update_successful = True
                log_user_role_update(self.domain, self.existing_user, self.request.user, USER_CHANGE_VIA_WEB)
            except KeyError:
                pass
        elif is_update_successful:
            self.existing_user.save()

        return is_update_successful
Esempio n. 3
0
def create_or_update_users_and_groups(upload_domain,
                                      user_specs,
                                      upload_user,
                                      group_memoizer=None,
                                      update_progress=None):
    domain_info_by_domain = {}

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

    current = 0

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

            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, upload_domain, user_specs,
                                          domain_info_by_domain,
                                          group_memoizer)

            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',
                                     []) if 'location_code' in row else None
            location_codes = format_location_codes(location_codes)
            role = row.get('role', None)
            profile = row.get('user_profile', 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))
                    check_changing_username(user, 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)
                    log_user_create = True
                    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))

                # Add in existing data. Don't use metadata - we don't want to add profile-controlled fields.
                for key, value in user.user_data.items():
                    if key not in data:
                        data[key] = value
                if profile:
                    profile_obj = domain_info.profiles_by_name[profile]
                    data[PROFILE_SLUG] = profile_obj.id
                    for key in profile_obj.fields.keys():
                        user.pop_metadata(key)
                try:
                    user.update_metadata(data)
                except ValueError as e:
                    raise UserUploadError(str(e))
                if uncategorized_data:
                    user.update_metadata(uncategorized_data)

                # Clear blank user data so that it can be purged by remove_unused_custom_fields_from_users_task
                for key in dict(data, **uncategorized_data):
                    value = user.metadata[key]
                    if value is None or value == '':
                        user.pop_metadata(key)

                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 and location_codes is not None:
                    # 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

                    # Do not update location info if the column is not included at all
                    location_ids = find_location_id(location_codes,
                                                    domain_info.location_cache)
                    locations_updated, primary_loc_removed = check_modified_user_loc(
                        location_ids, user.location_id,
                        user.assigned_location_ids)
                    if primary_loc_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_current_role = user.get_role(domain=domain)
                    log_role_update = not (
                        user_current_role
                        and user_current_role.get_qualified_id()
                        == role_qualified_id)
                    if log_role_update:
                        user.set_role(domain, role_qualified_id)

                if web_user:
                    user.update_metadata({'login_as_user': web_user})

                user.save()
                if log_user_create:
                    user.log_user_create(upload_user,
                                         USER_CHANGE_VIA_BULK_IMPORTER)
                if log_role_update:
                    log_user_role_update(domain, user, upload_user,
                                         USER_CHANGE_VIA_BULK_IMPORTER)
                if web_user:
                    check_can_upload_web_users(upload_user)
                    current_user = CouchUser.get_by_username(web_user)
                    if remove_web_user:
                        remove_web_user_from_domain(domain, current_user,
                                                    username, upload_user)
                    else:
                        check_user_role(username, role)
                        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):
                            create_or_update_web_user_invite(
                                web_user,
                                domain,
                                role_qualified_id,
                                upload_user,
                                user.location_id,
                                send_email=send_account_confirmation_email)

                        elif current_user.is_member_of(domain):
                            # edit existing user in the domain
                            current_user.set_role(domain, role_qualified_id)
                            if location_codes is not None:
                                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
Esempio n. 4
0
def create_or_update_users_and_groups(upload_domain,
                                      user_specs,
                                      upload_user,
                                      group_memoizer=None,
                                      update_progress=None):
    from corehq.apps.users.views.mobile.custom_data_fields import UserFieldsView
    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)
        }
        profiles_by_name = {}
        definition = CustomDataFieldsDefinition.get(domain,
                                                    UserFieldsView.field_type)
        if definition:
            profiles_by_name = {
                profile.name: profile
                for profile in definition.get_profiles()
            }
        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),
                                                list(profiles_by_name),
                                                upload_domain)

        domain_info = DomainInfo(validators, can_assign_locations,
                                 location_cache, roles_by_name,
                                 profiles_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
            log_user_create = False
            log_role_update = False

            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)
            profile = row.get('user_profile', 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)
                    log_user_create = True
                    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))

                # Add in existing data. Don't use metadata - we don't want to add profile-controlled fields.
                for key, value in user.user_data.items():
                    if key not in data:
                        data[key] = value
                if profile:
                    profile_obj = domain_info.profiles_by_name[profile]
                    data[PROFILE_SLUG] = profile_obj.id
                    for key in profile_obj.fields.keys():
                        user.pop_metadata(key)
                try:
                    user.update_metadata(data)
                except ValueError as e:
                    raise UserUploadError(str(e))
                if uncategorized_data:
                    user.update_metadata(uncategorized_data)

                # Clear blank user data so that it can be purged by remove_unused_custom_fields_from_users_task
                for key in dict(data, **uncategorized_data):
                    value = user.metadata[key]
                    if value is None or value == '':
                        user.pop_metadata(key)

                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_current_role = user.get_role(domain=domain)
                    log_role_update = not (
                        user_current_role
                        and user_current_role.get_qualified_id()
                        == role_qualified_id)
                    if log_role_update:
                        user.set_role(domain, role_qualified_id)

                if web_user:
                    user.update_metadata({'login_as_user': web_user})

                user.save()
                if log_user_create:
                    user.log_user_create(upload_user,
                                         USER_CHANGE_VIA_BULK_IMPORTER)
                if log_role_update:
                    log_user_role_update(domain, user, upload_user,
                                         USER_CHANGE_VIA_BULK_IMPORTER)
                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, invite_created = Invitation.objects.update_or_create(
                                email=web_user,
                                domain=domain,
                                defaults={
                                    'invited_by': upload_user.user_id,
                                    'invited_on': datetime.utcnow(),
                                    'supply_point': user.location_id,
                                    'role': role_qualified_id
                                },
                            )
                            if invite_created:
                                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