1 | """WSGI Policy Enforcement Point basic result handler module for a Genshi |
---|
2 | based implementation. Access denied HTML response is rendered using the |
---|
3 | Genshi templating language. |
---|
4 | |
---|
5 | NERC DataGrid Project |
---|
6 | """ |
---|
7 | __author__ = "P J Kershaw" |
---|
8 | __date__ = "05/01/10" |
---|
9 | __copyright__ = "(C) 2010 Science and Technology Facilities Council" |
---|
10 | __contact__ = "Philip.Kershaw@stfc.ac.uk" |
---|
11 | __revision__ = "$Id: $" |
---|
12 | __license__ = "BSD - see LICENSE file in top-level directory" |
---|
13 | from string import Template |
---|
14 | from genshi.template import TemplateLoader |
---|
15 | |
---|
16 | from httplib import UNAUTHORIZED, FORBIDDEN |
---|
17 | |
---|
18 | from ndg.security.server.wsgi import NDGSecurityMiddlewareBase |
---|
19 | from ndg.security.server.wsgi.session import SessionMiddlewareBase |
---|
20 | |
---|
21 | |
---|
22 | class GenshiPEPResultHandlerMiddleware(SessionMiddlewareBase): |
---|
23 | """Genshi based PEP result handler |
---|
24 | """ |
---|
25 | DEFAULT_TMPL_NAME = 'accessdenied.html' |
---|
26 | DEFAULT_TMPL_DIR = path.join(path.dirname(__file__), 'templates') |
---|
27 | DEFAULT_STATIC_CONTENT_DIR = path.join(path.dirname(__file__), 'layout') |
---|
28 | |
---|
29 | MSG_TMPL = ( |
---|
30 | "Access is forbidden for this resource:<br/><br/>" |
---|
31 | "$pdpResponseMsg<br/><br/>" |
---|
32 | "Please check with your site administrator that you have the required " |
---|
33 | "access privileges." |
---|
34 | ) |
---|
35 | |
---|
36 | PROPERTY_DEFAULTS = { |
---|
37 | 'messageTemplate': MSG_TMPL, |
---|
38 | 'templateName': DEFAULT_TMPL_NAME, |
---|
39 | 'templateRootDir': DEFAULT_TMPL_DIR, |
---|
40 | 'staticContentRootDir': DEFAULT_STATIC_CONTENT_DIR, |
---|
41 | 'heading': '', |
---|
42 | 'leftLogo': '', |
---|
43 | 'leftAlt': '', |
---|
44 | 'leftLink': '', |
---|
45 | 'leftImage': '', |
---|
46 | 'footerText': 'Test deployment only', |
---|
47 | 'rightLink': '', |
---|
48 | 'rightImage': '', |
---|
49 | 'rightAlt': '', |
---|
50 | 'helpIcon': '' |
---|
51 | } |
---|
52 | __slots__ = PROPERTY_DEFAULTS |
---|
53 | |
---|
54 | def __init__(self, app, global_conf, prefix='', **app_conf): |
---|
55 | ''' |
---|
56 | @type app: callable following WSGI interface |
---|
57 | @param app: next middleware application in the chain |
---|
58 | @type global_conf: dict |
---|
59 | @param global_conf: PasteDeploy global configuration dictionary |
---|
60 | @type prefix: basestring |
---|
61 | @param prefix: prefix for configuration items |
---|
62 | @type app_conf: dict |
---|
63 | @param app_conf: PasteDeploy application specific configuration |
---|
64 | dictionary |
---|
65 | ''' |
---|
66 | super(GenshiPEPResultHandlerMiddleware, self).__init__(app, |
---|
67 | global_conf, |
---|
68 | prefix=prefix, |
---|
69 | **app_conf) |
---|
70 | |
---|
71 | # Initialise attributes |
---|
72 | for k, v in GenshiPEPResultHandlerMiddleware.PROPERTY_DEFAULTS.items(): |
---|
73 | setattr(self, k, v) |
---|
74 | |
---|
75 | # Update from keywords |
---|
76 | for i in app_conf: |
---|
77 | setattr(self, i, app_conf[i]) |
---|
78 | |
---|
79 | self.__loader = TemplateLoader(self.templateRootDir, auto_reload=True) |
---|
80 | |
---|
81 | @NDGSecurityMiddlewareBase.initCall |
---|
82 | def __call__(self, environ, start_response): |
---|
83 | """Render access denied message or else if user is not authenticated, |
---|
84 | set HTTP 401 response |
---|
85 | |
---|
86 | @type environ: dict |
---|
87 | @param environ: WSGI environment variables dictionary |
---|
88 | @type start_response: function |
---|
89 | @param start_response: standard WSGI start response function |
---|
90 | @rtype: iterable |
---|
91 | @return: response |
---|
92 | """ |
---|
93 | if not self.isAuthenticated: |
---|
94 | # sets 401 response to be trapped by authentication handler |
---|
95 | log.warning("PEPResultHandlerMiddleware: user is not " |
---|
96 | "authenticated - setting HTTP 401 response") |
---|
97 | return self._setErrorResponse(code=UNAUTHORIZED) |
---|
98 | else: |
---|
99 | # Get response message from PDP recorded by PEP |
---|
100 | pepCtx = self.session.get(PEPFilter.PEPCTX_SESSION_KEYNAME, {}) |
---|
101 | pdpResponse = pepCtx.get(PEPFilter.PEPCTX_RESPONSE_SESSION_KEYNAME) |
---|
102 | pdpResponseMsg = getattr(pdpResponse, 'message', '') or '' |
---|
103 | |
---|
104 | msg = Template(self.messageTemplate).substitute( |
---|
105 | pdpResponseMsg=pdpResponseMsg) |
---|
106 | |
---|
107 | response = self._render(xml=msg) |
---|
108 | start_response( |
---|
109 | GenshiPEPResultHandlerMiddleware.getStatusMessage(FORBIDDEN), |
---|
110 | [('Content-type', 'text/html'), |
---|
111 | ('Content-Length', str(len(response)))]) |
---|
112 | |
---|
113 | return response |
---|
114 | |
---|
115 | def __setattr__(self, name, value): |
---|
116 | """Apply some generic type checking""" |
---|
117 | if name in GenshiPEPResultHandlerMiddleware.PROPERTY_DEFAULTS: |
---|
118 | if not isinstance(value, basestring): |
---|
119 | raise TypeError('Expecting string type for %r attribute; got ' |
---|
120 | '%r' % (name, type(value))) |
---|
121 | |
---|
122 | super(GenshiPEPResultHandlerMiddleware, self).__setattr__(name, value) |
---|
123 | |
---|
124 | def _getLoader(self): |
---|
125 | return self.__loader |
---|
126 | |
---|
127 | def _setLoader(self, value): |
---|
128 | if not isinstance(value, TemplateLoader): |
---|
129 | raise TypeError('Expecting %r type for "loader"; got %r' % |
---|
130 | (TemplateLoader, type(value))) |
---|
131 | self.__loader = value |
---|
132 | |
---|
133 | loader = property(_getLoader, _setLoader, |
---|
134 | doc="Genshi TemplateLoader instance") |
---|
135 | |
---|
136 | def _render(self, c=None, **kw): |
---|
137 | '''Wrapper for Genshi template rendering |
---|
138 | |
---|
139 | @type c: None/object |
---|
140 | @param c: reference to object to pass into template - defaults to self |
---|
141 | @type kw: dict |
---|
142 | @param kw: keywords to pass to template |
---|
143 | @rtype: string |
---|
144 | @return: rendered template |
---|
145 | ''' |
---|
146 | if c is None: |
---|
147 | kw['c'] = self |
---|
148 | |
---|
149 | tmpl = self.loader.load(self.templateName) |
---|
150 | rendering = tmpl.generate(**kw).render('html', doctype='html') |
---|
151 | |
---|
152 | return rendering |
---|