Example #1
0
    def create(self, validated_data):
        tenant = self.context.get('request').user.tenant
        contact_count = Contact.objects.filter(is_deleted=False).count()

        if tenant.billing.is_free_plan and contact_count >= settings.FREE_PLAN_ACCOUNT_CONTACT_LIMIT:
            raise serializers.ValidationError({
                'limit_reached': _('You\'ve reached the limit of contacts for the free plan.'),
            })

        instance = super(ContactSerializer, self).create(validated_data)

        credentials = get_credentials('moneybird')

        if has_required_tier(2) and credentials and credentials.integration_context.get('auto_sync'):
            self.send_moneybird_contact(validated_data, instance, credentials)

        # Track newly ceated accounts in segment.
        if not settings.TESTING:
            analytics.track(
                self.context.get('request').user.id,
                'contact-created', {
                    'creation_type': 'automatic' if is_external_referer(self.context.get('request')) else 'manual',
                },
            )

        return instance
Example #2
0
    def validate(self, attrs):
        password = attrs.get('password')
        password_confirmation = attrs.get('password_confirmation')
        email = attrs.get('email')
        webhooks = attrs.get('webhooks')

        if webhooks and not has_required_tier(2):
            raise PermissionDenied

        if self.instance:  # If there's an instance, it means we're updating.
            if password and not password_confirmation:
                raise serializers.ValidationError({
                    'password_confirmation':
                    _('When changing passwords, you need to confirm with your current password.'
                      ),
                })

            if email and email != self.instance.email and not password_confirmation:
                raise serializers.ValidationError({
                    'password_confirmation':
                    _('When changing email adresses, you need to confirm with your current password.'
                      ),
                })

        return super(LilyUserSerializer, self).validate(attrs)
Example #3
0
    def create(self, validated_data):
        tenant = self.context.get('request').user.tenant
        contact_count = Contact.objects.filter(is_deleted=False).count()

        if tenant.billing.is_free_plan and contact_count >= settings.FREE_PLAN_ACCOUNT_CONTACT_LIMIT:
            raise serializers.ValidationError({
                'limit_reached':
                _('You\'ve reached the limit of contacts for the free plan.'),
            })

        instance = super(ContactSerializer, self).create(validated_data)

        credentials = get_credentials('moneybird')

        if has_required_tier(
                2) and credentials and credentials.integration_context.get(
                    'auto_sync'):
            self.send_moneybird_contact(validated_data, instance, credentials)

        # Track newly ceated accounts in segment.
        if not settings.TESTING:
            analytics.track(
                self.context.get('request').user.id,
                'contact-created',
                {
                    'creation_type':
                    'automatic' if is_external_referer(
                        self.context.get('request')) else 'manual',
                },
            )

        return instance
Example #4
0
    def get_context_data(self, **kwargs):
        account_admin = self.request.user.tenant.admin.full_name

        kwargs = super(BaseView, self).get_context_data(**kwargs)
        kwargs.update({
            'INTERCOM_APP_ID': settings.INTERCOM_APP_ID,
            'SENTRY_FRONTEND_PUBLIC_DSN': settings.SENTRY_FRONTEND_PUBLIC_DSN,
            'BILLING_ENABLED': settings.BILLING_ENABLED,
            'SLACK_LILY_CLIENT_ID': settings.SLACK_LILY_CLIENT_ID,
            'DEBUG': settings.DEBUG,
            'account_admin': account_admin,
        })

        if not has_required_tier(1):
            account_count = Account.objects.filter(is_deleted=False).count()
            contact_count = Contact.objects.filter(is_deleted=False).count()
            email_account_count = EmailAccount.objects.filter(
                is_deleted=False).count()

            kwargs.update({
                'limit_reached': {
                    'accounts':
                    account_count >= settings.FREE_PLAN_ACCOUNT_CONTACT_LIMIT,
                    'contacts':
                    contact_count >= settings.FREE_PLAN_ACCOUNT_CONTACT_LIMIT,
                    'email_accounts':
                    email_account_count >=
                    settings.FREE_PLAN_EMAIL_ACCOUNT_LIMIT,
                },
            })

        return kwargs
