def serve_response(*path): """Serve up various respones with their correct response type""" known_types = dict( png=PNGResponse, jpg=JPGResponse, gif=PNGResponse, ico=ICOResponse, css=CSSResponse, js=JavascriptResponse, ) filename = os.path.realpath(os.path.join(*path)) logger = logging.getLogger(__name__) logger.debug('attempting to serve up filename %r', filename) if os.path.exists(filename): filenamel = filename.lower() for file_type in known_types: if filenamel.endswith('.' + file_type): data = open(filename, 'rb').read() response = known_types[file_type](data) return response return HTMLResponse('unknown file type') else: logger.warning('unable to serve filename %r', filename) relative_path = os.path.join(*path[1:]) msg = 'file not found: {}' return HTMLResponse(msg.format(relative_path))
def run_as_cgi(instance_path='..', start_time=timeit.default_timer()): if not os.path.exists(os.path.join(instance_path, 'dz.conf')): response = HTMLResponse(NEW_INSTALL_MESSAGE) else: response = generate_response(instance_path, start_time) sys.stdout.write(response.render())
def trap_errors(request, handler, *rest): """Trap exceptions and raise a server error >>> def exception_handler(request, *rest): ... raise Exception('error!') >>> def content_handler(request, *rest): ... return HTMLResponse('nuthin') >>> request = {} >>> response = trap_errors(request, content_handler) >>> status, headers, content = response.as_wsgi() >>> content b'nuthin' >>> status '200 OK' >>> response = trap_errors(request, exception_handler) >>> status, headers, content = response.as_wsgi() >>> status '500 Internal Server Error' >>> 'Server Error' in str(content) True """ try: return handler(request, *rest) except Exception: status = '500 Internal Server Error' return HTMLResponse( zoom.templates.internal_server_error_500, status )
def render(self, request): """render page""" logger = logging.getLogger(__name__) logger.debug('rendering page') self.content = render(self.header(), self.content, *self.args) app_theme = request.app.theme if hasattr(request, 'app') else None site_theme = request.site.theme self.theme = self.kwargs.get('theme', app_theme or site_theme or 'default') self.theme_uri = '/themes/' + self.theme zoom.render.add_helpers( zoom.forms.helpers(request), self.helpers(request), ) template = zoom.tools.get_template(self.template, self.theme) if zoom.system.site.settings.site.cookie_consent: zoom.requires('cookieconsent') return HTMLResponse(template, status=self.status)
def debug(request): """fake app for development purposes""" def format_section(title, content): """format a section for debugging output""" return '<pre>\n====== %s ======\n%s\n</pre>' % (title, repr(content)) def formatr(title, content): """format a section for debugging output in raw form""" return '<pre>\n====== %s ======\n%s</pre>' % (title, content) content = [] try: status = '200 OK' if request.module == 'wsgi': title = 'Hello from WSGI!' else: title = 'Hello from CGI!' content.extend([ '<br>\n', '<img src="/themes/default/images/banner_logo.png" />\n', '<hr>\n', # '<pre>{printed_output}</pre>\n', '<img src="/static/zoom/images/checkmark.png" />\n', '<br>\n', title, ]) # content.append(formatr('printed output', '{printed_output}')) content.append(formatr('test form', SAMPLE_FORM)) content.append(formatr('request', request)) content.append( formatr( 'paths', json.dumps(dict( path=[sys.path], directory=os.path.abspath('.'), pathname=__file__, ), indent=2))) content.append( formatr('environment', json.dumps(list(os.environ.items()), indent=2))) # print('testing printed output') data = request.data if 'photo' in data and data['photo'].filename: content.append(format_section('filename', data['photo'].filename)) content.append(format_section('filedata', data['photo'].value)) except Exception: content = ['<pre>{}</pre>'.format(traceback.format_exc())] return HTMLResponse(''.join(content), status=status)
def respond(content, request): """construct a response""" if content: if isinstance(content, Response): result = content elif hasattr(content, 'render') and content.render: result = content.render(request) elif isinstance(content, (list, set, tuple)): result = HTMLResponse(''.join(content)) elif isinstance(content, str): result = HTMLResponse(content) else: result = HTMLResponse('OK') return result
def debug(request): # pragma: no cover """Debugging page >>> type(debug(zoom.request.build('http://localhost/'))) <class 'zoom.response.HTMLResponse'> """ def section(title, content): """format a section for debugging output in raw form""" return '<h2>%s</h2>%s' % (title, content) pretty = zoom.utils.pretty content = [] try: if request.module == 'wsgi': title = 'Hello from WSGI!' else: title = 'Hello from CGI!' content.extend([ '<img src="/static/zoom/images/checkmark.png" />ZOOM Debug', '<hr>', '<h1>', title, '</h1>', ]) content.extend( section('request', '<pre>%s</pre>' % request) + section( 'paths', '<pre>%s</pre>' % pretty( dict( path=[sys.path], directory=os.path.abspath('.'), pathname=__file__, ) ) ) + section('request.env', '<pre>%s</pre>' % pretty(request.env)) + section('os.env', '<pre>%s</pre>' % pretty(os.environ)) ) except Exception: content = ['<pre>{}</pre>'.format(traceback.format_exc())] return HTMLResponse(''.join(content))
def run_as_app(a_request): """run as a wsgi style app""" request.__dict__ = a_request.__dict__ data.clear() data.update(request.data) del route[:] route.extend(request.route) if not os.path.exists(os.path.join(request.instance, 'dz.conf')): response = HTMLResponse(NEW_INSTALL_MESSAGE) else: response = generate_response(request.instance) return response
def render(self, request): """render page""" logger = logging.getLogger(__name__) logger.debug('rendering page') if self.title or self.subtitle or self.actions or self.search: full_page = Component(PageHeader(page=self), self.content) else: full_page = Component(self.content) self.content = full_page.render() zoom.render.add_helpers( zoom.forms.helpers(request), self.helpers(request), ) template = request.site.get_template(self.template) return HTMLResponse(template)
def render(self, request): """render page""" logger = logging.getLogger(__name__) logger.debug('rendering page') if self.title or self.subtitle or self.actions or self.search: full_page = Component(PageHeader(page=self), self.content) else: full_page = Component(self.content) self.content = full_page.render() self.theme = self.kwargs.get('theme', zoom.system.site.theme) self.theme_uri = '/themes/' + self.theme zoom.render.add_helpers( zoom.forms.helpers(request), self.helpers(request), ) template = zoom.tools.get_template(self.template, self.theme) return HTMLResponse(template)
def serve_static(request, handler, *rest): """Serve a static file Static files can be served in serveral ways. This particular middleware intended to be inserted into the middleware stack near the front before most other things. The reason being is that in many cases the static files are not site specific or app specific but rather are shared by the entire instance. In addition, many times static files require no authorization or special rights. This means that in many cases static files can be served without knowing who is asking or what site they are for. That is what this middleware does. This handler is mainly intended for development purposes as in a production evironment this type of content would typically be served up by the HTTP server or a caching layer. Note: This layer looks in four separate places for static files to serve. Three related to the zoom instance, and one, as a last resort, from the zoom library itself. If you are concerned about exposing the wrong files, be careful what you put in those directories or better yet, use a proxy. In addition to this instance wide static serving Sites and Apps may implement static file serving. If they do, this layer is unaware of that fact. The locations this handler will look are relative to the zoom instance. They are: <instance>/static <instance>/www/static <instance>/../static zoom/web/www/static >>> url = 'http://localhost/static/zoom/zoom.js' >>> request = zoom.request.build(url) >>> result = serve_static(request, lambda a: False) >>> isinstance(result, JavascriptResponse) True >>> url = 'http://localhost/notstatic/zoom/zoom.js' >>> request = zoom.request.build(url) >>> result = serve_static(request, lambda a: False) >>> isinstance(result, JavascriptResponse) False """ if request.path.startswith('/static/'): logger = logging.getLogger(__name__) isdir, join = os.path.isdir, os.path.join locations = list( filter(isdir, [ join(request.instance, 'static'), join(request.instance, 'www', 'static'), join(request.instance, '..', 'static'), zoom.tools.zoompath('zoom', '_assets', 'web', 'www', 'static'), ])) for location in locations: pathname = join(location, *request.route[1:]) logger.debug('looking for %r', pathname) if os.path.isfile(pathname): return serve_response(pathname) path = '/'.join(request.route[1:]) logger.warning('static resource %r not found in %r', path, locations) msg = 'resource not found: {!r}' return HTMLResponse(msg.format(path), status='404 Not Found') else: return handler(request, *rest)
def serve_response(*path): """Serve up various respones with their correct response type >>> zoom_js = zoom.tools.zoompath('zoom/_assets/web/www/static/zoom/zoom.js') >>> response = serve_response(zoom_js) >>> isinstance(response, JavascriptResponse) True >>> zoom_path = zoom.tools.zoompath('zoom', '_assets', 'web') >>> response = serve_response(zoom_path, 'www/static/zoom/nada.js') >>> isinstance(response, JavascriptResponse) False >>> response.content "file not found: 'www/static/zoom/nada.js'" >>> response.status '404 Not Found' >>> zoom_path = zoom.tools.zoompath('zoom', '_assets', 'web') >>> response = serve_response(zoom_path, 'www/static/zoom/images') >>> isinstance(response, JavascriptResponse) False >>> response.content "unknown file type ''" >>> response.status '415 Unsupported Media Type' """ known_types = dict( png=PNGResponse, jpg=JPGResponse, gif=PNGResponse, ico=ICOResponse, css=CSSResponse, sass=CSSResponse, js=JavascriptResponse, ttf=TTFResponse, json=JSONResponse, woff=WOFFResponse, woff2=WOFF2Response, map=BinaryResponse, svg=SVGResponse, ) exists = os.path.exists isfile = os.path.isfile pathname = os.path.realpath(os.path.join(*path)) logger = logging.getLogger(__name__) logger.debug('attempting to serve up file %r', pathname) if not isfile(pathname) and pathname.endswith('.css'): alt_pathname = pathname[:-3] + 'sass' logger.debug('trying alt_pathname %r', alt_pathname) if isfile(alt_pathname): pathname = alt_pathname else: raise Exception('not a file') if exists(pathname): pathnamel = pathname.lower() _, file_type = os.path.splitext(pathnamel) file_type = file_type[1:] response_type = known_types.get(file_type) if response_type: if file_type == 'json': with open(pathname, 'rb') as f: data = f.read() # JSONResponse expects an object which it will seriaize # so this unserializaing / reserializing an extra step # which may be unnecessary. For now, we'll use the # JSONResponse as designed but we may eventually want to # create a different response type in this case. data = zoom.jsonz.loads(data) elif file_type == 'sass': logger.debug('rendering sass file response %r', pathname) data = zoom.tools.sass(pathname).encode('utf8') else: with open(pathname, 'rb') as f: data = f.read() return response_type(data) msg = 'unknown file type {!r}'.format(file_type) logger.warning(msg) return HTMLResponse(msg, status='415 Unsupported Media Type') else: logger.warning('unable to serve file %r', pathname) relative_path = os.path.join(*path[1:]) msg = 'file not found: {!r}' return HTMLResponse(msg.format(relative_path), status='404 Not Found')
def generate_response(instance_path, start_time=None): """generate response to web request""" profiler = None debugging = True system_timer = SystemTimer(start_time) # capture stdout real_stdout = sys.stdout sys.stdout = StringIO.StringIO() try: try: # initialize context system.setup(instance_path, request.server, system_timer) system_timer.add('system initializated') user.setup() system_timer.add('user initializated') manager.setup() system_timer.add('manager initializated') if user.is_disabled: # we know who the user is, and their account is disabled msg = 'User {user.link} is disabled' raise UnauthorizedException(msg.format(user=user)) debugging = (system.debugging or system.show_errors or user.is_developer or user.is_administrator) session = system.session if system.track_visits: visited(request.subject, session.sid) csrf_token = data.pop('csrf_token', None) if request.method == 'POST' and system.csrf_validation: if csrf_token == session.csrf_token: del session.csrf_token else: msg = 'expected:%s got:%s' % (session.csrf_token, csrf_token) raise CrossSiteRequestForgeryAttempt(msg) requested_app_name = manager.requested_app_name() default_app_name = manager.default_app_name() os.chdir(system.config.sites_path) if not request.route: request.route.append(default_app_name) for app in manager.apps.values(): app.initialize(request) if manager.can_run(requested_app_name): system.app = manager.get_app(requested_app_name) profiler = (system.profile or user.profile) \ and cProfile.Profile() if profiler: profiler.enable() system_timer.add('app ready') response = system.app.run(request) system_timer.add('app returned') if profiler: profiler.disable() elif manager.can_run_if_login(requested_app_name): # as it stands now, an attacker can generate a list of # enabled apps by iterating the/a namespace and seeing # which ones return a logon form. def referrer(): """get the referrer""" uri = urllib.urlencode(dict(referrer=request.uri)) return uri and "?{}".format(uri) or '' response = redirect_to('/login{}'.format(referrer())) elif not requested_app_name: app = manager.get_app(default_app_name) if app: system.app = app else: raise Exception(default_app_name + ' app missing') response = system.app.run(request) elif manager.can_run(default_app_name): response = redirect_to('/') else: response = Page(PAGE_MISSING_MESSAGE).render() response.status = '404' timeout = session.save_session() set_session_cookie( response, session.sid, request.subject, timeout, system.secure_cookies, ) except UnauthorizedException: logger.security('unauthorized access attempt') if debugging: raise else: response = Page(UNAUTHORIZED_MESSAGE).render() response.status = '403' except CrossSiteRequestForgeryAttempt: logger.security('cross site forgery attempt') if debugging: raise else: response = redirect_to('/') except SessionExpiredException: response = Page( load_template('system_application_session_expired', SESSION_EXPIRED_MESSAGE)).render() except: t = htmlquote(traceback.format_exc()) logger.error(t) if debugging: try: tpl = load_template('system_application_error_developer', STANDARD_ERROR_MESSAGE) msg = tpl % dict(message=t) except: msg = SYSTEM_ERROR_MESSAGE % dict(message=t) else: try: msg = load_template('system_application_error_user', FRIENDLY_ERROR_MESSAGE) except: msg = FRIENDLY_ERROR_MESSAGE try: response = Page(msg).render() except: response = HTMLResponse(msg) if profiler: stats_s = StringIO.StringIO() sortby = 'cumulative' ps = pstats.Stats(profiler, stream=stats_s) ps.sort_stats(sortby) ps.print_stats(.1) t = stats_s.getvalue() t = t.replace(system.lib_path, '~zoom').replace( '/usr/lib/python2.7/dist-packages/', '~').replace('/usr/local/lib/python2.7/dist-packages/', '~') print(''.join([ '\n\n System Performance Metrics\n ' + '=' * 30, system_timer.report(), system.database.report(), system.db.report(), ' Profiler\n ------------\n', t ])) finally: printed_output = sys.stdout.getvalue() sys.stdout.close() sys.stdout = real_stdout logger.complete() system.release() if hasattr(response, 'printed_output'): response.printed_output = printed_output.replace('<', '<').replace( '>', '>') return response