# -*- coding: utf-8 -*- # Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """View handlers package. """ from pyramid.response import Response import logging import simplejson as json from appenlight.lib import helpers log = logging.getLogger(__name__) def includeme(config): """Add the application's view handlers. """ config.add_route("/", "/") config.add_route("angular_app_ui_ix", "/ui") config.add_route("angular_app_ui", "/ui/*remainder") # applications API config.add_route("applications_no_id", "/applications") config.add_route( "applications", "/applications/{resource_id}", factory="appenlight.security.ResourceFactory", ) config.add_route( "applications_property", "/applications/{resource_id}/{key}", factory="appenlight.security.ResourceFactory", ) config.add_route( "integrations_id", "/applications/{resource_id}/integrations/{integration}/{action}", factory="appenlight.security.ResourceFactory", ) # users API config.add_route("users_self", "/users/self") config.add_route("users_self_property", "/users/self/{key}") config.add_route("users_no_id", "/users") config.add_route("users", "/users/{user_id}") config.add_route("users_property", "/users/{user_id}/{key}") # events config.add_route("events_no_id", "/events") config.add_route("events", "/events/{event_id}") config.add_route("events_property", "/events/{event_id}/{key}") # groups config.add_route("groups_no_id", "/groups") config.add_route("groups", "/groups/{group_id}") config.add_route("groups_property", "/groups/{group_id}/{key}") # reports API config.add_route("reports", "/reports") config.add_route("slow_reports", "/slow_reports") config.add_route( "report_groups", "/report_groups/{group_id}", factory="appenlight.security.ResourceReportFactory", ) config.add_route( "report_groups_property", "/report_groups/{group_id}/{key}", factory="appenlight.security.ResourceReportFactory", ) # generic resource API config.add_route( "resources_property", "/resources/{resource_id}/{key}", factory="appenlight.security.ResourceFactory", ) # plugin configs API config.add_route( "plugin_configs", "/plugin_configs/{plugin_name}", factory="appenlight.security.ResourcePluginMixedFactory", ) config.add_route( "plugin_config", "/plugin_configs/{plugin_name}/{id}", factory="appenlight.security.ResourcePluginConfigFactory", ) # client endpoints API config.add_route( "api_reports", "/api/reports", factory="appenlight.security.APIFactory" ) config.add_route( "api_report", "/api/report", factory="appenlight.security.APIFactory" ) config.add_route("api_logs", "/api/logs", factory="appenlight.security.APIFactory") config.add_route("api_log", "/api/log", factory="appenlight.security.APIFactory") config.add_route( "api_slow_reports", "/api/slow_reports", factory="appenlight.security.APIFactory", ) config.add_route( "api_request_stats", "/api/request_stats", factory="appenlight.security.APIFactory", ) config.add_route( "api_metrics", "/api/metrics", factory="appenlight.security.APIFactory" ) config.add_route( "api_general_metrics", "/api/general_metrics", factory="appenlight.security.APIFactory", ) config.add_route( "api_general_metric", "/api/general_metric", factory="appenlight.security.APIFactory", ) config.add_route( "api_airbrake", "/notifier_api/v2/{action}", factory="appenlight.security.AirbrakeV2APIFactory", ) config.add_route( "api_sentry", "/api/{project}/store", factory="appenlight.security.SentryAPIFactory", ) config.add_route( "api_sentry_slash", "/api/{project}/store/", factory="appenlight.security.SentryAPIFactory", ) # other config.add_route("register", "/register") config.add_route("register_ajax", "/register_ajax") config.add_route("lost_password", "/lost_password") config.add_route("lost_password_generate", "/lost_password_generate") config.add_route("logs_no_id", "/logs") config.add_route("forbidden", "/forbidden") config.add_route("test", "/test/{action}") config.add_route("section_view", "/sections/{section}/{view}") config.add_view( "appenlight.views.forbidden_view", context="pyramid.exceptions.Forbidden", renderer="appenlight:templates/forbidden.jinja2", permission="__no_permission_required__", ) config.add_view( "appenlight.views.not_found_view", context="pyramid.exceptions.NotFound", renderer="appenlight:templates/not_found.jinja2", permission="__no_permission_required__", ) config.add_view( "appenlight.views.csrf_view", context="appenlight.lib.request.CSRFException", renderer="appenlight:templates/forbidden.jinja2", permission="__no_permission_required__", ) config.add_view( "appenlight.views.csrf_view", context="appenlight.forms.CSRFException", renderer="appenlight:templates/forbidden.jinja2", permission="__no_permission_required__", ) config.add_view( "appenlight.views.colander_invalid_view", context="colander.Invalid", renderer="json", permission="__no_permission_required__", ) config.add_view( "appenlight.views.bad_json_view", context="appenlight.lib.request.JSONException", renderer="json", permission="__no_permission_required__", ) # handle authomatic config.add_route("social_auth", "/social_auth/{provider}") config.add_route("social_auth_abort", "/social_auth/{provider}/abort") # only use in production if ( config.registry.settings.get("pyramid.reload_templates") is False and config.registry.settings.get("pyramid.debug_templates") is False ): config.add_view( "appenlight.views.error_view", context=Exception, renderer="appenlight:templates/error.jinja2", permission="__no_permission_required__", ) def bad_json_view(exc, request): request.environ["appenlight.ignore_error"] = 1 request.response.headers.add("X-AppEnlight-Error", "Incorrect JSON") request.response.status_int = 400 return "Incorrect JSON" def colander_invalid_view(exc, request): request.environ["appenlight.ignore_error"] = 1 log.warning( "API version %s, %s" % (request.params.get("protocol_version"), request.context.resource) ) log.warning("Invalid payload sent") errors = exc.asdict() request.response.headers.add("X-AppEnlight-Error", "Invalid payload sent") request.response.status_int = 422 return errors def csrf_view(exc, request): request.response.status = 403 from ..models import DBSession request.environ["appenlight.ignore_error"] = 1 request.response.headers.add("X-AppEnlight-Error", str(exc)) if request.user: request.user = DBSession.merge(request.user) return {"forbidden_view": True, "csrf": True} def not_found_view(exc, request): request.response.status = 404 from ..models import DBSession if request.user: request.user = DBSession.merge(request.user) if request.user: request.response.headers["x-appenlight-uid"] = "%s" % request.user.id request.response.headers["x-appenlight-flash"] = json.dumps( helpers.get_flash(request) ) return {} def forbidden_view(exc, request): # dont serve html for api requests from ..models import DBSession if request.user: request.user = DBSession.merge(request.user) if request.path.startswith("/api"): logging.warning("Wrong API Key sent") logging.info(request.url) logging.info("\n".join(["%s:%s" % (k, v) for k, v in request.headers.items()])) resp = Response( "Wrong api key", headers=(("X-AppEnlight-Error", "Incorrect API key"),) ) resp.status_int = 403 return resp if request.user: request.response.headers["x-appenlight-uid"] = "%s" % request.user.id request.response.headers["x-appenlight-flash"] = json.dumps( helpers.get_flash(request) ) request.response.status = 403 return {"forbidden_view": True} def error_view(exc, request): from ..models import DBSession if request.user: request.user = DBSession.merge(request.user) if request.path.startswith("/api"): resp = Response( "There was a problem handling your request please try again", headers=(("X-AppEnlight-Error", "Problem handling request"),), ) resp.status_int = 500 return resp log.error(exc) request.response.status = 500 return {}