Esempio n. 1
0
    def call_method_view(self, request, method, view, *args, **kwargs):
        """Call the given method view.

        The default behaviour is to call the given ``view`` passing in all
        ``args`` and ``kwargs``. However, Review Board allows certain resources
        to be disabled by setting the :py:attr:`~required_features` attribute.
        If a feature specified in that list is disabled, this method will
        return a 403 Forbidden response instead of calling the method view.

        In addition, Review Board has token access policies. If the client is
        authenticated with an API token, the token's access policies will be
        checked before calling the view. If the operation is disallowed, a 403
        Forbidden response will be returned.

        If read-only mode is enabled, all PUT, POST, and DELETE requests will
        be rejected with a 503 Service Unavailable response, unless the user
        is a superuser.

        Only if all these conditions are met will the view actually be called.

        Args:
            request (django.http.HttpRequest):
                The current HTTP request.

            method (unicode):
                The HTTP method.

            view (callable):
                The view.

            *args (tuple):
                Additional positional arguments.

            **kwargs (dict):
                Additional keyword arguments.

        Returns:
            WebAPIError or tuple:
            Either a 403 Forbidden error or the result of calling the method
            view, which will either be a
            :py:class:`~djblets.webapi.errors.WebAPIError` or a 2-tuple of the
            HTTP status code and a dict indicating the JSON response from the
            view.
        """
        for feature in self.required_features:
            if not feature.is_enabled(request=request):
                logging.warning('Disallowing %s for API resource %r because '
                                'feature %s is not enabled',
                                method, self, feature.feature_id,
                                request=request)
                return PERMISSION_DENIED

        if (is_site_read_only_for(request.user) and
            request.method not in ('GET', 'HEAD', 'OPTIONS')):
            return READ_ONLY_ERROR

        return super(WebAPIResource, self).call_method_view(
            request, method, view, *args, **kwargs)
Esempio n. 2
0
    def is_deletable_by(self, user):
        """Return whether the user can delete this review request.

        Args:
            user (django.contrib.auth.models.User):
                The user to check.

        Returns:
            bool:
            Whether the user can delete this review request.
        """
        return (user.has_perm('reviews.delete_reviewrequest')
                and not is_site_read_only_for(user))
Esempio n. 3
0
    def should_render(self, context):
        """Return whether the action should render.

        Args:
            context (dict):
                The current render context.

        Returns:
            bool:
            Whether the action should render.
        """
        return (context['perms']['reviews']['delete_reviewrequest'] and
                not is_site_read_only_for(context['request'].user))
Esempio n. 4
0
    def should_render(self, context):
        """Return whether the action should render.

        Args:
            context (dict):
                The current render context.

        Returns:
            bool:
            Whether the action should render.
        """
        user = context['request'].user
        return (user.is_authenticated() and not is_site_read_only_for(user))
Esempio n. 5
0
    def should_render(self, context):
        """Return whether the action should render.

        Args:
            context (dict):
                The current render context.

        Returns:
            bool:
            Whether the action should render.
        """
        return (context['perms']['reviews']['delete_reviewrequest']
                and not is_site_read_only_for(context['request'].user))
Esempio n. 6
0
    def is_deletable_by(self, user):
        """Return whether the user can delete this review request.

        Args:
            user (django.contrib.auth.models.User):
                The user to check.

        Returns:
            bool:
            Whether the user can delete this review request.
        """
        return (user.has_perm('reviews.delete_reviewrequest') and
                not is_site_read_only_for(user))
Esempio n. 7
0
    def is_status_mutable_by(self, user):
        """Return whether the user can modify this review request's status.

        Args:
            user (django.contrib.auth.models.User):
                The user to check.

        Returns:
            bool:
            Whether the user can modify this review request's status.
        """
        return ((self.submitter == user or user.has_perm(
            'reviews.can_change_status', self.local_site))
                and not is_site_read_only_for(user))
Esempio n. 8
0
    def should_render(self, context):
        """Return whether the action should render.

        Args:
            context (dict):
                The current render context.

        Returns:
            bool:
            Whether the action should render.
        """
        user = context['request'].user
        return (user.is_authenticated() and
                not is_site_read_only_for(user))
Esempio n. 9
0
    def is_status_mutable_by(self, user):
        """Return whether the user can modify this review request's status.

        Args:
            user (django.contrib.auth.models.User):
                The user to check.

        Returns:
            bool:
            Whether the user can modify this review request's status.
        """
        return ((self.submitter == user or
                 user.has_perm('reviews.can_change_status',
                               self.local_site)) and
                not is_site_read_only_for(user))
Esempio n. 10
0
    def save(self, *args, **kwargs):
        """Save the profile to the database.

        The profile will only be saved if the user is not affected by read-only
        mode.

        Args:
            *args (tuple):
                Positional arguments to pass through to the superclass.

            **kwargs (dict):
                Keyword arguments to pass through to the superclass.
        """
        if not is_site_read_only_for(self.user):
            super(Profile, self).save(*args, **kwargs)