Example #5
0
    def authenticate(self, request):
        for authenticator_cls in self.authenticators:
            authenticator = authenticator_cls()

            auth_tuple = authenticator.authenticate(request)
            if auth_tuple:
                if isinstance(authenticator,
                              TokenAuthentication) or isinstance(
                                  authenticator, TokenGETAuthentication):
                    if not has_required_tier(2, tenant=auth_tuple[0].tenant):
                        # Tenant is on free plan, so no external API access.
                        raise PermissionDenied({
                            'detail':
                            ('API access has been disabled because you are on the free plan.'
                             'Please upgrade to continue using the API')
                        })
                break
        else:
            # All authentication failed.
            return None

        # Set the current user for the tenant filters.
        set_current_user(auth_tuple[0])

        return auth_tuple
Example #6
0
    def update(self, instance, validated_data):
        if not self.context.get('request').user.is_admin:
            raise PermissionDenied

        if not has_required_tier(1):
            # Settings only allowed for 'Team' plan and higher.
            team_features = ['timelogging_enabled', 'billing_default']

            # Tenant doesn't have the required tier, so check if their API request
            # contains any of the blocked settings.
            if any(x in validated_data for x in team_features):
                raise PermissionDenied

        return super(TenantSerializer, self).update(instance, validated_data)
Example #7
0
    def update(self, instance, validated_data):
        if not self.context.get('request').user.is_admin:
            raise PermissionDenied

        if not has_required_tier(1):
            # Settings only allowed for 'Team' plan and higher.
            team_features = ['timelogging_enabled', 'billing_default']

            # Tenant doesn't have the required tier, so check if their API request
            # contains any of the blocked settings.
            if any(x in validated_data for x in team_features):
                raise PermissionDenied

        return super(TenantSerializer, self).update(instance, validated_data)
Example #8
0
    def create(self, validated_data):
        tenant = self.context.get('request').user.tenant
        contact_count = Contact.objects.filter(is_deleted=False).count()

        if tenant.billing.is_free_plan and contact_count >= settings.FREE_PLAN_ACCOUNT_CONTACT_LIMIT:
            raise serializers.ValidationError({
                'limit_reached':
                _('You\'ve reached the limit of contacts for the free plan.'),
            })

        instance = super(ContactSerializer, self).create(validated_data)

        credentials = get_credentials('moneybird')

        if has_required_tier(
                2) and credentials and credentials.integration_context.get(
                    'auto_sync'):
            self.send_moneybird_contact(validated_data, instance, credentials)

        return instance
Example #9
0
    def update(self, instance, validated_data):
        # Save the current data for later use.
        original_data = {
            'full_name': instance.full_name,
        }

        email_addresses = instance.email_addresses.all()

        if len(email_addresses) == 1:
            original_data.update({
                'original_email_address': email_addresses[0].email_address
            })

        instance = super(ContactSerializer, self).update(instance, validated_data)

        credentials = get_credentials('moneybird')

        if has_required_tier(2) and credentials and credentials.integration_context.get('auto_sync'):
            self.send_moneybird_contact(validated_data, instance, credentials, original_data)

        return instance
Example #10
0
    def token(self, request, *args, **kwargs):
        """
        This view only returns the token of the currently logged in user

        GET returns the current token
        POST generates a new token
        DELETE removes the current token
        """
        if not has_required_tier(2):
            return Response(status=status.HTTP_403_FORBIDDEN)

        if request.method in ('DELETE', 'POST'):
            Token.objects.filter(user=request.user).delete()
            if request.method == 'DELETE':
                return Response(status=status.HTTP_204_NO_CONTENT)
            else:
                Token.objects.create(user=request.user)

        serializer = LilyUserTokenSerializer(request.user)

        return Response(serializer.data)
Example #11
0
    def token(self, request, *args, **kwargs):
        """
        This view only returns the token of the currently logged in user

        GET returns the current token
        POST generates a new token
        DELETE removes the current token
        """
        if not has_required_tier(2):
            return Response(status=status.HTTP_403_FORBIDDEN)

        if request.method in ('DELETE', 'POST'):
            Token.objects.filter(user=request.user).delete()
            if request.method == 'DELETE':
                return Response(status=status.HTTP_204_NO_CONTENT)
            else:
                Token.objects.create(user=request.user)

        serializer = LilyUserTokenSerializer(request.user)

        return Response(serializer.data)
