~launchpad-pqm/launchpad/devel

11235.8.3 by Abel Deuring
change the bug attachment download URL for +text bug pages; ensure that spaces in file names are properly escaped in theURLs returned by ProxiedLibraryFileAlias.httl_ur; utf-8-encode these filenames
1
# Copyright 2010 Canonical Ltd.  This software is licensed under the
8687.15.18 by Karl Fogel
Add the copyright header block to files under lib/canonical/.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4195.1.1 by Brad Crittenden
Implement upload and management of files associated with a product release.
3
4
"""Browser file for LibraryFileAlias."""
5
6
__metaclass__ = type
7
8
__all__ = [
8450.1.2 by Celso Providelo
Stop ProxiedLibraryFileAlias traversals on deleted LFAs. Raising an appropriate error on failures, so broken callsites can be traced in the OOPS reports.
9
    'DeletedProxiedLibraryFileAlias',
8137.8.1 by Celso Providelo
Fixing #354373 (exposing IBuild.{build_log_url, upload_log_url} in the API relative to the webapp url, not the webservice one).
10
    'FileNavigationMixin',
11
    'LibraryFileAliasMD5View',
4195.1.1 by Brad Crittenden
Implement upload and management of files associated with a product release.
12
    'LibraryFileAliasView',
8137.8.1 by Celso Providelo
Fixing #354373 (exposing IBuild.{build_log_url, upload_log_url} in the API relative to the webapp url, not the webservice one).
13
    'ProxiedLibraryFileAlias',
4195.1.1 by Brad Crittenden
Implement upload and management of files associated with a product release.
14
    ]
15
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
16
from lazr.delegates import delegates
13324.1.2 by Steve Kowalik
Use GoneError, rather than setting the status.
17
from lazr.restful.interfaces import IWebBrowserOriginatingRequest
13931.2.1 by Steve Kowalik
Chip away at canonical.lazr a little more.
18
from lazr.restful.utils import get_current_browser_request
7077.2.12 by Celso Providelo
testing StreamOrRedirectLibraryFileAliasView.
19
from zope.publisher.interfaces import NotFound
7077.2.16 by Celso Providelo
applying review comments, r=bac.
20
from zope.security.interfaces import Unauthorized
7077.2.6 by Celso Providelo
Spliting IArchive & IBuild getFileByName(), both used to traverse on +files/<filename> to StreamOrRedirectLibraryFileAliasView.
21
14606.2.2 by William Grant
Move canonical.librarian.{client,utils} to lp.services.librarian.
22
from lp.app.errors import GoneError
14600.1.8 by Curtis Hovey
Move c.l.layers to lp.
23
from lp.layers import WebServiceLayer
14606.2.2 by William Grant
Move canonical.librarian.{client,utils} to lp.services.librarian.
24
from lp.services.librarian.client import url_path_quote
25
from lp.services.librarian.interfaces import ILibraryFileAlias
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
26
from lp.services.webapp.authorization import check_permission
27
from lp.services.webapp.publisher import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
28
    canonical_url,
29
    LaunchpadView,
12670.2.17 by William Grant
Fix import order.
30
    RedirectionView,
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
31
    stepthrough,
32
    )
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
33
from lp.services.webapp.url import urlappend
8137.8.1 by Celso Providelo
Fixing #354373 (exposing IBuild.{build_log_url, upload_log_url} in the API relative to the webapp url, not the webservice one).
34
4195.1.1 by Brad Crittenden
Implement upload and management of files associated with a product release.
35
36
class LibraryFileAliasView(LaunchpadView):
4195.1.12 by Brad Crittenden
Post-review changes
37
    """View to handle redirection for downloading files by URL.
4195.1.1 by Brad Crittenden
Implement upload and management of files associated with a product release.
38
39
    Rather than reference downloadable files via the obscure Librarian
7675.764.1 by Abel Deuring
class SafeStreamOrRedirectLibraryFileAliasView added which always sets the HTTP header Content-Disposition to 'attachment' for restricted LFAs; used this class to serve LFAs of bug attachments
40
    URL, downloadable files can be referenced via the Product Release URL,
41
    e.g. http://launchpad.net/firefox/1.0./1.0.0/+download/firefox-1.0.0.tgz.
7077.2.6 by Celso Providelo
Spliting IArchive & IBuild getFileByName(), both used to traverse on +files/<filename> to StreamOrRedirectLibraryFileAliasView.
42
    """
43
12670.2.9 by William Grant
Merge RedirectPerhapsWithTokenLibraryFileAliasView into LibraryFileAliasView. It's no longer an IBrowerPublisher, instead redirecting in initialize() like LFAV did.
44
    def initialize(self):
45
        """Redirect the request to the URL of the file in the Librarian."""
12670.2.12 by William Grant
Use LFA.getURL in LibraryFileAliasView, and forbid it from being used on restricted files. Alter FileNavigation to bypass LFAV, since it needs to work for restricted files. We're not sure that it isn't possible to get an LFAV on a file that you shouldn't be able to see.
46
        # Refuse to serve restricted files. We're not sure that no
