Esempio n. 1
0
    def team_updated(self, team: Team, updated_by: User) -> None:
        """A method to be called after a team has been updated.

        Always call this after you update a team, unless you need to bypass the hook.

        Args:
            team: The team that was updated.
            updated_by: Who updated the team.
        """
        AuditLogService.log(AuditLog.Action.UPDATE, updated_by, team)
Esempio n. 2
0
    def team_deleted(self, team: Team, deleted_by: User) -> None:
        """A method to be called after a team has been deleted.

        Always call this after you delete a team, unless you need to bypass the hook.

        Args:
            team: The team that was deleted.
            deleted_by: Who deleted the team.
        """
        AuditLogService.log(AuditLog.Action.DELETE, deleted_by, team)
Esempio n. 3
0
    def team_created(self, team: Team, created_by: Optional[User]) -> None:
        """A method to be called after a team has been created.

        Always call this after you create a team, unless you need to bypass the hook.

        Args:
            team: The team that was created.
            created_by: Who created the team.
        """
        AuditLogService.log(AuditLog.Action.CREATE, created_by, team)
    def test_update_log_with_changes(self, db):
        person = Person.objects.get(user__email="*****@*****.**")

        person.first_name = "Joe"
        person.last_name = "Doe"
        person.save()

        log = AuditLogService.log(AuditLog.Action.UPDATE, person.user, person)

        assert log.content_object == person
        assert log.actor == person.user
        assert log.action == AuditLog.Action.UPDATE.value
        assert log.object_repr["first_name"] == "Joe"
        assert log.object_repr["last_name"] == "Doe"
        assert {
            "action": "change",
            "key": "first_name",
            "from_value": "John",
            "to_value": "Joe",
        } in log.diff
        assert {
            "action": "change",
            "key": "last_name",
            "from_value": "Smith",
            "to_value": "Doe",
        } in log.diff
Esempio n. 5
0
    def profile_updated(self, request: Optional[HttpRequest], person: Person,
                        updated_by: User) -> None:
        """A method to be called after a profile has been updated.

        Please don't forget to call method this unless you need to bypass it.

        This is the main hook for calling out to other processes which need to happen
        after a profile has been updated.

        Only pass `None` for `request` when the action is made outside a web request,
        i.e, in a management command.

        Args:
            request: The HTTP request related to the action.
            person: The person behind the profile.
            updated_by: The user which updated the profile.
        """
        AuditLogService().log(AuditLog.Action.UPDATE, updated_by, person)

        if request:
            person.edited_or_confirmed_at = timezone.now()
            person.save()

            self.notify_about_changes(request, person)

        # Notify external services
        person_update_notifier.delay(person.id)
Esempio n. 6
0
    def get_context_data(self, **kwargs: dict) -> dict:
        context = super().get_context_data(**kwargs)

        profile = context["profile"]
        roles = profile.roles.select_related("team").all()

        context["roles"] = roles
        context["title"] = profile.full_name

        if roles:
            # TODO: How do we know which team to select as the main one?
            team = roles[0].team
            context["team"] = team
            # TODO: `parent_teams` is common to all views. Perhaps we should
            # refactor this into a common base view or mixin?
            context["parent_teams"] = list(TeamService().get_all_parent_teams(team)) + [
                team
            ]

        if self.request.user == profile.user or self.request.user.has_perm(
            "peoplefinder.view_auditlog"
        ):
            context["profile_audit_log"] = AuditLogService.get_audit_log(profile)
            context["profile_audit_log_excluded_keys"] = [
                "user_id",
                "manager_id",
                "created_at",
                "updated_at",
                "edited_or_confirmed_at",
            ]

        return context
    def test_create_log_with_changes(self, db):
        person = Person.objects.get(user__email="*****@*****.**")

        person.first_name = "Joe"
        person.last_name = "Doe"
        person.save()

        log = AuditLogService.log(AuditLog.Action.CREATE, person.user, person)

        assert log.object_repr["first_name"] == "Joe"
        # Even if there is a difference, a create log always has an empty diff.
        assert log.diff == []
    def test_delete_log_with_changes(self, db):
        person = Person.objects.get(user__email="*****@*****.**")

        person.first_name = "Joe"
        person.last_name = "Doe"
        person.save()

        log = AuditLogService.log(AuditLog.Action.DELETE, person.user, person)

        # A delete log always has an empty object repr.
        assert log.object_repr == {}
        # Even if there is a difference, a delete log always has an empty diff.
        assert log.diff == []