Example #12
0
    def validate(self, data):
        password = data.get('password')
        password_confirmation = data.get('password_confirmation')
        email = data.get('email')
        webhooks = data.get('webhooks', [])

        if webhooks and not has_required_tier(2):
            raise PermissionDenied

        for webhook in webhooks:
            if any(allowed_host in webhook['url']
                   for allowed_host in settings.ALLOWED_HOSTS):
                raise serializers.ValidationError({
                    'url':
                    _('You should set your url to something external to Lily.')
                })

        # If there's an instance, it means we're updating.
        if self.instance:
            # Only validate the current password if the user has a usable_password.
            # Users with social auth don't have one, but they can set one.
            if self.instance.has_usable_password():
                if password and not password_confirmation:
                    raise serializers.ValidationError({
                        'password_confirmation':
                        _('When changing passwords, you need to confirm with your current password.'
                          ),
                    })

                if email and email != self.instance.email and not password_confirmation:
                    raise serializers.ValidationError({
                        'password_confirmation':
                        _('When changing email adresses, you need to confirm with your current password.'
                          ),
                    })

        return super(LilyUserSerializer, self).validate(data)
Example #13
0
    def validate(self, data):
        password = data.get('password')
        password_confirmation = data.get('password_confirmation')
        email = data.get('email')
        webhooks = data.get('webhooks', [])

        if webhooks and not has_required_tier(2):
            raise PermissionDenied

        for webhook in webhooks:
            if any(allowed_host in webhook['url'] for allowed_host in settings.ALLOWED_HOSTS):
                raise serializers.ValidationError({
                    'url': _(
                        'You should set your url to something external to Lily.'
                    )
                })

        # If there's an instance, it means we're updating.
        if self.instance:
            # Only validate the current password if the user has a usable_password.
            # Users with social auth don't have one, but they can set one.
            if self.instance.has_usable_password():
                if password and not password_confirmation:
                    raise serializers.ValidationError({
                        'password_confirmation': _(
                            'When changing passwords, you need to confirm with your current password.'
                        ),
                    })

                if email and email != self.instance.email and not password_confirmation:
                    raise serializers.ValidationError({
                        'password_confirmation': _(
                            'When changing email adresses, you need to confirm with your current password.'
                        ),
                    })

        return super(LilyUserSerializer, self).validate(data)
Example #14
0
    def authenticate(self, request):
        for authenticator_cls in self.authenticators:
            authenticator = authenticator_cls()

            auth_tuple = authenticator.authenticate(request)
            if auth_tuple:
                if isinstance(authenticator, TokenAuthentication) or isinstance(authenticator, TokenGETAuthentication):
                    if not has_required_tier(2, tenant=auth_tuple[0].tenant):
                        # Tenant is on free plan, so no external API access.
                        raise PermissionDenied({
                            'detail': (
                                'API access has been disabled because you are on the free plan.'
                                'Please upgrade to continue using the API'
                            )
                        })
                break
        else:
            # All authentication failed.
            return None

        # Set the current user for the tenant filters.
        set_current_user(auth_tuple[0])

        return auth_tuple
Example #15
0
    def update(self, instance, validated_data):
        # Save the current data for later use.
        original_data = {
            'full_name': instance.full_name,
        }

        email_addresses = instance.email_addresses.all()

        if len(email_addresses) == 1:
            original_data.update(
                {'original_email_address': email_addresses[0].email_address})

        instance = super(ContactSerializer,
                         self).update(instance, validated_data)

        credentials = get_credentials('moneybird')

        if has_required_tier(
                2) and credentials and credentials.integration_context.get(
                    'auto_sync'):
            self.send_moneybird_contact(validated_data, instance, credentials,
                                        original_data)

        return instance
Example #16
0
    def has_permission(self, request, view):
        # We'll just put the default tier at two since that's the most common requirement.
        required_tier = getattr(view, 'required_tier', 2)

        # API access isn't available for the free and cheapest paid plan.
        return has_required_tier(required_tier)