47
        # restricted files are being leaked in the traversal hierarchy.
48
        assert not self.context.restricted
13324.1.1 by Steve Kowalik
Return a 410 for deleted LFAs in LibraryFileAliasView.
49
        # If the LFA is deleted, throw a 410.
50
        if self.context.deleted:
13324.1.2 by Steve Kowalik
Use GoneError, rather than setting the status.
51
            raise GoneError("File deleted.")
12670.2.12 by William Grant
Use LFA.getURL in LibraryFileAliasView, and forbid it from being used on restricted files. Alter FileNavigation to bypass LFAV, since it needs to work for restricted files. We're not sure that it isn't possible to get an LFAV on a file that you shouldn't be able to see.
52
        # Redirect based on the scheme of the request, as set by
53
        # Apache in the 'X-SCHEME' environment variable, which is
54
        # mapped to 'HTTP_X_SCHEME.  Note that only some requests
55
        # for librarian files are allowed to come in via http as
56
        # most are forced to https via Apache redirection.
57
        self.request.response.redirect(
58
            self.context.getURL(
59
                secure=self.request.get('HTTP_X_SCHEME') != 'http'))
12670.2.9 by William Grant
Merge RedirectPerhapsWithTokenLibraryFileAliasView into LibraryFileAliasView. It's no longer an IBrowerPublisher, instead redirecting in initialize() like LFAV did.
60
61
62
class LibraryFileAliasMD5View(LaunchpadView):
63
    """View to show the MD5 digest for a librarian file."""
64
65
    def render(self):
66
        """Return the plain text MD5 signature"""
67
        self.request.response.setHeader('Content-type', 'text/plain')
68
        return '%s %s' % (self.context.content.md5, self.context.filename)
7077.2.16 by Celso Providelo
applying review comments, r=bac.
69
7675.809.24 by Robert Collins
Make the public restricted librarian facility be controlled by a feature flag in the appservers.
70
8450.1.2 by Celso Providelo
Stop ProxiedLibraryFileAlias traversals on deleted LFAs. Raising an appropriate error on failures, so broken callsites can be traced in the OOPS reports.
71
class DeletedProxiedLibraryFileAlias(NotFound):
72
    """Raised when a deleted `ProxiedLibraryFileAlias` is accessed."""
73
74
7077.2.16 by Celso Providelo
applying review comments, r=bac.
75
class FileNavigationMixin:
76
    """Navigate to `LibraryFileAlias` hosted in a context.
77
78
    The navigation goes through +files/<filename> where file reference is
79
    provided by context `getFileByName(filename)`.
80
12670.2.9 by William Grant
Merge RedirectPerhapsWithTokenLibraryFileAliasView into LibraryFileAliasView. It's no longer an IBrowerPublisher, instead redirecting in initialize() like LFAV did.
81
    The requested file is proxied via `LibraryFileAliasView`,
12670.2.8 by William Grant
Clean up docstrings.
82
    making it possible to serve both public and restricted files.
7077.2.18 by Celso Providelo
Allowing our test infrastructure to support responses containing streamed files (missing content-length), r=stub.
83
7077.2.19 by Celso Providelo
typo-matic ...
84
    This navigation approach only supports domains with unique filenames,
85
    which is the case of IArchive and IBuild. It will probably have to be
86
    extended in order to allow traversing to multiple files potentially
87
    with the same filename (product files or bug attachments).
7077.2.16 by Celso Providelo
applying review comments, r=bac.
88
    """
7675.764.1 by Abel Deuring
class SafeStreamOrRedirectLibraryFileAliasView added which always sets the HTTP header Content-Disposition to 'attachment' for restricted LFAs; used this class to serve LFAs of bug attachments
89
7077.2.16 by Celso Providelo
applying review comments, r=bac.
90
    @stepthrough('+files')
91
    def traverse_files(self, filename):
92
        """Traverse on filename in the archive domain."""
93
        if not check_permission('launchpad.View', self.context):
94
            raise Unauthorized()
7675.764.1 by Abel Deuring
class SafeStreamOrRedirectLibraryFileAliasView added which always sets the HTTP header Content-Disposition to 'attachment' for restricted LFAs; used this class to serve LFAs of bug attachments
95
        library_file = self.context.getFileByName(filename)
8450.1.2 by Celso Providelo
Stop ProxiedLibraryFileAlias traversals on deleted LFAs. Raising an appropriate error on failures, so broken callsites can be traced in the OOPS reports.
96
97
        # Deleted library files result in NotFound-like error.
7675.415.4 by Abel Deuring
adjust storm classes to no longer define and use the dropped columns LibraryFileContent.deleted, LibraryFileContent.datemirrored (exception: librarian_gc.py is not yet fixed); add a property 'deleted' to LibraryFileAlias; replace usage of LibraryFileAlias.content.deleted by LibraryFileAlias.deleted; fix failing tests, except test_gc.py
98
        if library_file.deleted:
8450.1.2 by Celso Providelo
Stop ProxiedLibraryFileAlias traversals on deleted LFAs. Raising an appropriate error on failures, so broken callsites can be traced in the OOPS reports.
99
            raise DeletedProxiedLibraryFileAlias(filename, self.context)
