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)
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))
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))
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))
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))
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))
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)
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))
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))
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 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']))
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))
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))
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)
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)
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'), }
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)