def add_extra_historical_fields(sender, **kwargs): history_instance = kwargs['history_instance'] if not history_instance: return history_user = kwargs['history_user'] if history_user: history_instance.extra_history_user_id = history_user.id history_instance.extra_history_user_username = history_user.username request = getattr(HistoricalRecords.context, 'request', None) if request: history_instance.extra_history_ip = get_client_ip(request) history_instance.extra_history_browser = request.headers.get('User-Agent')
def new(self, action, user=None, request=None, **kwargs): """ Create an audit log for `action`. If user or request are given, other fields will be auto-populated from that information. """ actions_requiring_user = ( AuditLog.PAGEVIEW, AuditLog.DOWNLOAD, AuditLog.AUTHN, AuditLog.LOGOUT, ) if action in actions_requiring_user and (not user or not request): raise TypeError( f'A user and a request is required for the {action} action.') if action in (AuditLog.PAGEVIEW, AuditLog.DOWNLOAD) and 'project' not in kwargs: raise TypeError(f'A project is required for the {action} action.') # Don't save anonymous users. if user and user.is_anonymous: user = None if request: kwargs['ip'] = get_client_ip(request) kwargs['browser'] = request.headers.get('User-Agent') kwargs.setdefault('resource', request.path_info) kwargs.setdefault('auth_backend', get_auth_backend(request)) # Fill the project from the request if available. # This is frequently on actions generated from a subdomain. project_slug = getattr(request, 'host_project_slug', None) if 'project' not in kwargs and project_slug: kwargs['project'] = Project.objects.filter( slug=project_slug).first() return self.create( user=user, action=action, **kwargs, )
def get(self, request, project_slug, type_, version_slug): """ Download a specific piece of media. Perform an auth check if serving in private mode. .. warning:: This is linked directly from the HTML pages. It should only care about the Version permissions, not the actual Project permissions. """ version = get_object_or_404( Version.objects.public(user=request.user), project__slug=project_slug, slug=version_slug, ) # Send media download to analytics - sensitive data is anonymized analytics_event.delay( event_category='Build Media', event_action=f'Download {type_}', event_label=str(version), ua=request.META.get('HTTP_USER_AGENT'), uip=get_client_ip(request), ) storage = get_storage_class(settings.RTD_BUILD_MEDIA_STORAGE)() storage_path = version.project.get_storage_path( type_=type_, version_slug=version_slug, version_type=version.type, ) # URL without scheme and domain to perform an NGINX internal redirect url = storage.url(storage_path) url = urlparse(url)._replace(scheme='', netloc='').geturl() return self._serve_docs( request, final_project=version.project, version_slug=version.slug, path=url, download=True, )
def project_download_media(request, project_slug, type_, version_slug): """ Download a specific piece of media. Perform an auth check if serving in private mode. .. warning:: This is linked directly from the HTML pages. It should only care about the Version permissions, not the actual Project permissions. """ version = get_object_or_404( Version.objects.public(user=request.user), project__slug=project_slug, slug=version_slug, ) # Send media download to analytics - sensitive data is anonymized analytics_event.delay( event_category='Build Media', event_action=f'Download {type_}', event_label=str(version), ua=request.META.get('HTTP_USER_AGENT'), uip=get_client_ip(request), ) if settings.DEFAULT_PRIVACY_LEVEL == 'public' or settings.DEBUG: if settings.RTD_BUILD_MEDIA_STORAGE: storage = get_storage_class(settings.RTD_BUILD_MEDIA_STORAGE)() storage_path = version.project.get_storage_path( type_=type_, version_slug=version_slug, version_type=version.type, ) if storage.exists(storage_path): return HttpResponseRedirect(storage.url(storage_path)) media_path = os.path.join( settings.MEDIA_URL, type_, project_slug, version_slug, '%s.%s' % (project_slug, type_.replace('htmlzip', 'zip')), ) return HttpResponseRedirect(media_path) # Get relative media path path = (version.project.get_production_media_path( type_=type_, version_slug=version_slug, ).replace(settings.PRODUCTION_ROOT, '/prod_artifacts')) content_type, encoding = mimetypes.guess_type(path) content_type = content_type or 'application/octet-stream' response = HttpResponse(content_type=content_type) if encoding: response['Content-Encoding'] = encoding response['X-Accel-Redirect'] = path # Include version in filename; this fixes a long-standing bug filename = '{}-{}.{}'.format( project_slug, version_slug, path.split('.')[-1], ) response['Content-Disposition'] = 'filename=%s' % filename return response
def get( self, request, project_slug=None, type_=None, version_slug=None, lang_slug=None, subproject_slug=None, ): """ Download a specific piece of media. Perform an auth check if serving in private mode. This view is used to download a file using old-style URLs (download from the dashboard) and new-style URLs (download from the same domain as docs). Basically, the parameters received by the GET view are different (``project_slug`` does not come in the new-style URLs, for example) and we need to take it from the request. Once we get the final ``version`` to be served, everything is the same for both paths. .. warning:: This is linked directly from the HTML pages. It should only care about the Version permissions, not the actual Project permissions. """ if self.same_domain_url: # It uses the request to get the ``project``. The rest of arguments come # from the URL. final_project, lang_slug, version_slug, filename = _get_project_data_from_request( # noqa request, project_slug=None, subproject_slug=subproject_slug, lang_slug=lang_slug, version_slug=version_slug, ) if not self.allowed_user(request, final_project, version_slug): return self.get_unauthed_response(request, final_project) # We don't use ``.public`` in this filter because the access # permission was already granted by ``.allowed_user`` version = get_object_or_404( final_project.versions, slug=version_slug, ) else: # All the arguments come from the URL. version = get_object_or_404( Version.objects.public(user=request.user), project__slug=project_slug, slug=version_slug, ) # Send media download to analytics - sensitive data is anonymized analytics_event.delay( event_category='Build Media', event_action=f'Download {type_}', event_label=str(version), ua=request.META.get('HTTP_USER_AGENT'), uip=get_client_ip(request), ) storage = get_storage_class(settings.RTD_BUILD_MEDIA_STORAGE)() storage_path = version.project.get_storage_path( type_=type_, version_slug=version_slug, version_type=version.type, ) # URL without scheme and domain to perform an NGINX internal redirect url = storage.url(storage_path) url = urlparse(url)._replace(scheme='', netloc='').geturl() return self._serve_docs( request, final_project=version.project, version_slug=version.slug, path=url, download=True, )
def get( self, request, project_slug=None, type_=None, version_slug=None, lang_slug=None, subproject_slug=None, ): """ Download a specific piece of media. Perform an auth check if serving in private mode. This view is used to download a file using old-style URLs (download from the dashboard) and new-style URLs (download from the same domain as docs). Basically, the parameters received by the GET view are different (``project_slug`` does not come in the new-style URLs, for example) and we need to take it from the request. Once we get the final ``version`` to be served, everything is the same for both paths. .. warning:: This is linked directly from the HTML pages. It should only care about the Version permissions, not the actual Project permissions. """ if self.same_domain_url: version = self._version_same_domain_url( request, type_, lang_slug, version_slug, subproject_slug, ) else: version = self._version_dashboard_url( request, project_slug, type_, version_slug, ) # Send media download to analytics - sensitive data is anonymized analytics_event.delay( event_category='Build Media', event_action=f'Download {type_}', event_label=str(version), ua=request.META.get('HTTP_USER_AGENT'), uip=get_client_ip(request), ) storage = get_storage_class(settings.RTD_BUILD_MEDIA_STORAGE)() storage_path = version.project.get_storage_path( type_=type_, version_slug=version_slug, version_type=version.type, ) # URL without scheme and domain to perform an NGINX internal redirect url = storage.url(storage_path) url = urlparse(url)._replace(scheme='', netloc='').geturl() return self._serve_docs( request, final_project=version.project, version_slug=version.slug, path=url, download=True, )