100
12924.2.1 by William Grant
FileNavigationMixin now requires that the filename be the final path segment.
101
        # There can be no further path segments.
102
        if len(self.request.stepstogo) > 0:
103
            return None
104
12670.2.12 by William Grant
Use LFA.getURL in LibraryFileAliasView, and forbid it from being used on restricted files. Alter FileNavigation to bypass LFAV, since it needs to work for restricted files. We're not sure that it isn't possible to get an LFAV on a file that you shouldn't be able to see.
105
        return RedirectionView(
106
            library_file.getURL(include_token=True),
107
            self.request)
8137.8.1 by Celso Providelo
Fixing #354373 (exposing IBuild.{build_log_url, upload_log_url} in the API relative to the webapp url, not the webservice one).
108
109
110
class ProxiedLibraryFileAlias:
7675.809.22 by Robert Collins
More commentary.
111
    """A `LibraryFileAlias` decorator for use in URL generation.
112
12113.1.1 by Abel Deuring
Back out hack to serve restricted files directly from the internal restricted librarian port; LibraryBackedByteStorage.alias_url returns a URL with an access token for restricted files.
113
    The URL's output by this decorator will always point at the webapp. This
114
    is useful when:
115
     - the webapp has to be contacted to get access to a file (required for
116
       restricted files).
7675.809.22 by Robert Collins
More commentary.
117
     - files might change from public to private and thus not work even if the
118
       user has access to the once its private, unless they go via the webapp.
119
120
    This should be used anywhere we are outputting URL's to LibraryFileAliases
121
    other than directly in rendered pages. For rendered pages, using a
122
    LibraryFileAlias directly is OK as at that point the status of the file
12113.1.1 by Abel Deuring
Back out hack to serve restricted files directly from the internal restricted librarian port; LibraryBackedByteStorage.alias_url returns a URL with an access token for restricted files.
123
    is known.
8137.8.1 by Celso Providelo
Fixing #354373 (exposing IBuild.{build_log_url, upload_log_url} in the API relative to the webapp url, not the webservice one).
124
125
    Overrides `ILibraryFileAlias.http_url` to always point to the webapp URL,
126
    even when called from the webservice domain.
127
    """
128
    delegates(ILibraryFileAlias)
129
130
    def __init__(self, context, parent):
131
        self.context = context
132
        self.parent = parent
133
11495.1.1 by Robert Collins
Rollback rev 11491.
134
    @property
135
    def http_url(self):
136
        """Return the webapp URL for the context `LibraryFileAlias`.
8137.8.1 by Celso Providelo
Fixing #354373 (exposing IBuild.{build_log_url, upload_log_url} in the API relative to the webapp url, not the webservice one).
137
8450.1.3 by Celso Providelo
ProxiedLibraryFileAlias.http_url returns None for deleted files, it should prevent files to be linkified in the UI.
138
        Preserve the `LibraryFileAlias.http_url` behavior for deleted
139
        files, returning None.
11495.1.1 by Robert Collins
Rollback rev 11491.
140
141
        Mask webservice requests if it's the case, so the returned URL will
142
        be always relative to the parent webapp URL.
8137.8.1 by Celso Providelo
Fixing #354373 (exposing IBuild.{build_log_url, upload_log_url} in the API relative to the webapp url, not the webservice one).
143
        """
7675.415.4 by Abel Deuring
adjust storm classes to no longer define and use the dropped columns LibraryFileContent.deleted, LibraryFileContent.datemirrored (exception: librarian_gc.py is not yet fixed); add a property 'deleted' to LibraryFileAlias; replace usage of LibraryFileAlias.content.deleted by LibraryFileAlias.deleted; fix failing tests, except test_gc.py
144
        if self.context.deleted:
8450.1.3 by Celso Providelo
ProxiedLibraryFileAlias.http_url returns None for deleted files, it should prevent files to be linkified in the UI.
145
            return None
146
11495.1.1 by Robert Collins
Rollback rev 11491.
147
        request = get_current_browser_request()
148
        if WebServiceLayer.providedBy(request):
149
            request = IWebBrowserOriginatingRequest(request)
150
8137.8.1 by Celso Providelo
Fixing #354373 (exposing IBuild.{build_log_url, upload_log_url} in the API relative to the webapp url, not the webservice one).
151
        parent_url = canonical_url(self.parent, request=request)
152
        traversal_url = urlappend(parent_url, '+files')
11235.8.3 by Abel Deuring
change the bug attachment download URL for +text bug pages; ensure that spaces in file names are properly escaped in theURLs returned by ProxiedLibraryFileAlias.httl_ur; utf-8-encode these filenames
153
        url = urlappend(
11204.7.10 by Jeroen Vermeulen
So that caused it--sneaky merge conflict.
154
            traversal_url,
155
            url_path_quote(self.context.filename.encode('utf-8')))
8137.8.1 by Celso Providelo
Fixing #354373 (exposing IBuild.{build_log_url, upload_log_url} in the API relative to the webapp url, not the webservice one).
156
        return url