##// END OF EJS Templates
observers: hide it for CE edition....
marcink -
r4516:6a883584 stable
parent child
Show More
@@ -1,111 +1,111
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 from rhodecode.lib import helpers as h, rc_cache
21 from rhodecode.lib import helpers as h, rc_cache
22 from rhodecode.lib.utils2 import safe_int
22 from rhodecode.lib.utils2 import safe_int
23 from rhodecode.model.pull_request import get_diff_info
23 from rhodecode.model.pull_request import get_diff_info
24 from rhodecode.model.db import PullRequestReviewers
24 from rhodecode.model.db import PullRequestReviewers
25 # V3 - Reviewers, with default rules data
25 # V3 - Reviewers, with default rules data
26 # v4 - Added observers metadata
26 # v4 - Added observers metadata
27 REVIEWER_API_VERSION = 'V4'
27 REVIEWER_API_VERSION = 'V4'
28
28
29
29
30 def reviewer_as_json(user, reasons=None, role=None, mandatory=False, rules=None, user_group=None):
30 def reviewer_as_json(user, reasons=None, role=None, mandatory=False, rules=None, user_group=None):
31 """
31 """
32 Returns json struct of a reviewer for frontend
32 Returns json struct of a reviewer for frontend
33
33
34 :param user: the reviewer
34 :param user: the reviewer
35 :param reasons: list of strings of why they are reviewers
35 :param reasons: list of strings of why they are reviewers
36 :param mandatory: bool, to set user as mandatory
36 :param mandatory: bool, to set user as mandatory
37 """
37 """
38 role = role or PullRequestReviewers.ROLE_REVIEWER
38 role = role or PullRequestReviewers.ROLE_REVIEWER
39 if role not in PullRequestReviewers.ROLES:
39 if role not in PullRequestReviewers.ROLES:
40 raise ValueError('role is not one of %s', PullRequestReviewers.ROLES)
40 raise ValueError('role is not one of %s', PullRequestReviewers.ROLES)
41
41
42 return {
42 return {
43 'user_id': user.user_id,
43 'user_id': user.user_id,
44 'reasons': reasons or [],
44 'reasons': reasons or [],
45 'rules': rules or [],
45 'rules': rules or [],
46 'role': role,
46 'role': role,
47 'mandatory': mandatory,
47 'mandatory': mandatory,
48 'user_group': user_group,
48 'user_group': user_group,
49 'username': user.username,
49 'username': user.username,
50 'first_name': user.first_name,
50 'first_name': user.first_name,
51 'last_name': user.last_name,
51 'last_name': user.last_name,
52 'user_link': h.link_to_user(user),
52 'user_link': h.link_to_user(user),
53 'gravatar_link': h.gravatar_url(user.email, 14),
53 'gravatar_link': h.gravatar_url(user.email, 14),
54 }
54 }
55
55
56
56
57 def to_reviewers(e):
57 def to_reviewers(e):
58 if isinstance(e, (tuple, list)):
58 if isinstance(e, (tuple, list)):
59 return map(reviewer_as_json, e)
59 return map(reviewer_as_json, e)
60 else:
60 else:
61 return reviewer_as_json(e)
61 return reviewer_as_json(e)
62
62
63
63
64 def get_default_reviewers_data(current_user, source_repo, source_ref, target_repo, target_ref,
64 def get_default_reviewers_data(current_user, source_repo, source_ref, target_repo, target_ref,
65 include_diff_info=True):
65 include_diff_info=True):
66 """
66 """
67 Return json for default reviewers of a repository
67 Return json for default reviewers of a repository
68 """
68 """
69
69
70 diff_info = {}
70 diff_info = {}
71 if include_diff_info:
71 if include_diff_info:
72 diff_info = get_diff_info(
72 diff_info = get_diff_info(
73 source_repo, source_ref.commit_id, target_repo, target_ref.commit_id)
73 source_repo, source_ref.commit_id, target_repo, target_ref.commit_id)
74
74
75 reasons = ['Default reviewer', 'Repository owner']
75 reasons = ['Default reviewer', 'Repository owner']
76 json_reviewers = [reviewer_as_json(
76 json_reviewers = [reviewer_as_json(
77 user=target_repo.user, reasons=reasons, mandatory=False, rules=None, role=None)]
77 user=target_repo.user, reasons=reasons, mandatory=False, rules=None, role=None)]
78
78
79 compute_key = rc_cache.utils.compute_key_from_params(
79 compute_key = rc_cache.utils.compute_key_from_params(
80 current_user.user_id, source_repo.repo_id, source_ref.type, source_ref.name,
80 current_user.user_id, source_repo.repo_id, source_ref.type, source_ref.name,
81 source_ref.commit_id, target_repo.repo_id, target_ref.type, target_ref.name,
81 source_ref.commit_id, target_repo.repo_id, target_ref.type, target_ref.name,
82 target_ref.commit_id)
82 target_ref.commit_id)
83
83
84 return {
84 return {
85 'api_ver': REVIEWER_API_VERSION, # define version for later possible schema upgrade
85 'api_ver': REVIEWER_API_VERSION, # define version for later possible schema upgrade
86 'compute_key': compute_key,
86 'compute_key': compute_key,
87 'diff_info': diff_info,
87 'diff_info': diff_info,
88 'reviewers': json_reviewers,
88 'reviewers': json_reviewers,
89 'rules': {},
89 'rules': {},
90 'rules_data': {},
90 'rules_data': {},
91 }
91 }
92
92
93
93
94 def validate_default_reviewers(review_members, reviewer_rules):
94 def validate_default_reviewers(review_members, reviewer_rules):
95 """
95 """
96 Function to validate submitted reviewers against the saved rules
96 Function to validate submitted reviewers against the saved rules
97 """
97 """
98 reviewers = []
98 reviewers = []
99 reviewer_by_id = {}
99 reviewer_by_id = {}
100 for r in review_members:
100 for r in review_members:
101 reviewer_user_id = safe_int(r['user_id'])
101 reviewer_user_id = safe_int(r['user_id'])
102 entry = (reviewer_user_id, r['reasons'], r['mandatory'], r['role'], r['rules'])
102 entry = (reviewer_user_id, r['reasons'], r['mandatory'], r['role'], r['rules'])
103
103
104 reviewer_by_id[reviewer_user_id] = entry
104 reviewer_by_id[reviewer_user_id] = entry
105 reviewers.append(entry)
105 reviewers.append(entry)
106
106
107 return reviewers
107 return reviewers
108
108
109
109
110 def validate_observers(observer_members):
110 def validate_observers(observer_members, reviewer_rules):
111 return {}
111 return {}
@@ -1,767 +1,768
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import tempfile
25 import tempfile
26 import time
26 import time
27
27
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 import pyramid.events
29 import pyramid.events
30 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
31 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.authorization import ACLAuthorizationPolicy
32 from pyramid.config import Configurator
32 from pyramid.config import Configurator
33 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
36 from pyramid.renderers import render_to_response
36 from pyramid.renderers import render_to_response
37
37
38 from rhodecode.model import meta
38 from rhodecode.model import meta
39 from rhodecode.config import patches
39 from rhodecode.config import patches
40 from rhodecode.config import utils as config_utils
40 from rhodecode.config import utils as config_utils
41 from rhodecode.config.environment import load_pyramid_environment
41 from rhodecode.config.environment import load_pyramid_environment
42
42
43 import rhodecode.events
43 import rhodecode.events
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 from rhodecode.lib.request import Request
45 from rhodecode.lib.request import Request
46 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.vcs import VCSCommunicationError
47 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.celerylib.loader import configure_celery
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
53 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.lib.exc_tracking import store_exception
54 from rhodecode.subscribers import (
54 from rhodecode.subscribers import (
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 write_metadata_if_needed, write_usage_data, inject_app_settings)
56 write_metadata_if_needed, write_usage_data, inject_app_settings)
57
57
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 def is_http_error(response):
62 def is_http_error(response):
63 # error which should have traceback
63 # error which should have traceback
64 return response.status_code > 499
64 return response.status_code > 499
65
65
66
66
67 def should_load_all():
67 def should_load_all():
68 """
68 """
69 Returns if all application components should be loaded. In some cases it's
69 Returns if all application components should be loaded. In some cases it's
70 desired to skip apps loading for faster shell script execution
70 desired to skip apps loading for faster shell script execution
71 """
71 """
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
73 if ssh_cmd:
73 if ssh_cmd:
74 return False
74 return False
75
75
76 return True
76 return True
77
77
78
78
79 def make_pyramid_app(global_config, **settings):
79 def make_pyramid_app(global_config, **settings):
80 """
80 """
81 Constructs the WSGI application based on Pyramid.
81 Constructs the WSGI application based on Pyramid.
82
82
83 Specials:
83 Specials:
84
84
85 * The application can also be integrated like a plugin via the call to
85 * The application can also be integrated like a plugin via the call to
86 `includeme`. This is accompanied with the other utility functions which
86 `includeme`. This is accompanied with the other utility functions which
87 are called. Changing this should be done with great care to not break
87 are called. Changing this should be done with great care to not break
88 cases when these fragments are assembled from another place.
88 cases when these fragments are assembled from another place.
89
89
90 """
90 """
91
91
92 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
92 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
93 # will be replaced by the value of the environment variable "NAME" in this case.
93 # will be replaced by the value of the environment variable "NAME" in this case.
94 start_time = time.time()
94 start_time = time.time()
95
95
96 debug = asbool(global_config.get('debug'))
96 debug = asbool(global_config.get('debug'))
97 if debug:
97 if debug:
98 enable_debug()
98 enable_debug()
99
99
100 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
100 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
101
101
102 global_config = _substitute_values(global_config, environ)
102 global_config = _substitute_values(global_config, environ)
103 settings = _substitute_values(settings, environ)
103 settings = _substitute_values(settings, environ)
104
104
105 sanitize_settings_and_apply_defaults(global_config, settings)
105 sanitize_settings_and_apply_defaults(global_config, settings)
106
106
107 config = Configurator(settings=settings)
107 config = Configurator(settings=settings)
108
108
109 # Apply compatibility patches
109 # Apply compatibility patches
110 patches.inspect_getargspec()
110 patches.inspect_getargspec()
111
111
112 load_pyramid_environment(global_config, settings)
112 load_pyramid_environment(global_config, settings)
113
113
114 # Static file view comes first
114 # Static file view comes first
115 includeme_first(config)
115 includeme_first(config)
116
116
117 includeme(config)
117 includeme(config)
118
118
119 pyramid_app = config.make_wsgi_app()
119 pyramid_app = config.make_wsgi_app()
120 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
120 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
121 pyramid_app.config = config
121 pyramid_app.config = config
122
122
123 config.configure_celery(global_config['__file__'])
123 config.configure_celery(global_config['__file__'])
124 # creating the app uses a connection - return it after we are done
124 # creating the app uses a connection - return it after we are done
125 meta.Session.remove()
125 meta.Session.remove()
126 total_time = time.time() - start_time
126 total_time = time.time() - start_time
127 log.info('Pyramid app `%s` created and configured in %.2fs',
127 log.info('Pyramid app `%s` created and configured in %.2fs',
128 pyramid_app.func_name, total_time)
128 pyramid_app.func_name, total_time)
129
129
130 return pyramid_app
130 return pyramid_app
131
131
132
132
133 def not_found_view(request):
133 def not_found_view(request):
134 """
134 """
135 This creates the view which should be registered as not-found-view to
135 This creates the view which should be registered as not-found-view to
136 pyramid.
136 pyramid.
137 """
137 """
138
138
139 if not getattr(request, 'vcs_call', None):
139 if not getattr(request, 'vcs_call', None):
140 # handle like regular case with our error_handler
140 # handle like regular case with our error_handler
141 return error_handler(HTTPNotFound(), request)
141 return error_handler(HTTPNotFound(), request)
142
142
143 # handle not found view as a vcs call
143 # handle not found view as a vcs call
144 settings = request.registry.settings
144 settings = request.registry.settings
145 ae_client = getattr(request, 'ae_client', None)
145 ae_client = getattr(request, 'ae_client', None)
146 vcs_app = VCSMiddleware(
146 vcs_app = VCSMiddleware(
147 HTTPNotFound(), request.registry, settings,
147 HTTPNotFound(), request.registry, settings,
148 appenlight_client=ae_client)
148 appenlight_client=ae_client)
149
149
150 return wsgiapp(vcs_app)(None, request)
150 return wsgiapp(vcs_app)(None, request)
151
151
152
152
153 def error_handler(exception, request):
153 def error_handler(exception, request):
154 import rhodecode
154 import rhodecode
155 from rhodecode.lib import helpers
155 from rhodecode.lib import helpers
156
156
157 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
157 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
158
158
159 base_response = HTTPInternalServerError()
159 base_response = HTTPInternalServerError()
160 # prefer original exception for the response since it may have headers set
160 # prefer original exception for the response since it may have headers set
161 if isinstance(exception, HTTPException):
161 if isinstance(exception, HTTPException):
162 base_response = exception
162 base_response = exception
163 elif isinstance(exception, VCSCommunicationError):
163 elif isinstance(exception, VCSCommunicationError):
164 base_response = VCSServerUnavailable()
164 base_response = VCSServerUnavailable()
165
165
166 if is_http_error(base_response):
166 if is_http_error(base_response):
167 log.exception(
167 log.exception(
168 'error occurred handling this request for path: %s', request.path)
168 'error occurred handling this request for path: %s', request.path)
169
169
170 error_explanation = base_response.explanation or str(base_response)
170 error_explanation = base_response.explanation or str(base_response)
171 if base_response.status_code == 404:
171 if base_response.status_code == 404:
172 error_explanation += " Optionally you don't have permission to access this page."
172 error_explanation += " Optionally you don't have permission to access this page."
173 c = AttributeDict()
173 c = AttributeDict()
174 c.error_message = base_response.status
174 c.error_message = base_response.status
175 c.error_explanation = error_explanation
175 c.error_explanation = error_explanation
176 c.visual = AttributeDict()
176 c.visual = AttributeDict()
177
177
178 c.visual.rhodecode_support_url = (
178 c.visual.rhodecode_support_url = (
179 request.registry.settings.get('rhodecode_support_url') or
179 request.registry.settings.get('rhodecode_support_url') or
180 request.route_url('rhodecode_support')
180 request.route_url('rhodecode_support')
181 )
181 )
182 c.redirect_time = 0
182 c.redirect_time = 0
183 c.rhodecode_name = rhodecode_title
183 c.rhodecode_name = rhodecode_title
184 if not c.rhodecode_name:
184 if not c.rhodecode_name:
185 c.rhodecode_name = 'Rhodecode'
185 c.rhodecode_name = 'Rhodecode'
186
186
187 c.causes = []
187 c.causes = []
188 if is_http_error(base_response):
188 if is_http_error(base_response):
189 c.causes.append('Server is overloaded.')
189 c.causes.append('Server is overloaded.')
190 c.causes.append('Server database connection is lost.')
190 c.causes.append('Server database connection is lost.')
191 c.causes.append('Server expected unhandled error.')
191 c.causes.append('Server expected unhandled error.')
192
192
193 if hasattr(base_response, 'causes'):
193 if hasattr(base_response, 'causes'):
194 c.causes = base_response.causes
194 c.causes = base_response.causes
195
195
196 c.messages = helpers.flash.pop_messages(request=request)
196 c.messages = helpers.flash.pop_messages(request=request)
197
197
198 exc_info = sys.exc_info()
198 exc_info = sys.exc_info()
199 c.exception_id = id(exc_info)
199 c.exception_id = id(exc_info)
200 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
200 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
201 or base_response.status_code > 499
201 or base_response.status_code > 499
202 c.exception_id_url = request.route_url(
202 c.exception_id_url = request.route_url(
203 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
203 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
204
204
205 if c.show_exception_id:
205 if c.show_exception_id:
206 store_exception(c.exception_id, exc_info)
206 store_exception(c.exception_id, exc_info)
207
207
208 response = render_to_response(
208 response = render_to_response(
209 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
209 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
210 response=base_response)
210 response=base_response)
211
211
212 return response
212 return response
213
213
214
214
215 def includeme_first(config):
215 def includeme_first(config):
216 # redirect automatic browser favicon.ico requests to correct place
216 # redirect automatic browser favicon.ico requests to correct place
217 def favicon_redirect(context, request):
217 def favicon_redirect(context, request):
218 return HTTPFound(
218 return HTTPFound(
219 request.static_path('rhodecode:public/images/favicon.ico'))
219 request.static_path('rhodecode:public/images/favicon.ico'))
220
220
221 config.add_view(favicon_redirect, route_name='favicon')
221 config.add_view(favicon_redirect, route_name='favicon')
222 config.add_route('favicon', '/favicon.ico')
222 config.add_route('favicon', '/favicon.ico')
223
223
224 def robots_redirect(context, request):
224 def robots_redirect(context, request):
225 return HTTPFound(
225 return HTTPFound(
226 request.static_path('rhodecode:public/robots.txt'))
226 request.static_path('rhodecode:public/robots.txt'))
227
227
228 config.add_view(robots_redirect, route_name='robots')
228 config.add_view(robots_redirect, route_name='robots')
229 config.add_route('robots', '/robots.txt')
229 config.add_route('robots', '/robots.txt')
230
230
231 config.add_static_view(
231 config.add_static_view(
232 '_static/deform', 'deform:static')
232 '_static/deform', 'deform:static')
233 config.add_static_view(
233 config.add_static_view(
234 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
234 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
235
235
236
236
237 def includeme(config):
237 def includeme(config):
238 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
238 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
239 settings = config.registry.settings
239 settings = config.registry.settings
240 config.set_request_factory(Request)
240 config.set_request_factory(Request)
241
241
242 # plugin information
242 # plugin information
243 config.registry.rhodecode_plugins = collections.OrderedDict()
243 config.registry.rhodecode_plugins = collections.OrderedDict()
244
244
245 config.add_directive(
245 config.add_directive(
246 'register_rhodecode_plugin', register_rhodecode_plugin)
246 'register_rhodecode_plugin', register_rhodecode_plugin)
247
247
248 config.add_directive('configure_celery', configure_celery)
248 config.add_directive('configure_celery', configure_celery)
249
249
250 if asbool(settings.get('appenlight', 'false')):
250 if asbool(settings.get('appenlight', 'false')):
251 config.include('appenlight_client.ext.pyramid_tween')
251 config.include('appenlight_client.ext.pyramid_tween')
252
252
253 load_all = should_load_all()
253 load_all = should_load_all()
254
254
255 # Includes which are required. The application would fail without them.
255 # Includes which are required. The application would fail without them.
256 config.include('pyramid_mako')
256 config.include('pyramid_mako')
257 config.include('rhodecode.lib.rc_beaker')
257 config.include('rhodecode.lib.rc_beaker')
258 config.include('rhodecode.lib.rc_cache')
258 config.include('rhodecode.lib.rc_cache')
259
259
260 config.include('rhodecode.apps._base.navigation')
260 config.include('rhodecode.apps._base.navigation')
261 config.include('rhodecode.apps._base.subscribers')
261 config.include('rhodecode.apps._base.subscribers')
262 config.include('rhodecode.tweens')
262 config.include('rhodecode.tweens')
263 config.include('rhodecode.authentication')
263 config.include('rhodecode.authentication')
264
264
265 if load_all:
265 if load_all:
266 config.include('rhodecode.integrations')
266 config.include('rhodecode.integrations')
267
267
268 if load_all:
268 if load_all:
269 # load CE authentication plugins
269 # load CE authentication plugins
270 config.include('rhodecode.authentication.plugins.auth_crowd')
270 config.include('rhodecode.authentication.plugins.auth_crowd')
271 config.include('rhodecode.authentication.plugins.auth_headers')
271 config.include('rhodecode.authentication.plugins.auth_headers')
272 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
272 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
273 config.include('rhodecode.authentication.plugins.auth_ldap')
273 config.include('rhodecode.authentication.plugins.auth_ldap')
274 config.include('rhodecode.authentication.plugins.auth_pam')
274 config.include('rhodecode.authentication.plugins.auth_pam')
275 config.include('rhodecode.authentication.plugins.auth_rhodecode')
275 config.include('rhodecode.authentication.plugins.auth_rhodecode')
276 config.include('rhodecode.authentication.plugins.auth_token')
276 config.include('rhodecode.authentication.plugins.auth_token')
277
277
278 # Auto discover authentication plugins and include their configuration.
278 # Auto discover authentication plugins and include their configuration.
279 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
279 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
280 from rhodecode.authentication import discover_legacy_plugins
280 from rhodecode.authentication import discover_legacy_plugins
281 discover_legacy_plugins(config)
281 discover_legacy_plugins(config)
282
282
283 # apps
283 # apps
284 if load_all:
284 if load_all:
285 config.include('rhodecode.apps._base')
285 config.include('rhodecode.apps._base')
286 config.include('rhodecode.apps.hovercards')
286 config.include('rhodecode.apps.hovercards')
287 config.include('rhodecode.apps.ops')
287 config.include('rhodecode.apps.ops')
288 config.include('rhodecode.apps.admin')
288 config.include('rhodecode.apps.admin')
289 config.include('rhodecode.apps.channelstream')
289 config.include('rhodecode.apps.channelstream')
290 config.include('rhodecode.apps.file_store')
290 config.include('rhodecode.apps.file_store')
291 config.include('rhodecode.apps.login')
291 config.include('rhodecode.apps.login')
292 config.include('rhodecode.apps.home')
292 config.include('rhodecode.apps.home')
293 config.include('rhodecode.apps.journal')
293 config.include('rhodecode.apps.journal')
294 config.include('rhodecode.apps.repository')
294 config.include('rhodecode.apps.repository')
295 config.include('rhodecode.apps.repo_group')
295 config.include('rhodecode.apps.repo_group')
296 config.include('rhodecode.apps.user_group')
296 config.include('rhodecode.apps.user_group')
297 config.include('rhodecode.apps.search')
297 config.include('rhodecode.apps.search')
298 config.include('rhodecode.apps.user_profile')
298 config.include('rhodecode.apps.user_profile')
299 config.include('rhodecode.apps.user_group_profile')
299 config.include('rhodecode.apps.user_group_profile')
300 config.include('rhodecode.apps.my_account')
300 config.include('rhodecode.apps.my_account')
301 config.include('rhodecode.apps.svn_support')
301 config.include('rhodecode.apps.svn_support')
302 config.include('rhodecode.apps.ssh_support')
302 config.include('rhodecode.apps.ssh_support')
303 config.include('rhodecode.apps.gist')
303 config.include('rhodecode.apps.gist')
304 config.include('rhodecode.apps.debug_style')
304 config.include('rhodecode.apps.debug_style')
305 config.include('rhodecode.api')
305 config.include('rhodecode.api')
306
306
307 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
307 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
308 config.add_translation_dirs('rhodecode:i18n/')
308 config.add_translation_dirs('rhodecode:i18n/')
309 settings['default_locale_name'] = settings.get('lang', 'en')
309 settings['default_locale_name'] = settings.get('lang', 'en')
310
310
311 # Add subscribers.
311 # Add subscribers.
312 if load_all:
312 if load_all:
313 config.add_subscriber(inject_app_settings,
313 config.add_subscriber(inject_app_settings,
314 pyramid.events.ApplicationCreated)
314 pyramid.events.ApplicationCreated)
315 config.add_subscriber(scan_repositories_if_enabled,
315 config.add_subscriber(scan_repositories_if_enabled,
316 pyramid.events.ApplicationCreated)
316 pyramid.events.ApplicationCreated)
317 config.add_subscriber(write_metadata_if_needed,
317 config.add_subscriber(write_metadata_if_needed,
318 pyramid.events.ApplicationCreated)
318 pyramid.events.ApplicationCreated)
319 config.add_subscriber(write_usage_data,
319 config.add_subscriber(write_usage_data,
320 pyramid.events.ApplicationCreated)
320 pyramid.events.ApplicationCreated)
321 config.add_subscriber(write_js_routes_if_enabled,
321 config.add_subscriber(write_js_routes_if_enabled,
322 pyramid.events.ApplicationCreated)
322 pyramid.events.ApplicationCreated)
323
323
324 # request custom methods
324 # request custom methods
325 config.add_request_method(
325 config.add_request_method(
326 'rhodecode.lib.partial_renderer.get_partial_renderer',
326 'rhodecode.lib.partial_renderer.get_partial_renderer',
327 'get_partial_renderer')
327 'get_partial_renderer')
328
328
329 config.add_request_method(
329 config.add_request_method(
330 'rhodecode.lib.request_counter.get_request_counter',
330 'rhodecode.lib.request_counter.get_request_counter',
331 'request_count')
331 'request_count')
332
332
333 # Set the authorization policy.
333 # Set the authorization policy.
334 authz_policy = ACLAuthorizationPolicy()
334 authz_policy = ACLAuthorizationPolicy()
335 config.set_authorization_policy(authz_policy)
335 config.set_authorization_policy(authz_policy)
336
336
337 # Set the default renderer for HTML templates to mako.
337 # Set the default renderer for HTML templates to mako.
338 config.add_mako_renderer('.html')
338 config.add_mako_renderer('.html')
339
339
340 config.add_renderer(
340 config.add_renderer(
341 name='json_ext',
341 name='json_ext',
342 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
342 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
343
343
344 config.add_renderer(
344 config.add_renderer(
345 name='string_html',
345 name='string_html',
346 factory='rhodecode.lib.string_renderer.html')
346 factory='rhodecode.lib.string_renderer.html')
347
347
348 # include RhodeCode plugins
348 # include RhodeCode plugins
349 includes = aslist(settings.get('rhodecode.includes', []))
349 includes = aslist(settings.get('rhodecode.includes', []))
350 for inc in includes:
350 for inc in includes:
351 config.include(inc)
351 config.include(inc)
352
352
353 # custom not found view, if our pyramid app doesn't know how to handle
353 # custom not found view, if our pyramid app doesn't know how to handle
354 # the request pass it to potential VCS handling ap
354 # the request pass it to potential VCS handling ap
355 config.add_notfound_view(not_found_view)
355 config.add_notfound_view(not_found_view)
356 if not settings.get('debugtoolbar.enabled', False):
356 if not settings.get('debugtoolbar.enabled', False):
357 # disabled debugtoolbar handle all exceptions via the error_handlers
357 # disabled debugtoolbar handle all exceptions via the error_handlers
358 config.add_view(error_handler, context=Exception)
358 config.add_view(error_handler, context=Exception)
359
359
360 # all errors including 403/404/50X
360 # all errors including 403/404/50X
361 config.add_view(error_handler, context=HTTPError)
361 config.add_view(error_handler, context=HTTPError)
362
362
363
363
364 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
364 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
365 """
365 """
366 Apply outer WSGI middlewares around the application.
366 Apply outer WSGI middlewares around the application.
367 """
367 """
368 registry = config.registry
368 registry = config.registry
369 settings = registry.settings
369 settings = registry.settings
370
370
371 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
371 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
372 pyramid_app = HttpsFixup(pyramid_app, settings)
372 pyramid_app = HttpsFixup(pyramid_app, settings)
373
373
374 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
374 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
375 pyramid_app, settings)
375 pyramid_app, settings)
376 registry.ae_client = _ae_client
376 registry.ae_client = _ae_client
377
377
378 if settings['gzip_responses']:
378 if settings['gzip_responses']:
379 pyramid_app = make_gzip_middleware(
379 pyramid_app = make_gzip_middleware(
380 pyramid_app, settings, compress_level=1)
380 pyramid_app, settings, compress_level=1)
381
381
382 # this should be the outer most middleware in the wsgi stack since
382 # this should be the outer most middleware in the wsgi stack since
383 # middleware like Routes make database calls
383 # middleware like Routes make database calls
384 def pyramid_app_with_cleanup(environ, start_response):
384 def pyramid_app_with_cleanup(environ, start_response):
385 try:
385 try:
386 return pyramid_app(environ, start_response)
386 return pyramid_app(environ, start_response)
387 finally:
387 finally:
388 # Dispose current database session and rollback uncommitted
388 # Dispose current database session and rollback uncommitted
389 # transactions.
389 # transactions.
390 meta.Session.remove()
390 meta.Session.remove()
391
391
392 # In a single threaded mode server, on non sqlite db we should have
392 # In a single threaded mode server, on non sqlite db we should have
393 # '0 Current Checked out connections' at the end of a request,
393 # '0 Current Checked out connections' at the end of a request,
394 # if not, then something, somewhere is leaving a connection open
394 # if not, then something, somewhere is leaving a connection open
395 pool = meta.Base.metadata.bind.engine.pool
395 pool = meta.Base.metadata.bind.engine.pool
396 log.debug('sa pool status: %s', pool.status())
396 log.debug('sa pool status: %s', pool.status())
397 log.debug('Request processing finalized')
397 log.debug('Request processing finalized')
398
398
399 return pyramid_app_with_cleanup
399 return pyramid_app_with_cleanup
400
400
401
401
402 def sanitize_settings_and_apply_defaults(global_config, settings):
402 def sanitize_settings_and_apply_defaults(global_config, settings):
403 """
403 """
404 Applies settings defaults and does all type conversion.
404 Applies settings defaults and does all type conversion.
405
405
406 We would move all settings parsing and preparation into this place, so that
406 We would move all settings parsing and preparation into this place, so that
407 we have only one place left which deals with this part. The remaining parts
407 we have only one place left which deals with this part. The remaining parts
408 of the application would start to rely fully on well prepared settings.
408 of the application would start to rely fully on well prepared settings.
409
409
410 This piece would later be split up per topic to avoid a big fat monster
410 This piece would later be split up per topic to avoid a big fat monster
411 function.
411 function.
412 """
412 """
413
413
414 settings.setdefault('rhodecode.edition', 'Community Edition')
414 settings.setdefault('rhodecode.edition', 'Community Edition')
415 settings.setdefault('rhodecode.edition_id', 'CE')
415
416
416 if 'mako.default_filters' not in settings:
417 if 'mako.default_filters' not in settings:
417 # set custom default filters if we don't have it defined
418 # set custom default filters if we don't have it defined
418 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
419 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
419 settings['mako.default_filters'] = 'h_filter'
420 settings['mako.default_filters'] = 'h_filter'
420
421
421 if 'mako.directories' not in settings:
422 if 'mako.directories' not in settings:
422 mako_directories = settings.setdefault('mako.directories', [
423 mako_directories = settings.setdefault('mako.directories', [
423 # Base templates of the original application
424 # Base templates of the original application
424 'rhodecode:templates',
425 'rhodecode:templates',
425 ])
426 ])
426 log.debug(
427 log.debug(
427 "Using the following Mako template directories: %s",
428 "Using the following Mako template directories: %s",
428 mako_directories)
429 mako_directories)
429
430
430 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
431 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
431 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
432 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
432 raw_url = settings['beaker.session.url']
433 raw_url = settings['beaker.session.url']
433 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
434 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
434 settings['beaker.session.url'] = 'redis://' + raw_url
435 settings['beaker.session.url'] = 'redis://' + raw_url
435
436
436 # Default includes, possible to change as a user
437 # Default includes, possible to change as a user
437 pyramid_includes = settings.setdefault('pyramid.includes', [])
438 pyramid_includes = settings.setdefault('pyramid.includes', [])
438 log.debug(
439 log.debug(
439 "Using the following pyramid.includes: %s",
440 "Using the following pyramid.includes: %s",
440 pyramid_includes)
441 pyramid_includes)
441
442
442 # TODO: johbo: Re-think this, usually the call to config.include
443 # TODO: johbo: Re-think this, usually the call to config.include
443 # should allow to pass in a prefix.
444 # should allow to pass in a prefix.
444 settings.setdefault('rhodecode.api.url', '/_admin/api')
445 settings.setdefault('rhodecode.api.url', '/_admin/api')
445 settings.setdefault('__file__', global_config.get('__file__'))
446 settings.setdefault('__file__', global_config.get('__file__'))
446
447
447 # Sanitize generic settings.
448 # Sanitize generic settings.
448 _list_setting(settings, 'default_encoding', 'UTF-8')
449 _list_setting(settings, 'default_encoding', 'UTF-8')
449 _bool_setting(settings, 'is_test', 'false')
450 _bool_setting(settings, 'is_test', 'false')
450 _bool_setting(settings, 'gzip_responses', 'false')
451 _bool_setting(settings, 'gzip_responses', 'false')
451
452
452 # Call split out functions that sanitize settings for each topic.
453 # Call split out functions that sanitize settings for each topic.
453 _sanitize_appenlight_settings(settings)
454 _sanitize_appenlight_settings(settings)
454 _sanitize_vcs_settings(settings)
455 _sanitize_vcs_settings(settings)
455 _sanitize_cache_settings(settings)
456 _sanitize_cache_settings(settings)
456
457
457 # configure instance id
458 # configure instance id
458 config_utils.set_instance_id(settings)
459 config_utils.set_instance_id(settings)
459
460
460 return settings
461 return settings
461
462
462
463
463 def enable_debug():
464 def enable_debug():
464 """
465 """
465 Helper to enable debug on running instance
466 Helper to enable debug on running instance
466 :return:
467 :return:
467 """
468 """
468 import tempfile
469 import tempfile
469 import textwrap
470 import textwrap
470 import logging.config
471 import logging.config
471
472
472 ini_template = textwrap.dedent("""
473 ini_template = textwrap.dedent("""
473 #####################################
474 #####################################
474 ### DEBUG LOGGING CONFIGURATION ####
475 ### DEBUG LOGGING CONFIGURATION ####
475 #####################################
476 #####################################
476 [loggers]
477 [loggers]
477 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
478 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
478
479
479 [handlers]
480 [handlers]
480 keys = console, console_sql
481 keys = console, console_sql
481
482
482 [formatters]
483 [formatters]
483 keys = generic, color_formatter, color_formatter_sql
484 keys = generic, color_formatter, color_formatter_sql
484
485
485 #############
486 #############
486 ## LOGGERS ##
487 ## LOGGERS ##
487 #############
488 #############
488 [logger_root]
489 [logger_root]
489 level = NOTSET
490 level = NOTSET
490 handlers = console
491 handlers = console
491
492
492 [logger_sqlalchemy]
493 [logger_sqlalchemy]
493 level = INFO
494 level = INFO
494 handlers = console_sql
495 handlers = console_sql
495 qualname = sqlalchemy.engine
496 qualname = sqlalchemy.engine
496 propagate = 0
497 propagate = 0
497
498
498 [logger_beaker]
499 [logger_beaker]
499 level = DEBUG
500 level = DEBUG
500 handlers =
501