##// END OF EJS Templates
configurator: reorganize the code
ergo -
Show More
@@ -1,245 +1,218 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 # App Enlight Enterprise Edition, including its added features, Support
18 # App Enlight Enterprise Edition, including its added features, Support
19 # services, and proprietary license terms, please see
19 # services, and proprietary license terms, please see
20 # https://rhodecode.com/licenses/
20 # https://rhodecode.com/licenses/
21
21
22 import datetime
22 import datetime
23 import logging
23 import logging
24 import pyelasticsearch
24 import pyelasticsearch
25 import redis
25 import redis
26 import os
26 import os
27 from pkg_resources import iter_entry_points
27 from pkg_resources import iter_entry_points
28
28
29 import appenlight.lib.jinja2_filters as jinja2_filters
29 import appenlight.lib.jinja2_filters as jinja2_filters
30 import appenlight.lib.encryption as encryption
30 import appenlight.lib.encryption as encryption
31
31
32 from pyramid.config import PHASE3_CONFIG
32 from pyramid.config import PHASE3_CONFIG
33 from pyramid.authentication import AuthTktAuthenticationPolicy
33 from pyramid.authentication import AuthTktAuthenticationPolicy
34 from pyramid.authorization import ACLAuthorizationPolicy
34 from pyramid.authorization import ACLAuthorizationPolicy
35 from pyramid_mailer.mailer import Mailer
35 from pyramid_mailer.mailer import Mailer
36 from pyramid.renderers import JSON
36 from pyramid.renderers import JSON
37 from pyramid_redis_sessions import session_factory_from_settings
37 from pyramid_redis_sessions import session_factory_from_settings
38 from pyramid.settings import asbool, aslist
38 from pyramid.settings import asbool, aslist
39 from pyramid.security import AllPermissionsList
39 from pyramid.security import AllPermissionsList
40 from pyramid_authstack import AuthenticationStackPolicy
40 from pyramid_authstack import AuthenticationStackPolicy
41 from redlock import Redlock
41 from redlock import Redlock
42 from sqlalchemy import engine_from_config
42 from sqlalchemy import engine_from_config
43
43
44 from appenlight.celery import configure_celery
44 from appenlight.celery import configure_celery
45 from appenlight.lib.configurator import CythonCompatConfigurator
45 from appenlight.lib.configurator import (CythonCompatConfigurator,
46 register_appenlight_plugin)
46 from appenlight.lib import cache_regions
47 from appenlight.lib import cache_regions
47 from appenlight.lib.ext_json import json
48 from appenlight.lib.ext_json import json
48 from appenlight.security import groupfinder, AuthTokenAuthenticationPolicy
49 from appenlight.security import groupfinder, AuthTokenAuthenticationPolicy
49
50
50 json_renderer = JSON(serializer=json.dumps, indent=4)
51 json_renderer = JSON(serializer=json.dumps, indent=4)
51
52
52 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
53
54
54
55
55 def datetime_adapter(obj, request):
56 def datetime_adapter(obj, request):
56 return obj.isoformat()
57 return obj.isoformat()
57
58
58
59
59 def all_permissions_adapter(obj, request):
60 def all_permissions_adapter(obj, request):
60 return '__all_permissions__'
61 return '__all_permissions__'
61
62
62
63
63 json_renderer.add_adapter(datetime.datetime, datetime_adapter)
64 json_renderer.add_adapter(datetime.datetime, datetime_adapter)
64 json_renderer.add_adapter(AllPermissionsList, all_permissions_adapter)
65 json_renderer.add_adapter(AllPermissionsList, all_permissions_adapter)
65
66
66
67
67 def main(global_config, **settings):
68 def main(global_config, **settings):
68 """ This function returns a Pyramid WSGI application.
69 """ This function returns a Pyramid WSGI application.
69 """
70 """
70 auth_tkt_policy = AuthTktAuthenticationPolicy(
71 auth_tkt_policy = AuthTktAuthenticationPolicy(
71 settings['authtkt.secret'],
72 settings['authtkt.secret'],
72 hashalg='sha512',
73 hashalg='sha512',
73 callback=groupfinder,
74 callback=groupfinder,
74 max_age=2592000,
75 max_age=2592000,
75 secure=asbool(settings.get('authtkt.secure', 'false')))
76 secure=asbool(settings.get('authtkt.secure', 'false')))
76 auth_token_policy = AuthTokenAuthenticationPolicy(
77 auth_token_policy = AuthTokenAuthenticationPolicy(
77 callback=groupfinder
78 callback=groupfinder
78 )
79 )
79 authorization_policy = ACLAuthorizationPolicy()
80 authorization_policy = ACLAuthorizationPolicy()
80 authentication_policy = AuthenticationStackPolicy()
81 authentication_policy = AuthenticationStackPolicy()
81 authentication_policy.add_policy('auth_tkt', auth_tkt_policy)
82 authentication_policy.add_policy('auth_tkt', auth_tkt_policy)
82 authentication_policy.add_policy('auth_token', auth_token_policy)
83 authentication_policy.add_policy('auth_token', auth_token_policy)
83 # set crypto key
84 # set crypto key
84 encryption.ENCRYPTION_SECRET = settings.get('encryption_secret')
85 encryption.ENCRYPTION_SECRET = settings.get('encryption_secret')
85 # import this later so encyption key can be monkeypatched
86 # import this later so encyption key can be monkeypatched
86 from appenlight.models import DBSession, register_datastores
87 from appenlight.models import DBSession, register_datastores
87 # update config with cometd info
88 # update config with cometd info
88 settings['cometd_servers'] = {'server': settings['cometd.server'],
89 settings['cometd_servers'] = {'server': settings['cometd.server'],
89 'secret': settings['cometd.secret']}
90 'secret': settings['cometd.secret']}
90
91
91 # Create the Pyramid Configurator.
92 # Create the Pyramid Configurator.
92 settings['_mail_url'] = settings['mailing.app_url']
93 settings['_mail_url'] = settings['mailing.app_url']
93 config = CythonCompatConfigurator(
94 config = CythonCompatConfigurator(
94 settings=settings,
95 settings=settings,
95 authentication_policy=authentication_policy,
96 authentication_policy=authentication_policy,
96 authorization_policy=authorization_policy,
97 authorization_policy=authorization_policy,
97 root_factory='appenlight.security.RootFactory',
98 root_factory='appenlight.security.RootFactory',
98 default_permission='view')
99 default_permission='view')
100 # custom registry variables
101
102 # resource type information
103 config.registry.resource_types = ['resource', 'application']
104 # plugin information
105 config.registry.appenlight_plugins = {}
106
99 config.set_default_csrf_options(require_csrf=True, header='X-XSRF-TOKEN')
107 config.set_default_csrf_options(require_csrf=True, header='X-XSRF-TOKEN')
100 config.add_view_deriver('appenlight.predicates.csrf_view',
108 config.add_view_deriver('appenlight.predicates.csrf_view',
101 name='csrf_view')
109 name='csrf_view')
102
110
103 # later, when config is available
111 # later, when config is available
104 dogpile_config = {'url': settings['redis.url'],
112 dogpile_config = {'url': settings['redis.url'],
105 "redis_expiration_time": 86400,
113 "redis_expiration_time": 86400,
106 "redis_distributed_lock": True}
114 "redis_distributed_lock": True}
107 cache_regions.regions = cache_regions.CacheRegions(dogpile_config)
115 cache_regions.regions = cache_regions.CacheRegions(dogpile_config)
108 config.registry.cache_regions = cache_regions.regions
116 config.registry.cache_regions = cache_regions.regions
109 engine = engine_from_config(settings, 'sqlalchemy.',
117 engine = engine_from_config(settings, 'sqlalchemy.',
110 json_serializer=json.dumps)
118 json_serializer=json.dumps)
111 DBSession.configure(bind=engine)
119 DBSession.configure(bind=engine)
112
120
113 # json rederer that serializes datetime
121 # json rederer that serializes datetime
114 config.add_renderer('json', json_renderer)
122 config.add_renderer('json', json_renderer)
115 config.set_request_property('appenlight.lib.request.es_conn', 'es_conn')
123 config.set_request_property('appenlight.lib.request.es_conn', 'es_conn')
116 config.set_request_property('appenlight.lib.request.get_user', 'user',
124 config.set_request_property('appenlight.lib.request.get_user', 'user',
117 reify=True)
125 reify=True)
118 config.set_request_property('appenlight.lib.request.get_csrf_token',
126 config.set_request_property('appenlight.lib.request.get_csrf_token',
119 'csrf_token', reify=True)
127 'csrf_token', reify=True)
120 config.set_request_property('appenlight.lib.request.safe_json_body',
128 config.set_request_property('appenlight.lib.request.safe_json_body',
121 'safe_json_body', reify=True)
129 'safe_json_body', reify=True)
122 config.set_request_property('appenlight.lib.request.unsafe_json_body',
130 config.set_request_property('appenlight.lib.request.unsafe_json_body',
123 'unsafe_json_body', reify=True)
131 'unsafe_json_body', reify=True)
124 config.add_request_method('appenlight.lib.request.add_flash_to_headers',
132 config.add_request_method('appenlight.lib.request.add_flash_to_headers',
125 'add_flash_to_headers')
133 'add_flash_to_headers')
126 config.add_request_method('appenlight.lib.request.get_authomatic',
134 config.add_request_method('appenlight.lib.request.get_authomatic',
127 'authomatic', reify=True)
135 'authomatic', reify=True)
128
136
129 config.include('pyramid_redis_sessions')
137 config.include('pyramid_redis_sessions')
130 config.include('pyramid_tm')
138 config.include('pyramid_tm')
131 config.include('pyramid_jinja2')
139 config.include('pyramid_jinja2')
132 config.include('appenlight_client.ext.pyramid_tween')
140 config.include('appenlight_client.ext.pyramid_tween')
133 config.include('ziggurat_foundations.ext.pyramid.sign_in')
141 config.include('ziggurat_foundations.ext.pyramid.sign_in')
134 es_server_list = aslist(settings['elasticsearch.nodes'])
142 es_server_list = aslist(settings['elasticsearch.nodes'])
135 redis_url = settings['redis.url']
143 redis_url = settings['redis.url']
136 log.warning('Elasticsearch server list: {}'.format(es_server_list))
144 log.warning('Elasticsearch server list: {}'.format(es_server_list))
137 log.warning('Redis server: {}'.format(redis_url))
145 log.warning('Redis server: {}'.format(redis_url))
138 config.registry.es_conn = pyelasticsearch.ElasticSearch(es_server_list)
146 config.registry.es_conn = pyelasticsearch.ElasticSearch(es_server_list)
139 config.registry.redis_conn = redis.StrictRedis.from_url(redis_url)
147 config.registry.redis_conn = redis.StrictRedis.from_url(redis_url)
140
148
141 config.registry.redis_lockmgr = Redlock([settings['redis.redlock.url'], ],
149 config.registry.redis_lockmgr = Redlock([settings['redis.redlock.url'], ],
142 retry_count=0, retry_delay=0)
150 retry_count=0, retry_delay=0)
143 # mailer
151 # mailer
144 config.registry.mailer = Mailer.from_settings(settings)
152 config.registry.mailer = Mailer.from_settings(settings)
145
153
146 # Configure sessions
154 # Configure sessions
147 session_factory = session_factory_from_settings(settings)
155 session_factory = session_factory_from_settings(settings)
148 config.set_session_factory(session_factory)
156 config.set_session_factory(session_factory)
149
157
150 # Configure renderers and event subscribers
158 # Configure renderers and event subscribers
151 config.add_jinja2_extension('jinja2.ext.loopcontrols')
159 config.add_jinja2_extension('jinja2.ext.loopcontrols')
152 config.add_jinja2_search_path('appenlight:templates')
160 config.add_jinja2_search_path('appenlight:templates')
153 # event subscribers
161 # event subscribers
154 config.add_subscriber("appenlight.subscribers.application_created",
162 config.add_subscriber("appenlight.subscribers.application_created",
155 "pyramid.events.ApplicationCreated")
163 "pyramid.events.ApplicationCreated")
156 config.add_subscriber("appenlight.subscribers.add_renderer_globals",
164 config.add_subscriber("appenlight.subscribers.add_renderer_globals",
157 "pyramid.events.BeforeRender")
165 "pyramid.events.BeforeRender")
158 config.add_subscriber('appenlight.subscribers.new_request',
166 config.add_subscriber('appenlight.subscribers.new_request',
159 'pyramid.events.NewRequest')
167 'pyramid.events.NewRequest')
160 config.add_view_predicate('context_type_class',
168 config.add_view_predicate('context_type_class',
161 'appenlight.predicates.contextTypeClass')
169 'appenlight.predicates.contextTypeClass')
162
170
163 register_datastores(es_conn=config.registry.es_conn,
171 register_datastores(es_conn=config.registry.es_conn,
164 redis_conn=config.registry.redis_conn,
172 redis_conn=config.registry.redis_conn,
165 redis_lockmgr=config.registry.redis_lockmgr)
173 redis_lockmgr=config.registry.redis_lockmgr)
166
174
167 # base stuff and scan
175 # base stuff and scan
168
176
169 # need to ensure webassets exists otherwise config.override_asset()
177 # need to ensure webassets exists otherwise config.override_asset()
170 # throws exception
178 # throws exception
171 if not os.path.exists(settings['webassets.dir']):
179 if not os.path.exists(settings['webassets.dir']):
172 os.mkdir(settings['webassets.dir'])
180 os.mkdir(settings['webassets.dir'])
173 config.add_static_view(path='appenlight:webassets',
181 config.add_static_view(path='appenlight:webassets',
174 name='static', cache_max_age=3600)
182 name='static', cache_max_age=3600)
175 config.override_asset(to_override='appenlight:webassets/',
183 config.override_asset(to_override='appenlight:webassets/',
176 override_with=settings['webassets.dir'])
184 override_with=settings['webassets.dir'])
177
185
178 config.include('appenlight.views')
186 config.include('appenlight.views')
179 config.include('appenlight.views.admin')
187 config.include('appenlight.views.admin')
180 config.scan(ignore=['appenlight.migrations',
188 config.scan(ignore=['appenlight.migrations', 'appenlight.scripts',
181 'appenlight.scripts',
182 'appenlight.tests'])
189 'appenlight.tests'])
183
190
184 # resource type information
185 config.registry.resource_types = ['resource', 'application']
186
187 # plugin information
188 config.registry.appenlight_plugins = {}
189
190 def register_appenlight_plugin(config, plugin_name, plugin_config):
191 def register():
192 log.warning('Registering plugin: {}'.format(plugin_name))
193 if plugin_name not in config.registry.appenlight_plugins:
194 config.registry.appenlight_plugins[plugin_name] = {
195 'javascript': None,
196 'static': None,
197 'css': None,
198 'top_nav': None,
199 'celery_tasks': None,
200 'celery_beats': None,
201 'fulltext_indexer': None,
202 'sqlalchemy_migrations': None,
203 'default_values_setter': None,
204 'resource_types': [],
205 'url_gen': None
206 }
207 config.registry.appenlight_plugins[plugin_name].update(
208 plugin_config)
209 # inform AE what kind of resource types we have available
210 # so we can avoid failing when a plugin is removed but data
211 # is still present in the db
212 if plugin_config.get('resource_types'):
213 config.registry.resource_types.extend(
214 plugin_config['resource_types'])
215
216 config.action('appenlight_plugin={}'.format(plugin_name), register)
217
218 config.add_directive('register_appenlight_plugin',
191 config.add_directive('register_appenlight_plugin',
219 register_appenlight_plugin)
192 register_appenlight_plugin)
220
193
221 for entry_point in iter_entry_points(group='appenlight.plugins'):
194 for entry_point in iter_entry_points(group='appenlight.plugins'):
222 plugin = entry_point.load()
195 plugin = entry_point.load()
223 plugin.includeme(config)
196 plugin.includeme(config)
224
197
225 # include other appenlight plugins explictly if needed
198 # include other appenlight plugins explictly if needed
226 includes = aslist(settings.get('appenlight.includes', []))
199 includes = aslist(settings.get('appenlight.includes', []))
227 for inc in includes:
200 for inc in includes:
228 config.include(inc)
201 config.include(inc)
229
202
230 # run this after everything registers in configurator
203 # run this after everything registers in configurator
231
204
232 def pre_commit():
205 def pre_commit():
233 jinja_env = config.get_jinja2_environment()
206 jinja_env = config.get_jinja2_environment()
234 jinja_env.filters['tojson'] = json.dumps
207 jinja_env.filters['tojson'] = json.dumps
235 jinja_env.filters['toJSONUnsafe'] = jinja2_filters.toJSONUnsafe
208 jinja_env.filters['toJSONUnsafe'] = jinja2_filters.toJSONUnsafe
236
209
237 config.action(None, pre_commit, order=PHASE3_CONFIG + 999)
210 config.action(None, pre_commit, order=PHASE3_CONFIG + 999)
238
211
239 def wrap_config_celery():
212 def wrap_config_celery():
240 configure_celery(config.registry)
213 configure_celery(config.registry)
241
214
242 config.action(None, wrap_config_celery, order=PHASE3_CONFIG + 999)
215 config.action(None, wrap_config_celery, order=PHASE3_CONFIG + 999)
243
216
244 app = config.make_wsgi_app()
217 app = config.make_wsgi_app()
245 return app
218 return app
@@ -1,61 +1,92 b''
1 import inspect
1 import inspect
2 import logging
2
3
3 from pyramid.config import Configurator
4 from pyramid.config import Configurator
4
5
6 log = logging.getLogger(__name__)
5
7
6 class InspectProxy(object):
8 class InspectProxy(object):
7 """
9 """
8 Proxy to the `inspect` module that allows us to use the pyramid include
10 Proxy to the `inspect` module that allows us to use the pyramid include
9 mechanism for cythonized modules without source file.
11 mechanism for cythonized modules without source file.
10 """
12 """
11
13
12 def _get_cyfunction_func_code(self, cyfunction):
14 def _get_cyfunction_func_code(self, cyfunction):
13 """
15 """
14 Unpack the `func_code` attribute of a cython function.
16 Unpack the `func_code` attribute of a cython function.
15 """
17 """
16 if inspect.ismethod(cyfunction):
18 if inspect.ismethod(cyfunction):
17 cyfunction = cyfunction.im_func
19 cyfunction = cyfunction.im_func
18 return getattr(cyfunction, 'func_code')
20 return getattr(cyfunction, 'func_code')
19
21
20 def getmodule(self, *args, **kwds):
22 def getmodule(self, *args, **kwds):
21 """
23 """
22 Simple proxy to `inspect.getmodule`.
24 Simple proxy to `inspect.getmodule`.
23 """
25 """
24 return inspect.getmodule(*args, **kwds)
26 return inspect.getmodule(*args, **kwds)
25
27
26 def getsourcefile(self, obj):
28 def getsourcefile(self, obj):
27 """
29 """
28 Proxy to `inspect.getsourcefile` or `inspect.getfile` depending on if
30 Proxy to `inspect.getsourcefile` or `inspect.getfile` depending on if
29 it's called to look up the source file that contains the magic pyramid
31 it's called to look up the source file that contains the magic pyramid
30 `includeme` callable.
32 `includeme` callable.
31
33
32 For cythonized modules the source file may be deleted. Therefore we
34 For cythonized modules the source file may be deleted. Therefore we
33 return the result of `inspect.getfile` instead. In the case of the
35 return the result of `inspect.getfile` instead. In the case of the
34 `configurator.include` method this is OK, because the result is passed
36 `configurator.include` method this is OK, because the result is passed
35 to `os.path.dirname` which strips the file name. So it doesn't matter
37 to `os.path.dirname` which strips the file name. So it doesn't matter
36 if we return the path to the source file or another file in the same
38 if we return the path to the source file or another file in the same
37 directory.
39 directory.
38 """
40 """
39 # Check if it's called to look up the source file that contains the
41 # Check if it's called to look up the source file that contains the
40 # magic pyramid `includeme` callable.
42 # magic pyramid `includeme` callable.
41 if getattr(obj, '__name__') == 'includeme':
43 if getattr(obj, '__name__') == 'includeme':
42 try:
44 try:
43 return inspect.getfile(obj)
45 return inspect.getfile(obj)
44 except TypeError as e:
46 except TypeError as e:
45 # Cython functions are not recognized as functions by the
47 # Cython functions are not recognized as functions by the
46 # inspect module. We have to unpack the func_code attribute
48 # inspect module. We have to unpack the func_code attribute
47 # ourself.
49 # ourself.
48 if 'cyfunction' in e.message:
50 if 'cyfunction' in e.message:
49 obj = self._get_cyfunction_func_code(obj)
51 obj = self._get_cyfunction_func_code(obj)
50 return inspect.getfile(obj)
52 return inspect.getfile(obj)
51 raise
53 raise
52 else:
54 else:
53 return inspect.getsourcefile(obj)
55 return inspect.getsourcefile(obj)
54
56
55
57
56 class CythonCompatConfigurator(Configurator):
58 class CythonCompatConfigurator(Configurator):
57 """
59 """
58 Customized configurator to replace the inspect class attribute with
60 Customized configurator to replace the inspect class attribute with
59 a custom one that is cython compatible.
61 a custom one that is cython compatible.
60 """
62 """
61 inspect = InspectProxy()
63 inspect = InspectProxy()
64
65
66 def register_appenlight_plugin(config, plugin_name, plugin_config):
67 def register():
68 log.warning('Registering plugin: {}'.format(plugin_name))
69 if plugin_name not in config.registry.appenlight_plugins:
70 config.registry.appenlight_plugins[plugin_name] = {
71 'javascript': None,
72 'static': None,
73 'css': None,
74 'top_nav': None,
75 'celery_tasks': None,
76 'celery_beats': None,
77 'fulltext_indexer': None,
78 'sqlalchemy_migrations': None,
79 'default_values_setter': None,
80 'resource_types': [],
81 'url_gen': None
82 }
83 config.registry.appenlight_plugins[plugin_name].update(
84 plugin_config)
85 # inform AE what kind of resource types we have available
86 # so we can avoid failing when a plugin is removed but data
87 # is still present in the db
88 if plugin_config.get('resource_types'):
89 config.registry.resource_types.extend(
90 plugin_config['resource_types'])
91
92 config.action('appenlight_plugin={}'.format(plugin_name), register)
General Comments 0
You need to be logged in to leave comments. Login now