Esempio n. 11
0
    def save(self, *args, **kwargs):
        """Save the profile to the database.

        The profile will only be saved if the user is not affected by read-only
        mode.

        Args:
            *args (tuple):
                Positional arguments to pass through to the superclass.

            **kwargs (dict):
                Keyword arguments to pass through to the superclass.
        """
        if not is_site_read_only_for(self.user):
            super(Profile, self).save(*args, **kwargs)
Esempio n. 12
0
    def should_render(self, context):
        """Return whether or not this action should render.

        If the corresponding review request has a repository, then an upload
        diff form exists, so we should render this UploadDiffAction.

        Args:
            context (django.template.Context):
                The collection of key-value pairs available in the template
                just before this action is to be rendered.

        Returns:
            bool: Determines if this action should render.
        """
        return (context['review_request'].repository_id is not None and
                not is_site_read_only_for(context['request'].user))
Esempio n. 13
0
    def should_render(self, context):
        """Return whether the action should render.

        Args:
            context (dict):
                The current render context.

        Returns:
            bool:
            Whether the action should render.
        """
        request = context['request']
        user = request.user

        return (user.is_authenticated() and not is_site_read_only_for(user)
                and general_comments_feature.is_enabled(request=request))
Esempio n. 14
0
    def should_render(self, context):
        """Return whether or not this action should render.

        If the corresponding review request has a repository, then an upload
        diff form exists, so we should render this UploadDiffAction.

        Args:
            context (django.template.Context):
                The collection of key-value pairs available in the template
                just before this action is to be rendered.

        Returns:
            bool: Determines if this action should render.
        """
        return (context['review_request'].repository_id is not None
                and not is_site_read_only_for(context['request'].user))
Esempio n. 15
0
    def should_render(self, context):
        """Return whether the action should render.

        Args:
            context (dict):
                The current render context.

        Returns:
            bool:
            Whether the action should render.
        """
        request = context['request']
        user = request.user

        return (user.is_authenticated() and
                not is_site_read_only_for(user) and
                general_comments_feature.is_enabled(request=request))
Esempio n. 16
0
def read_only(request):
    """Return a dictionary with the read-only state.

    Args:
        request (django.http.HttpRequest):
            The current HTTP request.

    Returns:
        dict:
        State to add to the context.
    """
    siteconfig = SiteConfiguration.objects.get_current()

    return {
        'is_read_only': is_site_read_only_for(request.user),
        'read_only_message': siteconfig.get('read_only_message', ''),
    }
def read_only(request):
    """Return a dictionary with the read-only state.

    Args:
        request (django.http.HttpRequest):
            The current HTTP request.

    Returns:
        dict:
        State to add to the context.
    """
    siteconfig = SiteConfiguration.objects.get_current()

    return {
        'is_read_only': is_site_read_only_for(request.user),
        'read_only_message': siteconfig.get('read_only_message', ''),
    }
Esempio n. 18
0
    def should_render(self, context):
        """Return whether the action should render.

        Args:
            context (dict):
                The current render context.

        Returns:
            bool:
            Whether the action should render.
        """
        review_request = context['review_request']
        user = context['request'].user

        return (review_request.status == ReviewRequest.PENDING_REVIEW
                and not is_site_read_only_for(user)
                and (user.pk == review_request.submitter_id
                     or context['perms']['reviews']['can_edit_reviewrequest']))
Esempio n. 19
0
    def should_render(self, context):
        """Return whether the action should render.

        Args:
            context (dict):
                The current render context.

        Returns:
            bool:
            Whether the action should render.
        """
        review_request = context['review_request']
        user = context['request'].user

        return (review_request.status == ReviewRequest.PENDING_REVIEW and
                not is_site_read_only_for(user) and
                (user.pk == review_request.submitter_id or
                 context['perms']['reviews']['can_edit_reviewrequest']))
Esempio n. 20
0
    def can_change_issue_status(self, user):
        """Return whether the user can change the issue status.

        Currently, this is allowed for:
        - The user who owns the review request.
        - The user who opened the issue (posted the comment).

        Args:
            user (django.contrib.auth.models.User):
                The user being checked.

        Returns:
            bool:
            True if the given user is allowed to change the issue status.
        """
        if not (user and user.is_authenticated()):
            return False

        return ((self.get_review_request().is_mutable_by(user) or
                 user.pk == self.get_review().user_id) and
                not is_site_read_only_for(user))
