author | Sylvain Thénault <sylvain.thenault@logilab.fr> |
Fri, 23 Apr 2010 11:10:30 +0200 | |
changeset 5382 | cb5dfea92285 |
parent 5223 | 6abd6e3599f4 |
child 5423 | e15abfdcce38 |
permissions | -rw-r--r-- |
0 | 1 |
"""WSGI request handler for cubicweb |
2 |
||
3 |
:organization: Logilab |
|
4212
ab6573088b4a
update copyright: welcome 2010
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2058
diff
changeset
|
4 |
:copyright: 2008-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
0 | 5 |
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
1977
606923dff11b
big bunch of copyright / docstring update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1802
diff
changeset
|
6 |
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
0 | 7 |
""" |
8 |
||
9 |
__docformat__ = "restructuredtext en" |
|
10 |
||
4719
aaed3f813ef8
kill dead/useless code as suggested by pylint
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
11 |
from cubicweb import AuthenticationError |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4721
diff
changeset
|
12 |
from cubicweb.web import Redirect, DirectResponse, StatusResponse, LogOut |
0 | 13 |
from cubicweb.web.application import CubicWebPublisher |
14 |
from cubicweb.wsgi.request import CubicWebWsgiRequest |
|
15 |
||
16 |
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html |
|
17 |
STATUS_CODE_TEXT = { |
|
18 |
100: 'CONTINUE', |
|
19 |
101: 'SWITCHING PROTOCOLS', |
|
20 |
200: 'OK', |
|
21 |
201: 'CREATED', |
|
22 |
202: 'ACCEPTED', |
|
23 |
203: 'NON-AUTHORITATIVE INFORMATION', |
|
24 |
204: 'NO CONTENT', |
|
25 |
205: 'RESET CONTENT', |
|
26 |
206: 'PARTIAL CONTENT', |
|
27 |
300: 'MULTIPLE CHOICES', |
|
28 |
301: 'MOVED PERMANENTLY', |
|
29 |
302: 'FOUND', |
|
30 |
303: 'SEE OTHER', |
|
31 |
304: 'NOT MODIFIED', |
|
32 |
305: 'USE PROXY', |
|
33 |
306: 'RESERVED', |
|
34 |
307: 'TEMPORARY REDIRECT', |
|
35 |
400: 'BAD REQUEST', |
|
36 |
401: 'UNAUTHORIZED', |
|
37 |
402: 'PAYMENT REQUIRED', |
|
38 |
403: 'FORBIDDEN', |
|
39 |
404: 'NOT FOUND', |
|
40 |
405: 'METHOD NOT ALLOWED', |
|
41 |
406: 'NOT ACCEPTABLE', |
|
42 |
407: 'PROXY AUTHENTICATION REQUIRED', |
|
43 |
408: 'REQUEST TIMEOUT', |
|
44 |
409: 'CONFLICT', |
|
45 |
410: 'GONE', |
|
46 |
411: 'LENGTH REQUIRED', |
|
47 |
412: 'PRECONDITION FAILED', |
|
48 |
413: 'REQUEST ENTITY TOO LARGE', |
|
49 |
414: 'REQUEST-URI TOO LONG', |
|
50 |
415: 'UNSUPPORTED MEDIA TYPE', |
|
51 |
416: 'REQUESTED RANGE NOT SATISFIABLE', |
|
52 |
417: 'EXPECTATION FAILED', |
|
53 |
500: 'INTERNAL SERVER ERROR', |
|
54 |
501: 'NOT IMPLEMENTED', |
|
55 |
502: 'BAD GATEWAY', |
|
56 |
503: 'SERVICE UNAVAILABLE', |
|
57 |
504: 'GATEWAY TIMEOUT', |
|
58 |
505: 'HTTP VERSION NOT SUPPORTED', |
|
59 |
} |
|
60 |
||
61 |
||
62 |
class WSGIResponse(object): |
|
63 |
"""encapsulates the wsgi response parameters |
|
64 |
(code, headers and body if there is one) |
|
65 |
""" |
|
66 |
def __init__(self, code, req, body=None): |
|
67 |
text = STATUS_CODE_TEXT.get(code, 'UNKNOWN STATUS CODE') |
|
68 |
self.status = '%s %s' % (code, text) |
|
69 |
self.headers = [(str(k), str(v)) for k, v in req.headers_out.items()] |
|
70 |
if body: |
|
71 |
self.body = [body] |
|
72 |
else: |
|
73 |
self.body = [] |
|
74 |
||
75 |
def __iter__(self): |
|
76 |
return iter(self.body) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
0
diff
changeset
|
77 |
|
0 | 78 |
|
79 |
||
80 |
class CubicWebWSGIApplication(object): |
|
81 |
"""This is the wsgi application which will be called by the |
|
82 |
wsgi server with the WSGI ``environ`` and ``start_response`` |
|
83 |
parameters. |
|
84 |
||
85 |
XXX: missing looping tasks and proper repository shutdown when |
|
86 |
the application is stopped. |
|
87 |
NOTE: no pyro |
|
88 |
""" |
|
89 |
||
90 |
def __init__(self, config, debug=None, vreg=None): |
|
91 |
self.appli = CubicWebPublisher(config, debug=debug, vreg=vreg) |
|
92 |
self.debugmode = debug |
|
93 |
self.config = config |
|
94 |
self.base_url = None |
|
95 |
# self.base_url = config['base-url'] or config.default_base_url() |
|
96 |
# assert self.base_url[-1] == '/' |
|
97 |
# self.https_url = config['https-url'] |
|
98 |
# assert not self.https_url or self.https_url[-1] == '/' |
|
2770
356e9d7c356d
R propagate registry API changes
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
2058
diff
changeset
|
99 |
self.url_rewriter = self.appli.vreg['components'].select_or_none('urlrewriter') |
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
0
diff
changeset
|
100 |
|
0 | 101 |
def _render(self, req): |
102 |
"""this function performs the actual rendering |
|
103 |
XXX missing: https handling, url rewriting, cache management, |
|
104 |
authentication |
|
105 |
""" |
|
106 |
if self.base_url is None: |
|
107 |
self.base_url = self.config._base_url = req.base_url() |
|
108 |
# XXX https handling needs to be implemented |
|
109 |
if req.authmode == 'http': |
|
110 |
# activate realm-based auth |
|
111 |
realm = self.config['realm'] |
|
112 |
req.set_header('WWW-Authenticate', [('Basic', {'realm' : realm })], raw=False) |
|
113 |
try: |
|
114 |
self.appli.connect(req) |
|
115 |
except Redirect, ex: |
|
116 |
return self.redirect(req, ex.location) |
|
117 |
path = req.path |
|
118 |
if not path or path == "/": |
|
119 |
path = 'view' |
|
120 |
try: |
|
121 |
result = self.appli.publish(path, req) |
|
122 |
except DirectResponse, ex: |
|
123 |
return WSGIResponse(200, req, ex.response) |
|
124 |
except StatusResponse, ex: |
|
125 |
return WSGIResponse(ex.status, req, ex.content) |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4721
diff
changeset
|
126 |
except AuthenticationError: # must be before AuthenticationError |
0 | 127 |
return self.request_auth(req) |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4721
diff
changeset
|
128 |
except LogOut: |
0 | 129 |
if self.config['auth-mode'] == 'cookie': |
130 |
# in cookie mode redirecting to the index view is enough : |
|
131 |
# either anonymous connection is allowed and the page will |
|
132 |
# be displayed or we'll be redirected to the login form |
|
133 |
msg = req._('you have been logged out') |
|
134 |
# if req.https: |
|
135 |
# req._base_url = self.base_url |
|
136 |
# req.https = False |
|
137 |
url = req.build_url('view', vid='index', __message=msg) |
|
138 |
return self.redirect(req, url) |
|
139 |
else: |
|
140 |
# in http we have to request auth to flush current http auth |
|
141 |
# information |
|
142 |
return self.request_auth(req, loggedout=True) |
|
143 |
except Redirect, ex: |
|
144 |
return self.redirect(req, ex.location) |
|
145 |
if not result: |
|
146 |
# no result, something went wrong... |
|
147 |
self.error('no data (%s)', req) |
|
148 |
# 500 Internal server error |
|
149 |
return self.redirect(req, req.build_url('error')) |
|
150 |
return WSGIResponse(200, req, result) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
0
diff
changeset
|
151 |
|
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
0
diff
changeset
|
152 |
|
0 | 153 |
def __call__(self, environ, start_response): |
154 |
"""WSGI protocol entry point""" |
|
155 |
req = CubicWebWsgiRequest(environ, self.appli.vreg, self.base_url) |
|
156 |
response = self._render(req) |
|
157 |
start_response(response.status, response.headers) |
|
158 |
return response.body |
|
159 |
||
160 |
def redirect(self, req, location): |
|
161 |
"""convenience function which builds a redirect WSGIResponse""" |
|
162 |
self.debug('redirecting to %s', location) |
|
163 |
req.set_header('location', str(location)) |
|
164 |
return WSGIResponse(303, req) |
|
1802
d628defebc17
delete-trailing-whitespace + some copyright update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
0
diff
changeset
|
165 |
|
0 | 166 |
def request_auth(self, req, loggedout=False): |
167 |
"""returns the appropriate WSGIResponse to require the user to log in |
|
168 |
""" |
|
169 |
# if self.https_url and req.base_url() != self.https_url: |
|
170 |
# return self.redirect(self.https_url + 'login') |
|
171 |
if self.config['auth-mode'] == 'http': |
|
172 |
code = 401 # UNAUTHORIZED |
|
173 |
else: |
|
174 |
code = 403 # FORBIDDEN |
|
175 |
if loggedout: |
|
176 |
# if req.https: |
|
177 |
# req._base_url = self.base_url |
|
178 |
# req.https = False |
|
179 |
content = self.appli.loggedout_content(req) |
|
180 |
else: |
|
181 |
content = self.appli.need_login_content(req) |
|
182 |
return WSGIResponse(code, req, content) |
|
183 |
||
184 |
||
185 |
from logging import getLogger |
|
186 |
from cubicweb import set_log_methods |
|
187 |
set_log_methods(CubicWebWSGIApplication, getLogger('cubicweb.wsgi')) |