|
|
# -*- 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 {}
|
|
|
|