Esempio n. 21
0
    def can_change_issue_status(self, user):
        """Return whether the user can change the issue status.

        Currently, this is allowed for:
        - The user who owns the review request.
        - The user who opened the issue (posted the comment).

        Args:
            user (django.contrib.auth.models.User):
                The user being checked.

        Returns:
            bool:
            True if the given user is allowed to change the issue status.
        """
        if not (user and user.is_authenticated()):
            return False

        return ((self.get_review_request().is_mutable_by(user) or
                 user == self.get_review().user) and
                not is_site_read_only_for(user))
Esempio n. 22
0
    def call_method_view(self, request, method, view, *args, **kwargs):
        """Call the given method view.

        The default behaviour is to call the given ``view`` passing in all
        ``args`` and ``kwargs``. However, Review Board allows certain resources
        to be disabled by setting the :py:attr:`~required_features` attribute.
        If a feature specified in that list is disabled, this method will
        return a 403 Forbidden response instead of calling the method view.

        In addition, Review Board has token access policies. If the client is
        authenticated with an API token, the token's access policies will be
        checked before calling the view. If the operation is disallowed, a 403
        Forbidden response will be returned.

        If read-only mode is enabled, all PUT, POST, and DELETE requests will
        be rejected with a 503 Service Unavailable response, unless the user
        is a superuser.

        Only if all these conditions are met will the view actually be called.

        Args:
            request (django.http.HttpRequest):
                The current HTTP request.

            method (unicode):
                The HTTP method.

            view (callable):
                The view.

            *args (tuple):
                Additional positional arguments.

            **kwargs (dict):
                Additional keyword arguments.

        Returns:
            WebAPIError or tuple:
            Either a 403 Forbidden error or the result of calling the method
            view, which will either be a
            :py:class:`~djblets.webapi.errors.WebAPIError` or a 2-tuple of the
            HTTP status code and a dict indicating the JSON response from the
            view.
        """
        for feature in self.required_features:
            if not feature.is_enabled(request=request):
                logger.warning(
                    'Disallowing %s for API resource %r because '
                    'feature %s is not enabled',
                    method,
                    self,
                    feature.feature_id,
                    request=request)
                return PERMISSION_DENIED

        if (is_site_read_only_for(request.user)
                and request.method not in ('GET', 'HEAD', 'OPTIONS')):
            return READ_ONLY_ERROR

        return super(WebAPIResource,
                     self).call_method_view(request, method, view, *args,
                                            **kwargs)
Esempio n. 23
0
def js_user_session_info(context):
    """Return JSON-serialized data for a new RB.UserSession instance.

    This is used on all Review Board pages to construct the attributes
    passed to :js:class:`RB.UserSession`. This contains information on the user
    and their authentication state, important API URLs that need to be
    accessed, timezone information, avatar URLs, and various user preferences.

    Args:
        context (django.template.Context):
            The current template context.

    Returns:
        django.utils.safestring.SafeText:
        The JSON-serialized attribute data.
    """
    request = context['request']
    user = request.user
    authenticated = user.is_authenticated()

    info = {
        'authenticated': authenticated,
    }

    if authenticated:
        # Authenticated users.
        siteconfig = SiteConfiguration.objects.get_current()
        profile = request.user.get_profile()
        username = user.username
        avatar_urls = {}

        info.update({
            'fullName': user.get_full_name() or username,
            'readOnly': is_site_read_only_for(user),
            'username': username,
        })

        # Inject some URLs needed to manage some user state.
        info['sessionURL'] = local_site_reverse('session-resource',
                                                request=request)

        for key, url_name in (('archivedReviewRequestsURL',
                               'archived-review-requests-resource'),
                              ('mutedReviewRequestsURL',
                               'muted-review-requests-resource'),
                              ('userFileAttachmentsURL',
                               'user-file-attachments-resource'),
                              ('userPageURL',
                               'user'), ('watchedReviewGroupsURL',
                                         'watched-review-groups-resource'),
                              ('watchedReviewRequestsURL',
                               'watched-review-requests-resource')):
            info[key] = local_site_reverse(url_name,
                                           request=request,
                                           kwargs={
                                               'username': username,
                                           })

        if profile is not None:
            cur_timezone = pytz.timezone(profile.timezone)
            use_rich_text = profile.should_use_rich_text
            info.update({
                'commentsOpenAnIssue':
                profile.open_an_issue,
                'enableDesktopNotifications':
                profile.should_enable_desktop_notifications,
            })
        else:
            cur_timezone = timezone.get_current_timezone()
            use_rich_text = siteconfig.get('default_use_rich_text')

        if siteconfig.get('avatars_enabled'):
            avatar_service = avatar_services.for_user(user)

            if avatar_service is None:
                logger.error(
                    'Could not get a suitable avatar service for '
                    'user %s in js_session_info().', user)
            else:
                # Fetch a 32x32 avatar URL (and any variants for different
                # screen DPIs). We only fetch 32x32 for historical reasons,
                # but may want to extend this in the future for additional
                # sizes.
                avatar_urls = {
                    size: avatar_service.get_avatar_urls(request=request,
                                                         user=user,
                                                         size=size)
                    for size in (32, )
                }

        info.update({
            'avatarURLs':
            avatar_urls,
            'defaultUseRichText':
            use_rich_text,
            'timezoneOffset':
            dateformat.format(datetime.now(tz=cur_timezone), 'O'),
        })
    else:
        # Anonymous users.
        info['loginURL'] = local_site_reverse('login', request=request)

    return json_dumps(info)
