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