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