1
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
1
# Copyright 2009 Canonical Ltd. This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
4
# pylint: disable-msg=W0231,E1002
34
34
from zope.app.server import wsgi
35
35
from zope.app.wsgi import WSGIPublisherApplication
36
36
from zope.component import getUtility
37
from zope.event import notify
38
37
from zope.interface import (
61
60
from zope.server.http.wsgihttpserver import PMDBWSGIHTTPServer
62
61
from zope.session.interfaces import ISession
64
from lp.app.errors import UnexpectedFormData
66
from lp.services.config import config
67
from lp.services.features import get_relevant_feature_controller
68
from lp.services.features.flags import NullFeatureController
69
from lp.services.feeds.interfaces.application import IFeedsApplication
70
from lp.services.feeds.interfaces.feed import IFeed
71
from lp.services.oauth.interfaces import (
63
from canonical.config import config
64
from canonical.launchpad.interfaces.launchpad import (
67
IWebServiceApplication,
69
from canonical.launchpad.interfaces.oauth import (
73
71
IOAuthSignedRequest,
76
from lp.services.propertycache import cachedproperty
77
from lp.services.webapp.authentication import (
74
import canonical.launchpad.layers
75
from canonical.launchpad.webapp.authentication import (
78
76
check_oauth_signature,
79
77
get_oauth_authorization,
81
from lp.services.webapp.authorization import (
79
from canonical.launchpad.webapp.authorization import (
82
80
LAUNCHPAD_SECURITY_POLICY_CACHE_KEY,
84
from lp.services.webapp.errorlog import ErrorReportRequest
85
from lp.services.webapp.interfaces import (
86
FinishReadOnlyRequestEvent,
82
from canonical.launchpad.webapp.errorlog import ErrorReportRequest
83
from canonical.launchpad.webapp.interfaces import (
88
85
IBasicLaunchpadRequest,
95
92
IPlacelessLoginSource,
98
from lp.services.webapp.notifications import (
95
from canonical.launchpad.webapp.notifications import (
100
97
NotificationRequest,
101
98
NotificationResponse,
103
from lp.services.webapp.opstats import OpStats
104
from lp.services.webapp.publication import LaunchpadBrowserPublication
105
from lp.services.webapp.publisher import (
100
from canonical.launchpad.webapp.opstats import OpStats
101
from canonical.launchpad.webapp.publication import LaunchpadBrowserPublication
102
from canonical.launchpad.webapp.publisher import (
106
103
get_current_browser_request,
109
from lp.services.webapp.vhosts import allvhosts
110
from lp.services.webservice.interfaces import IWebServiceApplication
106
from canonical.launchpad.webapp.vhosts import allvhosts
107
from canonical.lazr.interfaces.feed import IFeed
108
from lp.app.errors import UnexpectedFormData
109
from lp.services.features import get_relevant_feature_controller
110
from lp.services.features.flags import NullFeatureController
111
from lp.services.propertycache import cachedproperty
111
112
from lp.testopenid.interfaces.server import ITestOpenIDApplication
112
from lp.xmlrpc.interfaces import IPrivateApplication
542
542
"""See `IBasicLaunchpadRequest`."""
543
543
return 'XMLHttpRequest' == self.getHeader('HTTP_X_REQUESTED_WITH')
545
def getURL(self, level=0, path_only=False, include_query=False):
546
"""See `IBasicLaunchpadRequest`."""
547
sup = super(LaunchpadBrowserRequestMixin, self)
548
url = sup.getURL(level, path_only)
550
query_string = self.get('QUERY_STRING')
551
if query_string is not None and len(query_string) > 0:
552
url = "%s?%s" % (url, query_string)
556
546
class BasicLaunchpadRequest(LaunchpadBrowserRequestMixin):
557
547
"""Mixin request class to provide stepstogo."""
563
553
self._wsgi_keys = set()
564
554
self.needs_datepicker_iframe = False
565
555
self.needs_datetimepicker_iframe = False
556
self.needs_json = False
566
557
super(BasicLaunchpadRequest, self).__init__(
567
558
body_instream, environ, response)
617
608
ILaunchpadBrowserApplicationRequest, ISynchronizer,
618
lp.layers.LaunchpadLayer)
609
canonical.launchpad.layers.LaunchpadLayer)
620
611
retry_max_count = 5 # How many times we're willing to retry
702
693
def web_service_request_to_browser_request(webservice_request):
703
694
"""Convert a given webservice request into a webapp one.
705
Overrides 'SERVER_URL' to the 'mainsite', preserving headers and
706
body. Encodes PATH_INFO because it is unconditionally decoded by
707
zope.publisher.http.sane_environment.
696
Simply overrides 'SERVER_URL' to the 'mainsite', preserving headers and
709
699
body = webservice_request.bodyStream.getCacheStream().read()
710
700
environ = dict(webservice_request.environment)
711
701
environ['SERVER_URL'] = allvhosts.configs['mainsite'].rooturl
712
if 'PATH_INFO' in environ:
713
environ['PATH_INFO'] = environ['PATH_INFO'].encode('utf-8')
714
702
return LaunchpadBrowserRequest(body, environ)
810
798
return request.response
813
class LaunchpadTestRequest(LaunchpadBrowserRequestMixin,
814
TestRequest, ErrorReportRequest):
801
class LaunchpadTestRequest(TestRequest, ErrorReportRequest,
802
LaunchpadBrowserRequestMixin):
815
803
"""Mock request for use in unit and functional tests.
817
805
>>> request = LaunchpadTestRequest(SERVER_URL='http://127.0.0.1/foo/bar')
825
813
It provides LaunchpadLayer and adds a mock INotificationRequest
828
>>> lp.layers.LaunchpadLayer.providedBy(request)
816
>>> canonical.launchpad.layers.LaunchpadLayer.providedBy(request)
830
818
>>> INotificationRequest.providedBy(request)
857
845
>>> request.needs_datepicker_iframe
850
>>> request.needs_json
862
INotificationRequest, IBasicLaunchpadRequest, IParticipation,
863
lp.layers.LaunchpadLayer)
854
implements(INotificationRequest, IBasicLaunchpadRequest, IParticipation,
855
canonical.launchpad.layers.LaunchpadLayer)
865
856
# These two attributes satisfy IParticipation.
867
858
interaction = None
874
865
self.traversed_objects = []
875
866
self.needs_datepicker_iframe = False
876
867
self.needs_datetimepicker_iframe = False
868
self.needs_json = False
877
869
# Use an existing feature controller if one exists, otherwise use the
878
870
# null controller.
879
871
self.features = get_relevant_feature_controller()
959
951
"""See zope.app.publication.interfaces.IPublicationRequestFactory"""
960
952
assert output_stream is None, 'output_stream is deprecated in Z3.2'
962
# Mark the request with the 'lp.layers.debug' layer
954
# Mark the request with the 'canonical.launchpad.layers.debug' layer
963
955
request = HTTPPublicationRequestFactory.__call__(
964
956
self, input_stream, env)
965
lp.layers.setFirstLayer(
966
request, lp.layers.DebugLayer)
957
canonical.launchpad.layers.setFirstLayer(
958
request, canonical.launchpad.layers.DebugLayer)
1123
1115
class FeedsBrowserRequest(LaunchpadBrowserRequest):
1124
1116
"""Request type for a launchpad feed."""
1125
implements(lp.layers.FeedsLayer)
1117
implements(canonical.launchpad.layers.FeedsLayer)
1130
1122
class APIDocBrowserRequest(LaunchpadBrowserRequest):
1131
implements(lp.layers.APIDocLayer)
1123
implements(canonical.launchpad.layers.APIDocLayer)
1134
1126
class APIDocBrowserPublication(LaunchpadBrowserPublication):
1138
1130
# ---- testopenid
1140
1132
class TestOpenIDBrowserRequest(LaunchpadBrowserRequest):
1141
implements(lp.layers.TestOpenIDLayer)
1133
implements(canonical.launchpad.layers.TestOpenIDLayer)
1144
1136
class TestOpenIDBrowserPublication(LaunchpadBrowserPublication):
1175
1167
pageid += ':' + collection_identifier
1176
1168
op = (view.request.get('ws.op')
1177
1169
or view.request.query_string_params.get('ws.op'))
1178
if op and isinstance(op, basestring):
1179
1171
pageid += ':' + op
1202
1194
return super(WebServicePublication, self).getResource(request, ob)
1204
def finishReadOnlyRequest(self, request, ob, txn):
1196
def finishReadOnlyRequest(self, txn):
1205
1197
"""Commit the transaction so that created OAuthNonces are stored."""
1206
notify(FinishReadOnlyRequestEvent(ob, request))
1207
1198
# Transaction commits usually need to be aware of the possibility of
1208
1199
# a doomed transaction. We do not expect that this code will
1209
1200
# encounter doomed transactions. If it does, this will need to be
1309
1300
class LaunchpadWebServiceRequestTraversal(WebServiceRequestTraversal):
1310
implements(lp.layers.WebServiceLayer)
1301
implements(canonical.launchpad.layers.WebServiceLayer)
1312
1303
def getRootURL(self, rootsite):
1313
1304
"""See IBasicLaunchpadRequest."""