Example #1
0
 def get_teams(self, instance: Organization) -> List[Dict[str, Any]]:
     teams = cast(
         List[Dict[str, Any]],
         TeamBasicSerializer(instance.teams.all(),
                             context=self.context,
                             many=True).data)
     visible_teams = [
         team for team in teams
         if team["effective_membership_level"] is not None
     ]
     return visible_teams
Example #2
0
class UserSerializer(serializers.ModelSerializer):

    has_password = serializers.SerializerMethodField()
    is_impersonated = serializers.SerializerMethodField()
    team = TeamBasicSerializer(read_only=True)
    organization = OrganizationSerializer(read_only=True)
    organizations = OrganizationBasicSerializer(many=True, read_only=True)
    set_current_organization = serializers.CharField(write_only=True,
                                                     required=False)
    set_current_team = serializers.CharField(write_only=True, required=False)
    current_password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = User
        fields = [
            "date_joined",
            "uuid",
            "distinct_id",
            "first_name",
            "email",
            "email_opt_in",
            "anonymize_data",
            "toolbar_mode",
            "has_password",
            "is_staff",
            "is_impersonated",
            "team",
            "organization",
            "organizations",
            "set_current_organization",
            "set_current_team",
            "password",
            "current_password",  # used when changing current password
            "events_column_config",
        ]
        extra_kwargs = {
            "date_joined": {
                "read_only": True
            },
            "is_staff": {
                "read_only": True
            },
            "password": {
                "write_only": True
            },
        }

    def get_has_password(self, instance: User) -> bool:
        return instance.has_usable_password()

    def get_is_impersonated(self, _) -> Optional[bool]:
        if "request" not in self.context:
            return None
        return is_impersonated_session(self.context["request"])

    def validate_set_current_organization(self, value: str) -> Organization:
        try:
            organization = Organization.objects.get(id=value)
            if organization.memberships.filter(
                    user=self.context["request"].user).exists():
                return organization
        except Organization.DoesNotExist:
            pass

        raise serializers.ValidationError(
            f"Object with id={value} does not exist.", code="does_not_exist")

    def validate_set_current_team(self, value: str) -> Team:
        try:
            team = Team.objects.get(pk=value)
            if self.context["request"].user.teams.filter(pk=team.pk).exists():
                return team
        except Team.DoesNotExist:
            pass

        raise serializers.ValidationError(
            f"Object with id={value} does not exist.", code="does_not_exist")

    def validate_password_change(self, instance: User,
                                 current_password: Optional[str],
                                 password: Optional[str]) -> Optional[str]:
        if password:
            if instance.password and instance.has_usable_password():
                # If user has a password set, we check it's provided to allow updating it. We need to check that is both
                # usable (properly hashed) and that a password actually exists.
                if not current_password:
                    raise serializers.ValidationError(
                        {
                            "current_password": [
                                "This field is required when updating your password."
                            ]
                        },
                        code="required")

                if not instance.check_password(current_password):
                    raise serializers.ValidationError(
                        {
                            "current_password":
                            ["Your current password is incorrect."]
                        },
                        code="incorrect_password")
            try:
                validate_password(password, instance)
            except ValidationError as e:
                raise serializers.ValidationError({"password": e.messages})

        return password

    def update(self, instance: models.Model, validated_data: Any) -> Any:

        # Update current_organization and current_team
        current_organization = validated_data.pop("set_current_organization",
                                                  None)
        current_team = validated_data.pop("set_current_team", None)
        if current_organization:
            if current_team and not current_organization.teams.filter(
                    pk=current_team.pk).exists():
                raise serializers.ValidationError({
                    "set_current_team": [
                        "Team must belong to the same organization in set_current_organization."
                    ]
                })

            validated_data["current_organization"] = current_organization
            validated_data[
                "current_team"] = current_team if current_team else current_organization.teams.first(
                )
        elif current_team:
            validated_data["current_team"] = current_team
            validated_data["current_organization"] = current_team.organization

        # Update password
        current_password = validated_data.pop("current_password", None)
        password = self.validate_password_change(
            cast(User, instance), current_password,
            validated_data.pop("password", None))

        updated_attrs = list(validated_data.keys())
        instance = cast(User, super().update(instance, validated_data))

        if password:
            instance.set_password(password)
            instance.save()
            update_session_auth_hash(self.context["request"], instance)
            updated_attrs.append("password")

        report_user_updated(instance, updated_attrs)

        return instance

    def to_representation(self, instance: Any) -> Any:
        user_identify.identify_task.delay(user_id=instance.id)
        return super().to_representation(instance)
Example #3
0
class OrganizationSerializer(serializers.ModelSerializer):
    membership_level = serializers.SerializerMethodField()
    setup = (
        serializers.SerializerMethodField()
    )  # Information related to the current state of the onboarding/setup process
    teams = TeamBasicSerializer(many=True, read_only=True)

    class Meta:
        model = Organization
        fields = [
            "id",
            "name",
            "created_at",
            "updated_at",
            "membership_level",
            "personalization",
            "setup",
            "setup_section_2_completed",
            "plugins_access_level",
            "teams",
            "available_features",
        ]
        read_only_fields = [
            "id",
            "created_at",
            "updated_at",
        ]
        extra_kwargs = {
            "setup_section_2_completed": {
                "write_only": True
            }
        }  # `setup` is used for reading this attribute

    def create(self, validated_data: Dict, *args: Any,
               **kwargs: Any) -> Organization:
        serializers.raise_errors_on_nested_writes("create", self,
                                                  validated_data)
        organization, _, _ = Organization.objects.bootstrap(
            self.context["request"].user, **validated_data)
        return organization

    def get_membership_level(
            self, organization: Organization
    ) -> Optional[OrganizationMembership.Level]:
        membership = OrganizationMembership.objects.filter(
            organization=organization,
            user=self.context["request"].user,
        ).first()
        return membership.level if membership is not None else None

    def get_setup(
            self,
            instance: Organization) -> Dict[str, Union[bool, int, str, None]]:

        if not instance.is_onboarding_active:
            # As Section 2 is the last one of the setup process (as of today),
            # if it's completed it means the setup process is done
            return {"is_active": False, "current_section": None}

        non_demo_team_id = next(
            (team.pk for team in instance.teams.filter(is_demo=False)), None)
        any_project_ingested_events = instance.teams.filter(
            is_demo=False, ingested_event=True).exists()
        any_project_completed_snippet_onboarding = instance.teams.filter(
            is_demo=False,
            completed_snippet_onboarding=True,
        ).exists()

        current_section = 1
        if non_demo_team_id and any_project_ingested_events and any_project_completed_snippet_onboarding:
            # All steps from section 1 completed, move on to section 2
            current_section = 2

        return {
            "is_active":
            True,
            "current_section":
            current_section,
            "any_project_ingested_events":
            any_project_ingested_events,
            "any_project_completed_snippet_onboarding":
            any_project_completed_snippet_onboarding,
            "non_demo_team_id":
            non_demo_team_id,
            "has_invited_team_members":
            instance.invites.exists() or instance.members.count() > 1,
        }