Esempio n. 9
0
    def profile_created(self, person: Person,
                        created_by: Optional[User]) -> None:
        """A method to be called after a profile has been created.

        Please don't forget to call method this unless you need to bypass it.

        This is the main hook for calling out to other processes which need to happen
        after a profile has been created.

        Args:
            person: The person behind the profile.
            created_by: The user which created the profile.
        """
        AuditLogService().log(AuditLog.Action.CREATE, created_by, person)
Esempio n. 10
0
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        # John Smith - normal user
        user_john_smith, _ = User.objects.get_or_create(
            username="******",
            first_name="John",
            last_name="Smith",
            email="*****@*****.**",
            legacy_sso_user_id="john-smith-sso-user-id",
            is_staff=False,
            is_superuser=False,
        )

        if hasattr(user_john_smith, "profile"):
            # We need to delete the profile's audit log separately because the primary
            # key is always the same when using a one-to-one relationship.
            AuditLogService.get_audit_log(user_john_smith.profile).delete()
            user_john_smith.profile.delete()

        call_command("loaddata", "countries.json")
        call_command("create_test_teams")
        call_command("create_user_profiles")
        call_command("create_people_finder_groups")

        user_john_smith.refresh_from_db()

        team_software = Team.objects.get(slug="software")

        user_john_smith.profile.roles.get_or_create(
            team=team_software,
            job_title="Software Engineer",
        )

        PersonService().profile_updated(None, user_john_smith.profile, user_john_smith)

        # Leave this here to check we have reset the db into a known state.
        assert AuditLogService.get_audit_log(user_john_smith.profile).count() == 2
Esempio n. 11
0
    def profile_deleted(self, request: Optional[HttpRequest], person: Person,
                        deleted_by: User) -> None:
        """
        This is the main hook for calling out to other processes which need to
        happen after a profile has been deleted. Please don't forget to call this
        method unless you need to bypass it.

        Args:
            request: The HTTP request related to the action.
            person: The person behind the profile.
            deleted_by: The user which deleted the profile.
        """
        person.is_active = False
        person.became_inactive = timezone.now()
        person.save()

        AuditLogService().log(AuditLog.Action.DELETE, deleted_by, person)

        if request:
            self.notify_about_deletion(person, deleted_by)
Esempio n. 12
0
    def get_context_data(self, **kwargs: dict) -> dict:
        context = super().get_context_data(**kwargs)

        team = context["team"]
        team_service = TeamService()

        context["parent_teams"] = team_service.get_all_parent_teams(team)
        context["sub_teams"] = team_service.get_immediate_child_teams(team)

        if self.request.user.has_perm("peoplefinder.delete_team"):
            (
                context["can_team_be_deleted"],
                context["reasons_team_cannot_be_deleted"],
            ) = team_service.can_team_be_deleted(team)

        # Must be a leaf team.
        if not context["sub_teams"]:
            context["members"] = team.members.all().order_by(
                "person__first_name", "person__last_name")
        else:
            context[
                "people_outside_subteams_count"] = TeamMember.objects.filter(
                    team=team).count()

            # Warning: Multiple requests per sub-team. This might need optimising in the
            # future.
            for sub_team in context["sub_teams"]:
                sub_team.avg_profile_completion = (
                    Person.objects.with_profile_completion().filter(teams__in=[
                        sub_team,
                        *team_service.get_all_child_teams(sub_team),
                    ]).aggregate(
                        Avg("profile_completion"))["profile_completion__avg"])

        if self.request.user.has_perms(
            ["peoplefinder.change_team", "peoplefinder.view_auditlog"]):
            context["team_audit_log"] = AuditLogService.get_audit_log(team)

        return context