Esempio n. 24
0
def reply_section(context,
                  review,
                  comment,
                  context_type,
                  context_id,
                  reply_to_text=''):
    """Render a template for displaying a reply.

    This takes the same parameters as :tag:`reply_list`. The template
    rendered by this function, :template:`reviews/review_reply_section.html`,
    is responsible for invoking :tag:`reply_list` and as such passes these
    variables through. It does not make use of them itself.

    Args:
        context (django.template.Context):
            The collection of key-value pairs available in the template.

        review (reviewboard.reviews.models.Review):
            The review being replied to.

        comment (reviewboard.reviews.models.BaseComment):
            The comment being replied to.

        context_type (unicode):
            The type of comment being replied to. This is one of
            ``diff_comments``, ``screenshot_comments``,
            ``file_attachment_comments``, ``general_comments`` (if the reply is
            to a comment), or ``body_top`` or ``body_bottom`` if the reply is
            to the header or footer text of the review.

        context_id (unicode):
            The internal ID used by the JavaScript code for storing and
            categorizing comments.

        reply_to_text (unicode):
            The text in the review being replied to.

    Returns:
        dict:
        The context to use when rendering the template included by the
        inclusion tag.
    """
    user = context.get('user', None)

    if comment != '':
        if type(comment) is ScreenshotComment:
            context_id += 's'
        elif type(comment) is FileAttachmentComment:
            context_id += 'f'
        elif type(comment) is GeneralComment:
            context_id += 'g'

        context_id += six.text_type(comment.id)

    return {
        'review': review,
        'comment': comment,
        'context_type': context_type,
        'context_id': context_id,
        'user': context.get('user'),
        'local_site_name': context.get('local_site_name'),
        'is_read_only': is_site_read_only_for(user),
        'reply_to_is_empty': reply_to_text == '',
        'request': context['request'],
        'last_visited': context.get('last_visited'),
    }
Esempio n. 25
0
def reply_section(context, review, comment, context_type, context_id,
                  reply_to_text=''):
    """Render a template for displaying a reply.

    This takes the same parameters as :tag:`reply_list`. The template
    rendered by this function, :template:`reviews/review_reply_section.html`,
    is responsible for invoking :tag:`reply_list` and as such passes these
    variables through. It does not make use of them itself.

    Args:
        context (django.template.Context):
            The collection of key-value pairs available in the template.

        review (reviewboard.reviews.models.Review):
            The review being replied to.

        comment (reviewboard.reviews.models.BaseComment):
            The comment being replied to.

        context_type (unicode):
            The type of comment being replied to. This is one of
            ``diff_comments``, ``screenshot_comments``,
            ``file_attachment_comments``, ``general_comments`` (if the reply is
            to a comment), or ``body_top`` or ``body_bottom`` if the reply is
            to the header or footer text of the review.

        context_id (unicode):
            The internal ID used by the JavaScript code for storing and
            categorizing comments.

        reply_to_text (unicode):
            The text in the review being replied to.

    Returns:
        dict:
        The context to use when rendering the template included by the
        inclusion tag.
    """
    user = context.get('user', None)

    if comment != '':
        if type(comment) is ScreenshotComment:
            context_id += 's'
        elif type(comment) is FileAttachmentComment:
            context_id += 'f'
        elif type(comment) is GeneralComment:
            context_id += 'g'

        context_id += six.text_type(comment.id)

    return {
        'review': review,
        'comment': comment,
        'context_type': context_type,
        'context_id': context_id,
        'user': context.get('user'),
        'local_site_name': context.get('local_site_name'),
        'is_read_only': is_site_read_only_for(user),
        'reply_to_is_empty': reply_to_text == '',
        'request': context['request'],
        'last_visited': context.get('last_visited'),
    }
Esempio n. 26
0
 def _check_read_only(request, *args, **kwargs):
     if is_site_read_only_for(request.user):
         return HttpResponseRedirect(
             local_site_reverse('read-only', request=request))
     else:
         return view(request, *args, **kwargs)
Esempio n. 27
0
 def _check_read_only(request, *args, **kwargs):
     if is_site_read_only_for(request.user):
         return HttpResponseRedirect(
             local_site_reverse('read-only', request=request))
     else:
         return view(request, *args, **kwargs)