def _populate_database( cls, settings: plaster_pastedeploy.ConfigDict, add_test_data: bool ) -> None: engine = get_engine(settings) session_factory = get_session_factory(engine) app_config = CFG(settings) print("- Populate database with default data -") with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) try: fixtures = [BaseFixture] fixtures_loader = FixturesLoader(dbsession, app_config) fixtures_loader.loads(fixtures) transaction.commit() if add_test_data: app_config.configure_filedepot() fixtures = [ContentFixture] fixtures_loader.loads(fixtures) transaction.commit() print("Database initialized.") except IntegrityError as exc: transaction.abort() print('Database initialization failed') raise DatabaseInitializationFailed( 'Warning, there was a problem when adding default data' ', it may have already been added.' ) from exc
class TracimEnv(BaseMiddleware): def __init__(self, application, config): super().__init__(application, config) self._application = application self.settings = config["tracim_settings"] self.app_config = CFG(self.settings) self.app_config.configure_filedepot() self.plugin_manager = init_plugin_manager(self.app_config) self.engine = get_engine(self.app_config) self.session_factory = get_session_factory(self.engine) def __call__(self, environ, start_response): # TODO - G.M - 18-05-2018 - This code should not create trouble # with thread and database, this should be verify. # see https://github.com/tracim/tracim_backend/issues/62 registry = get_current_registry() registry.ldap_connector = None if AuthType.LDAP in self.app_config.AUTH_TYPES: registry = self.setup_ldap(registry, self.app_config) environ["tracim_registry"] = registry tracim_context = WebdavTracimContext(environ, self.app_config, self.plugin_manager) session = create_dbsession_for_context(self.session_factory, transaction.manager, tracim_context) tracim_context.dbsession = session environ["tracim_context"] = tracim_context try: for chunk in self._application(environ, start_response): yield chunk transaction.commit() except Exception: transaction.abort() raise finally: # NOTE SGD 2020-06-30: avoid circular reference between environment dict and context. # This ensures the context will be deleted as soon as this function is exited del environ["tracim_context"] tracim_context.cleanup() def setup_ldap(self, registry: Registry, app_config: CFG): manager = ConnectionManager( uri=app_config.LDAP_URL, bind=app_config.LDAP_BIND_DN, passwd=app_config.LDAP_BIND_PASS, tls=app_config.LDAP_TLS, use_pool=app_config.LDAP_USE_POOL, pool_size=app_config.LDAP_POOL_SIZE, pool_lifetime=app_config.LDAP_POOL_LIFETIME, get_info=app_config.LDAP_GET_INFO, ) registry.ldap_login_query = _LDAPQuery( base_dn=app_config.LDAP_USER_BASE_DN, filter_tmpl=app_config.LDAP_USER_FILTER, scope=ldap3.LEVEL, attributes=ldap3.ALL_ATTRIBUTES, cache_period=0, ) registry.ldap_connector = Connector(registry, manager) return registry
def take_action(self, parsed_args: argparse.Namespace) -> None: super(DeleteDBCommand, self).take_action(parsed_args) config_uri = parsed_args.config_file # setup_logging(config_uri) settings = get_appsettings(config_uri) settings.update(settings.global_conf) if 'sqlalchemy.url' not in settings or not settings['sqlalchemy.url']: raise InvalidSettingFile('Wrong or empty sqlalchemy database url,' 'check config file') engine = get_engine(settings) app_config = CFG(settings) app_config.configure_filedepot() if parsed_args.force: print('Database deletion begin.') DeclarativeBase.metadata.drop_all(engine) print('Database deletion done.') try: print('Cleaning depot begin.') depot = DepotManager.get() depot_files = depot.list() for file_ in depot_files: depot.delete(file_) print('Cleaning depot done.') except FileNotFoundError: print( 'Warning! Can delete depots file, is depot path correctly' ' configured?' ) else: raise ForceArgumentNeeded( 'Warning, You should use --force if you really want to' ' delete database.' )
def take_action(self, parsed_args: argparse.Namespace) -> None: super(DeleteDBCommand, self).take_action(parsed_args) config_uri = parsed_args.config_file # setup_logging(config_uri) settings = get_appsettings(config_uri) settings.update(settings.global_conf) app_config = CFG(settings) app_config.configure_filedepot() engine = get_engine(app_config) if parsed_args.force: print("Database deletion begin.") DeclarativeBase.metadata.drop_all(engine) print("Database deletion done.") try: print("Cleaning depot begin.") depot = DepotManager.get() depot_files = depot.list() for file_ in depot_files: try: depot.delete(file_) # TODO - G.M - 2019-05-09 - better handling of specific exception here except Exception as exc: traceback.print_exc() print("Something goes wrong during deletion of {}".format(file_)) raise exc print("Cleaning depot done.") except FileNotFoundError: print("Warning! Can delete depots file, is depot path correctly" " configured?") else: raise ForceArgumentNeeded( "Warning, You should use --force if you really want to" " delete database." )
def initialize_config_from_environment() -> CFG: config_uri = os.environ["TRACIM_CONF_PATH"] setup_logging(config_uri) settings = get_appsettings(config_uri) settings.update(settings.global_conf) app_config = CFG(settings) app_config.configure_filedepot() return app_config
class TracimEnv(BaseMiddleware): def __init__(self, application, config): super().__init__(application, config) self._application = application self.settings = config['tracim_settings'] self.engine = get_engine(self.settings) self.session_factory = get_scoped_session_factory(self.engine) self.app_config = CFG(self.settings) self.app_config.configure_filedepot() def __call__(self, environ, start_response): # TODO - G.M - 18-05-2018 - This code should not create trouble # with thread and database, this should be verify. # see https://github.com/tracim/tracim_backend/issues/62 tm = transaction.manager session = get_tm_session(self.session_factory, tm) registry = get_current_registry() registry.ldap_connector = None if AuthType.LDAP in self.app_config.AUTH_TYPES: registry = self.setup_ldap(registry, self.app_config) environ['tracim_registry'] = registry environ['tracim_context'] = WebdavTracimContext(environ, self.app_config, session) try: app = self._application(environ, start_response) except Exception as exc: transaction.rollback() raise exc finally: transaction.commit() session.close() return app def setup_ldap(self, registry: Registry, app_config: CFG): manager = ConnectionManager( uri=app_config.LDAP_URL, bind=app_config.LDAP_BIND_DN, passwd=app_config.LDAP_BIND_PASS, tls=app_config.LDAP_TLS, use_pool=app_config.LDAP_USE_POOL, pool_size=app_config.LDAP_POOL_SIZE, pool_lifetime=app_config.LDAP_POOL_LIFETIME, get_info=app_config.LDAP_GET_INFO ) registry.ldap_login_query = _LDAPQuery( base_dn=app_config.LDAP_USER_BASE_DN, filter_tmpl=app_config.LDAP_USER_FILTER, scope=ldap3.LEVEL, attributes=ldap3.ALL_ATTRIBUTES, cache_period=0 ) registry.ldap_connector = Connector(registry, manager) return registry
class TracimEnv(BaseMiddleware): def __init__(self, application, config): super().__init__(application, config) self._application = application self.settings = config["tracim_settings"] self.app_config = CFG(self.settings) self.app_config.configure_filedepot() self.engine = get_engine(self.app_config) self.session_factory = get_scoped_session_factory(self.engine) def __call__(self, environ, start_response): # TODO - G.M - 18-05-2018 - This code should not create trouble # with thread and database, this should be verify. # see https://github.com/tracim/tracim_backend/issues/62 tm = transaction.manager session = get_tm_session(self.session_factory, tm) registry = get_current_registry() registry.ldap_connector = None if AuthType.LDAP in self.app_config.AUTH_TYPES: registry = self.setup_ldap(registry, self.app_config) environ["tracim_registry"] = registry environ["tracim_context"] = WebdavTracimContext( environ, self.app_config, session) try: app = self._application(environ, start_response) except Exception as exc: transaction.rollback() raise exc finally: transaction.commit() session.close() return app def setup_ldap(self, registry: Registry, app_config: CFG): manager = ConnectionManager( uri=app_config.LDAP_URL, bind=app_config.LDAP_BIND_DN, passwd=app_config.LDAP_BIND_PASS, tls=app_config.LDAP_TLS, use_pool=app_config.LDAP_USE_POOL, pool_size=app_config.LDAP_POOL_SIZE, pool_lifetime=app_config.LDAP_POOL_LIFETIME, get_info=app_config.LDAP_GET_INFO, ) registry.ldap_login_query = _LDAPQuery( base_dn=app_config.LDAP_USER_BASE_DN, filter_tmpl=app_config.LDAP_USER_FILTER, scope=ldap3.LEVEL, attributes=ldap3.ALL_ATTRIBUTES, cache_period=0, ) registry.ldap_connector = Connector(registry, manager) return registry
# coding=utf-8 # Runner for daemon import os from pyramid.paster import get_appsettings from pyramid.paster import setup_logging from tracim_backend.config import CFG from tracim_backend.lib.mail_fetcher.daemon import MailFetcherDaemon config_uri = os.environ['TRACIM_CONF_PATH'] setup_logging(config_uri) settings = get_appsettings(config_uri) settings.update(settings.global_conf) app_config = CFG(settings) app_config.configure_filedepot() daemon = MailFetcherDaemon(app_config, burst=False) daemon.run()
def web(global_config, **local_settings): """ This function returns a Pyramid WSGI application. """ settings = global_config settings.update(local_settings) # set CFG object app_config = CFG(settings) app_config.configure_filedepot() settings['CFG'] = app_config configurator = Configurator(settings=settings, autocommit=True) # Add AuthPolicy configurator.include("pyramid_beaker") configurator.include("pyramid_multiauth") policies = [] if app_config.REMOTE_USER_HEADER: policies.append( RemoteAuthentificationPolicy( remote_user_email_login_header=app_config.REMOTE_USER_HEADER, )) policies.append( CookieSessionAuthentificationPolicy( reissue_time=app_config.SESSION_REISSUE_TIME), # nopep8 ) if app_config.API_KEY: policies.append( ApiTokenAuthentificationPolicy( api_key_header=TRACIM_API_KEY_HEADER, api_user_email_login_header=TRACIM_API_USER_EMAIL_LOGIN_HEADER ), ) policies.append( TracimBasicAuthAuthenticationPolicy(realm=BASIC_AUTH_WEBUI_REALM), ) # Hack for ldap if AuthType.LDAP in app_config.AUTH_TYPES: import ldap3 configurator.include('pyramid_ldap3') configurator.ldap_setup(app_config.LDAP_URL, bind=app_config.LDAP_BIND_DN, passwd=app_config.LDAP_BIND_PASS, use_tls=app_config.LDAP_TLS, use_pool=app_config.LDAP_USE_POOL, pool_size=app_config.LDAP_POOL_SIZE, pool_lifetime=app_config.LDAP_POOL_LIFETIME, get_info=app_config.LDAP_GET_INFO) configurator.ldap_set_login_query( base_dn=app_config.LDAP_USER_BASE_DN, filter_tmpl=app_config.LDAP_USER_FILTER, scope=ldap3.LEVEL, attributes=ldap3.ALL_ATTRIBUTES) configurator.include(add_cors_support) # make sure to add this before other routes to intercept OPTIONS configurator.add_cors_preflight_handler() # Default authorization : Accept anything. configurator.set_authorization_policy(AcceptAllAuthorizationPolicy()) authn_policy = MultiAuthenticationPolicy(policies) configurator.set_authentication_policy(authn_policy) # INFO - GM - 11-04-2018 - set default perm # setting default perm is needed to force authentification # mecanism in all views. configurator.set_default_permission(TRACIM_DEFAULT_PERM) # Override default request configurator.set_request_factory(TracimRequest) # Pyramids "plugin" include. configurator.include('pyramid_jinja2') # Add SqlAlchemy DB configurator.include('.models.setup_models') # set Hapic context = PyramidContext( configurator=configurator, default_error_builder=ErrorSchema(), debug=app_config.DEBUG, ) hapic.set_context(context) # INFO - G.M - 2018-07-04 - global-context exceptions # Not found context.handle_exception(PageNotFound, HTTPStatus.NOT_FOUND) # Bad request context.handle_exception(WorkspaceNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) # nopep8 context.handle_exception(UserNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) # nopep8 context.handle_exception(ContentNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) # nopep8 context.handle_exception(WorkspaceNotFound, HTTPStatus.BAD_REQUEST) context.handle_exception(UserDoesNotExist, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentNotFound, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentTypeNotExist, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentInNotEditableState, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST) context.handle_exception(InvalidId, HTTPStatus.BAD_REQUEST) context.handle_exception(SameValueError, HTTPStatus.BAD_REQUEST) # Auth exception context.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED) context.handle_exception(UserAuthenticatedIsNotActive, HTTPStatus.FORBIDDEN) context.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN) context.handle_exception(InsufficientUserRoleInWorkspace, HTTPStatus.FORBIDDEN) # nopep8 context.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN) # Internal server error context.handle_exception(OperationalError, HTTPStatus.INTERNAL_SERVER_ERROR) context.handle_exception(Exception, HTTPStatus.INTERNAL_SERVER_ERROR) # Add controllers session_controller = SessionController() system_controller = SystemController() user_controller = UserController() account_controller = AccountController() reset_password_controller = ResetPasswordController() workspace_controller = WorkspaceController() comment_controller = CommentController() html_document_controller = HTMLDocumentController() thread_controller = ThreadController() file_controller = FileController() folder_controller = FolderController() configurator.include(session_controller.bind, route_prefix=BASE_API_V2) configurator.include(system_controller.bind, route_prefix=BASE_API_V2) configurator.include(user_controller.bind, route_prefix=BASE_API_V2) configurator.include(account_controller.bind, route_prefix=BASE_API_V2) configurator.include(reset_password_controller.bind, route_prefix=BASE_API_V2) # nopep8 configurator.include(workspace_controller.bind, route_prefix=BASE_API_V2) configurator.include(comment_controller.bind, route_prefix=BASE_API_V2) configurator.include(html_document_controller.bind, route_prefix=BASE_API_V2) # nopep8 configurator.include(thread_controller.bind, route_prefix=BASE_API_V2) configurator.include(file_controller.bind, route_prefix=BASE_API_V2) configurator.include(folder_controller.bind, route_prefix=BASE_API_V2) if app_config.FRONTEND_SERVE: configurator.include('pyramid_mako') frontend_controller = FrontendController( app_config.FRONTEND_DIST_FOLDER_PATH) # nopep8 configurator.include(frontend_controller.bind) hapic.add_documentation_view( '/api/v2/doc', 'Tracim v2 API', 'API of Tracim v2', ) return configurator.make_wsgi_app()
class FunctionalTest(unittest.TestCase): fixtures = [BaseFixture] config_uri = "tests_configs.ini" config_section = "functional_test" def _set_logger(self) -> None: """ Set all logger to a high level to avoid getting too much noise for tests """ logger._logger.setLevel("ERROR") logging.getLogger().setLevel("ERROR") logging.getLogger("sqlalchemy").setLevel("ERROR") logging.getLogger("txn").setLevel("ERROR") logging.getLogger("cliff").setLevel("ERROR") logging.getLogger("_jb_pytest_runner").setLevel("ERROR") def setUp(self) -> None: self._set_logger() DepotManager._clear() settings = plaster.get_settings(self.config_uri, self.config_section) self.settings = self.override_settings(settings) # INFO - G.M - 2019-03-19 - Reset all hapic context: PyramidContext # and controllers hapic.reset_context() # TODO - G.M - 2019-03-19 - Replace this code by something better, see # https://github.com/algoo/hapic/issues/144 hapic._controllers = [] self.app_config = CFG(self.settings) # type: CFG self.app_config = self.override_app_config(self.app_config) self.app_config.configure_filedepot() self.connect_database(create_tables=True) self.init_database() DepotManager._clear() self.run_app() def connect_database(self, create_tables: bool = False) -> None: self.engine = get_engine(self.app_config) if create_tables: DeclarativeBase.metadata.create_all(self.engine) self.session_factory = get_session_factory(self.engine) self.session = get_tm_session(self.session_factory, transaction.manager) def override_settings( self, settings: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: """ Allow to override some setting by code. by default : do nothing. """ return settings def override_app_config(self, app_config: CFG) -> CFG: """ Allow to override app_config parameter for tests by default : do nothing. """ return app_config def run_app(self) -> None: app = web({}, **self.settings) self.testapp = TestApp(app) def init_database(self): with transaction.manager: try: fixtures_loader = FixturesLoader(self.session, self.app_config) fixtures_loader.loads(self.fixtures) transaction.commit() logger.info(self, "Database initialized.") except IntegrityError: logger.error( self, "Warning, there was a problem when adding default data" ", it may have already been added:", ) import traceback logger.error(self, traceback.format_exc()) transaction.abort() logger.error(self, "Database initialization failed") def disconnect_database(self, remove_tables: bool = False) -> None: self.session.rollback() transaction.abort() self.session.close_all() self.engine.dispose() if remove_tables: DeclarativeBase.metadata.drop_all(self.engine) DepotManager._clear() def tearDown(self) -> None: logger.debug(self, "TearDown Test...") self.disconnect_database(remove_tables=True) testing.tearDown()
def web(global_config: OrderedDict, **local_settings) -> Router: """ This function returns a Pyramid WSGI application. """ settings = deepcopy(global_config) settings.update(local_settings) # set CFG object app_config = CFG(settings) app_config.configure_filedepot() settings["CFG"] = app_config # Init plugin manager plugin_manager = init_plugin_manager(app_config) settings["plugin_manager"] = plugin_manager configurator = Configurator(settings=settings, autocommit=True) # Add beaker session cookie tracim_setting_for_beaker = sliced_dict(settings, beginning_key_string="session.") tracim_setting_for_beaker["session.data_dir"] = app_config.SESSION__DATA_DIR tracim_setting_for_beaker["session.lock_dir"] = app_config.SESSION__LOCK_DIR tracim_setting_for_beaker["session.httponly"] = app_config.SESSION__HTTPONLY tracim_setting_for_beaker["session.secure"] = app_config.SESSION__SECURE session_factory = pyramid_beaker.session_factory_from_settings(tracim_setting_for_beaker) configurator.set_session_factory(session_factory) pyramid_beaker.set_cache_regions_from_settings(tracim_setting_for_beaker) # Add AuthPolicy configurator.include("pyramid_multiauth") policies = [] if app_config.REMOTE_USER_HEADER: policies.append( RemoteAuthentificationPolicy(remote_user_login_header=app_config.REMOTE_USER_HEADER) ) policies.append(CookieSessionAuthentificationPolicy()) policies.append(QueryTokenAuthentificationPolicy()) if app_config.API__KEY: policies.append( ApiTokenAuthentificationPolicy( api_key_header=TRACIM_API_KEY_HEADER, api_user_login_header=TRACIM_API_USER_LOGIN_HEADER, ) ) policies.append(TracimBasicAuthAuthenticationPolicy(realm=BASIC_AUTH_WEBUI_REALM)) # Hack for ldap if AuthType.LDAP in app_config.AUTH_TYPES: import ldap3 configurator.include("pyramid_ldap3") configurator.ldap_setup( app_config.LDAP_URL, bind=app_config.LDAP_BIND_DN, passwd=app_config.LDAP_BIND_PASS, use_tls=app_config.LDAP_TLS, use_pool=app_config.LDAP_USE_POOL, pool_size=app_config.LDAP_POOL_SIZE, pool_lifetime=app_config.LDAP_POOL_LIFETIME, get_info=app_config.LDAP_GET_INFO, ) configurator.ldap_set_login_query( base_dn=app_config.LDAP_USER_BASE_DN, filter_tmpl=app_config.LDAP_USER_FILTER, scope=ldap3.LEVEL, attributes=ldap3.ALL_ATTRIBUTES, ) configurator.include(add_cors_support) # make sure to add this before other routes to intercept OPTIONS configurator.add_cors_preflight_handler() # Default authorization : Accept anything. configurator.set_authorization_policy(AcceptAllAuthorizationPolicy()) authn_policy = MultiAuthenticationPolicy(policies) configurator.set_authentication_policy(authn_policy) # INFO - GM - 11-04-2018 - set default perm # setting default perm is needed to force authentification # mechanism in all views. configurator.set_default_permission(TRACIM_DEFAULT_PERM) # Override default request configurator.set_request_factory(TracimRequest) # Pyramids "plugin" include. # Add SqlAlchemy DB init_models(configurator, app_config) # set Hapic context = TracimPyramidContext( configurator=configurator, default_error_builder=ErrorSchema(), debug=app_config.DEBUG ) hapic.set_context(context) # INFO - G.M - 2018-07-04 - global-context exceptions # Not found context.handle_exception(PageNotFound, HTTPStatus.NOT_FOUND) # Bad request context.handle_exception(WorkspaceNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) context.handle_exception(UserNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) context.handle_exception(WorkspaceNotFound, HTTPStatus.BAD_REQUEST) context.handle_exception(UserDoesNotExist, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentNotFound, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentTypeNotExist, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentInNotEditableState, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST) context.handle_exception(InvalidId, HTTPStatus.BAD_REQUEST) context.handle_exception(SameValueError, HTTPStatus.BAD_REQUEST) # Auth exception context.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED) context.handle_exception(UserGivenIsNotTheSameAsAuthenticated, HTTPStatus.FORBIDDEN) context.handle_exception(UserAuthenticatedIsNotActive, HTTPStatus.FORBIDDEN) context.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN) context.handle_exception(InsufficientUserRoleInWorkspace, HTTPStatus.FORBIDDEN) context.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN) # Internal server error context.handle_exception(OperationalError, HTTPStatus.INTERNAL_SERVER_ERROR) context.handle_exception(Exception, HTTPStatus.INTERNAL_SERVER_ERROR) # Add controllers session_controller = SessionController() system_controller = SystemController() user_controller = UserController() account_controller = AccountController() reset_password_controller = ResetPasswordController() workspace_controller = WorkspaceController() comment_controller = CommentController() configurator.include(session_controller.bind, route_prefix=BASE_API) configurator.include(system_controller.bind, route_prefix=BASE_API) configurator.include(user_controller.bind, route_prefix=BASE_API) configurator.include(account_controller.bind, route_prefix=BASE_API) configurator.include(reset_password_controller.bind, route_prefix=BASE_API) configurator.include(workspace_controller.bind, route_prefix=BASE_API) configurator.include(comment_controller.bind, route_prefix=BASE_API) app_lib = ApplicationApi(app_list=app_list) for app in app_lib.get_all(): app.load_controllers( app_config=app_config, configurator=configurator, route_prefix=BASE_API, context=context ) configurator.scan("tracim_backend.lib.utils.authentification") # TODO - G.M - 2019-05-17 - check if possible to avoid this import here, # import is here because import SearchController without adding it to # pyramid make trouble in hapic which try to get view related # to controller but failed. from tracim_backend.lib.search.search_factory import SearchFactory search_controller = SearchFactory.get_search_controller(app_config) configurator.include(search_controller.bind, route_prefix=BASE_API) if app_config.FRONTEND__SERVE: configurator.include("pyramid_mako") frontend_controller = FrontendController( dist_folder_path=app_config.FRONTEND__DIST_FOLDER_PATH, custom_toolbox_folder_path=app_config.FRONTEND__CUSTOM_TOOLBOX_FOLDER_PATH, cache_token=app_config.FRONTEND__CACHE_TOKEN, ) configurator.include(frontend_controller.bind) # INFO - G.M - 2019-11-27 - Include plugin custom web code plugin_manager.hook.web_include(configurator=configurator, app_config=app_config) hapic.add_documentation_view("/api/doc", "Tracim API", "API of Tracim") return configurator.make_wsgi_app()
def take_action(self, parsed_args: argparse.Namespace) -> None: super(UpdateNamingConventionsV1ToV2Command, self).take_action(parsed_args) config_uri = parsed_args.config_file settings = get_appsettings(config_uri) settings.update(settings.global_conf) app_config = CFG(settings) app_config.configure_filedepot() engine = get_engine(app_config) inspector = reflection.Inspector.from_engine(engine) v1_unique_convention = re.compile(r"uk__(\w+)__(\w+)") v1_foreign_key_convention = re.compile(r"fk__(\w+)__(\w+)__(\w+)") v1_primary_key_convention = re.compile(r"pk__(\w+)") if not engine.dialect.name.startswith("postgresql"): raise ValueError( "This command is only supported on PostgreSQL databases") with engine.begin(): for table_name in inspector.get_table_names(): if table_name == "migrate_version": continue for unique_constraint in inspector.get_unique_constraints( table_name): match = v1_unique_convention.search( unique_constraint["name"]) if match: new_name = "uq__{}__{}".format(match.group(1), match.group(2)) engine.execute( "ALTER TABLE {} RENAME CONSTRAINT {} TO {}".format( table_name, unique_constraint["name"], new_name)) for foreign_key in inspector.get_foreign_keys(table_name): match = v1_foreign_key_convention.search( foreign_key["name"]) # special cases for content_revisions and revision_read_status if foreign_key[ "name"] == "fk__content_revisions__owner_id": new_name = "fk_content_revisions_owner_id_users" engine.execute( "ALTER TABLE {} RENAME CONSTRAINT {} TO {}".format( table_name, foreign_key["name"], new_name)) elif foreign_key[ "name"] == "revision_read_status_revision_id_fkey": new_name = "fk_revision_read_status_revision_id_content_revisions" engine.execute( "ALTER TABLE {} RENAME CONSTRAINT {} TO {}".format( table_name, foreign_key["name"], new_name)) elif foreign_key[ "name"] == "revision_read_status_user_id_fkey": new_name = "fk_revision_read_status_user_id_users" engine.execute( "ALTER TABLE {} RENAME CONSTRAINT {} TO {}".format( table_name, foreign_key["name"], new_name)) elif match: new_name = "fk_{}_{}_{}".format( match.group(1), match.group(2), match.group(3)) engine.execute( "ALTER TABLE {} RENAME CONSTRAINT {} TO {}".format( table_name, foreign_key["name"], new_name)) primary_key = inspector.get_pk_constraint(table_name) if primary_key: match = v1_primary_key_convention.search( primary_key["name"]) if primary_key["name"] == "pk__users__user_id": engine.execute( "ALTER INDEX {} RENAME TO pk_users".format( primary_key["name"])) elif primary_key[ "name"] == "pk__content_revisions__revision_id": engine.execute( "ALTER INDEX {} RENAME TO pk_content_revisions". format(primary_key["name"])) elif primary_key[ "name"] == "pk__user_workspace__user_id__workspace_id": engine.execute( "ALTER INDEX {} RENAME TO pk_user_workspace". format(primary_key["name"])) elif primary_key["name"] == "pk__workspace__workspace_id": engine.execute( "ALTER INDEX {} RENAME TO pk_workspaces".format( primary_key["name"])) elif primary_key["name"] == "revision_read_status_pkey": engine.execute( "ALTER INDEX {} RENAME TO pk_revision_read_status". format(primary_key["name"])) elif match: new_name = "pk_{}".format(match.group(1)) engine.execute("ALTER INDEX {} RENAME TO {}".format( primary_key["name"], new_name))
def web(global_config, **local_settings): """ This function returns a Pyramid WSGI application. """ settings = global_config settings.update(local_settings) # set CFG object app_config = CFG(settings) app_config.configure_filedepot() settings['CFG'] = app_config configurator = Configurator(settings=settings, autocommit=True) # Add AuthPolicy configurator.include("pyramid_beaker") configurator.include("pyramid_multiauth") policies = [] if app_config.REMOTE_USER_HEADER: policies.append( RemoteAuthentificationPolicy( remote_user_email_login_header=app_config.REMOTE_USER_HEADER, ) ) policies.append( CookieSessionAuthentificationPolicy( reissue_time=app_config.SESSION_REISSUE_TIME), # nopep8 ) if app_config.API_KEY: policies.append( ApiTokenAuthentificationPolicy( api_key_header=TRACIM_API_KEY_HEADER, api_user_email_login_header=TRACIM_API_USER_EMAIL_LOGIN_HEADER ), ) policies.append( TracimBasicAuthAuthenticationPolicy( realm=BASIC_AUTH_WEBUI_REALM ), ) # Hack for ldap if AuthType.LDAP in app_config.AUTH_TYPES: import ldap3 configurator.include('pyramid_ldap3') configurator.ldap_setup( app_config.LDAP_URL, bind=app_config.LDAP_BIND_DN, passwd=app_config.LDAP_BIND_PASS, use_tls=app_config.LDAP_TLS, use_pool=app_config.LDAP_USE_POOL, pool_size=app_config.LDAP_POOL_SIZE, pool_lifetime=app_config.LDAP_POOL_LIFETIME, get_info=app_config.LDAP_GET_INFO ) configurator.ldap_set_login_query( base_dn=app_config.LDAP_USER_BASE_DN, filter_tmpl=app_config.LDAP_USER_FILTER, scope=ldap3.LEVEL, attributes=ldap3.ALL_ATTRIBUTES ) configurator.include(add_cors_support) # make sure to add this before other routes to intercept OPTIONS configurator.add_cors_preflight_handler() # Default authorization : Accept anything. configurator.set_authorization_policy(AcceptAllAuthorizationPolicy()) authn_policy = MultiAuthenticationPolicy(policies) configurator.set_authentication_policy(authn_policy) # INFO - GM - 11-04-2018 - set default perm # setting default perm is needed to force authentification # mecanism in all views. configurator.set_default_permission(TRACIM_DEFAULT_PERM) # Override default request configurator.set_request_factory(TracimRequest) # Pyramids "plugin" include. configurator.include('pyramid_jinja2') # Add SqlAlchemy DB configurator.include('.models.setup_models') # set Hapic context = PyramidContext( configurator=configurator, default_error_builder=ErrorSchema(), debug=app_config.DEBUG, ) hapic.set_context(context) # INFO - G.M - 2018-07-04 - global-context exceptions # Not found context.handle_exception(PageNotFound, HTTPStatus.NOT_FOUND) # Bad request context.handle_exception(WorkspaceNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) # nopep8 context.handle_exception(UserNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) # nopep8 context.handle_exception(ContentNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) # nopep8 context.handle_exception(WorkspaceNotFound, HTTPStatus.BAD_REQUEST) context.handle_exception(UserDoesNotExist, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentNotFound, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentTypeNotExist, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentInNotEditableState, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST) context.handle_exception(InvalidId, HTTPStatus.BAD_REQUEST) context.handle_exception(SameValueError, HTTPStatus.BAD_REQUEST) # Auth exception context.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED) context.handle_exception(UserAuthenticatedIsNotActive, HTTPStatus.FORBIDDEN) context.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN) context.handle_exception(InsufficientUserRoleInWorkspace, HTTPStatus.FORBIDDEN) # nopep8 context.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN) # Internal server error context.handle_exception(OperationalError, HTTPStatus.INTERNAL_SERVER_ERROR) context.handle_exception(Exception, HTTPStatus.INTERNAL_SERVER_ERROR) # Add controllers session_controller = SessionController() system_controller = SystemController() user_controller = UserController() account_controller = AccountController() reset_password_controller = ResetPasswordController() workspace_controller = WorkspaceController() comment_controller = CommentController() html_document_controller = HTMLDocumentController() thread_controller = ThreadController() file_controller = FileController() folder_controller = FolderController() configurator.include(session_controller.bind, route_prefix=BASE_API_V2) configurator.include(system_controller.bind, route_prefix=BASE_API_V2) configurator.include(user_controller.bind, route_prefix=BASE_API_V2) configurator.include(account_controller.bind, route_prefix=BASE_API_V2) configurator.include(reset_password_controller.bind, route_prefix=BASE_API_V2) # nopep8 configurator.include(workspace_controller.bind, route_prefix=BASE_API_V2) configurator.include(comment_controller.bind, route_prefix=BASE_API_V2) configurator.include(html_document_controller.bind, route_prefix=BASE_API_V2) # nopep8 configurator.include(thread_controller.bind, route_prefix=BASE_API_V2) configurator.include(file_controller.bind, route_prefix=BASE_API_V2) configurator.include(folder_controller.bind, route_prefix=BASE_API_V2) if app_config.FRONTEND_SERVE: configurator.include('pyramid_mako') frontend_controller = FrontendController(app_config.FRONTEND_DIST_FOLDER_PATH) # nopep8 configurator.include(frontend_controller.bind) hapic.add_documentation_view( '/api/v2/doc', 'Tracim v2 API', 'API of Tracim v2', ) return configurator.make_wsgi_app()
class FunctionalTest(unittest.TestCase): fixtures = [BaseFixture] config_uri = 'tests_configs.ini' config_section = 'functional_test' def _set_logger(self) -> None: """ Set all logger to a high level to avoid getting too much noise for tests """ logger._logger.setLevel('ERROR') logging.getLogger().setLevel('ERROR') logging.getLogger('sqlalchemy').setLevel('ERROR') logging.getLogger('txn').setLevel('ERROR') logging.getLogger('cliff').setLevel('ERROR') logging.getLogger('_jb_pytest_runner').setLevel('ERROR') def setUp(self) -> None: self._set_logger() DepotManager._clear() settings = plaster.get_settings(self.config_uri, self.config_section) self.settings = self.override_settings(settings) hapic.reset_context() self.connect_database(create_tables=True) self.app_config = CFG(self.settings) self.app_config.configure_filedepot() self.init_database(self.settings) DepotManager._clear() self.run_app() def connect_database(self, create_tables: bool = False) -> None: self.engine = get_engine(self.settings) if create_tables: DeclarativeBase.metadata.create_all(self.engine) self.session_factory = get_session_factory(self.engine) self.session = get_tm_session(self.session_factory, transaction.manager) def override_settings( self, settings: typing.Dict[str, typing.Any] ) -> typing.Dict[str, typing.Any]: # nopep8 """ Allow to override some setting by code. by default : do nothing. """ return settings def run_app(self) -> None: app = web({}, **self.settings) self.testapp = TestApp(app) def init_database(self, settings: typing.Dict[str, typing.Any]): with transaction.manager: try: fixtures_loader = FixturesLoader(self.session, self.app_config) fixtures_loader.loads(self.fixtures) transaction.commit() logger.info(self, "Database initialized.") except IntegrityError: logger.error( self, 'Warning, there was a problem when adding default data' # nopep8 ', it may have already been added:') import traceback logger.error(self, traceback.format_exc()) transaction.abort() logger.error(self, 'Database initialization failed') def disconnect_database(self, remove_tables: bool = False) -> None: self.session.rollback() transaction.abort() self.session.close_all() self.engine.dispose() if remove_tables: DeclarativeBase.metadata.drop_all(self.engine) DepotManager._clear() def tearDown(self) -> None: logger.debug(self, 'TearDown Test...') self.disconnect_database(remove_tables=True) testing.tearDown()
def web(global_config, **local_settings): """ This function returns a Pyramid WSGI application. """ settings = deepcopy(global_config) settings.update(local_settings) # set CFG object app_config = CFG(settings) app_config.configure_filedepot() settings["CFG"] = app_config configurator = Configurator(settings=settings, autocommit=True) # Add beaker session cookie tracim_setting_for_beaker = sliced_dict(settings, beginning_key_string="session.") tracim_setting_for_beaker["session.data_dir"] = app_config.SESSION__DATA_DIR tracim_setting_for_beaker["session.lock_dir"] = app_config.SESSION__LOCK_DIR session_factory = pyramid_beaker.session_factory_from_settings(tracim_setting_for_beaker) configurator.set_session_factory(session_factory) pyramid_beaker.set_cache_regions_from_settings(tracim_setting_for_beaker) # Add AuthPolicy configurator.include("pyramid_multiauth") policies = [] if app_config.REMOTE_USER_HEADER: policies.append( RemoteAuthentificationPolicy( remote_user_email_login_header=app_config.REMOTE_USER_HEADER ) ) policies.append( CookieSessionAuthentificationPolicy(reissue_time=app_config.SESSION__REISSUE_TIME) ) if app_config.API__KEY: policies.append( ApiTokenAuthentificationPolicy( api_key_header=TRACIM_API_KEY_HEADER, api_user_email_login_header=TRACIM_API_USER_EMAIL_LOGIN_HEADER, ) ) policies.append(TracimBasicAuthAuthenticationPolicy(realm=BASIC_AUTH_WEBUI_REALM)) # Hack for ldap if AuthType.LDAP in app_config.AUTH_TYPES: import ldap3 configurator.include("pyramid_ldap3") configurator.ldap_setup( app_config.LDAP_URL, bind=app_config.LDAP_BIND_DN, passwd=app_config.LDAP_BIND_PASS, use_tls=app_config.LDAP_TLS, use_pool=app_config.LDAP_USE_POOL, pool_size=app_config.LDAP_POOL_SIZE, pool_lifetime=app_config.LDAP_POOL_LIFETIME, get_info=app_config.LDAP_GET_INFO, ) configurator.ldap_set_login_query( base_dn=app_config.LDAP_USER_BASE_DN, filter_tmpl=app_config.LDAP_USER_FILTER, scope=ldap3.LEVEL, attributes=ldap3.ALL_ATTRIBUTES, ) configurator.include(add_cors_support) # make sure to add this before other routes to intercept OPTIONS configurator.add_cors_preflight_handler() # Default authorization : Accept anything. configurator.set_authorization_policy(AcceptAllAuthorizationPolicy()) authn_policy = MultiAuthenticationPolicy(policies) configurator.set_authentication_policy(authn_policy) # INFO - GM - 11-04-2018 - set default perm # setting default perm is needed to force authentification # mecanism in all views. configurator.set_default_permission(TRACIM_DEFAULT_PERM) # Override default request configurator.set_request_factory(TracimRequest) # Pyramids "plugin" include. configurator.include("pyramid_jinja2") # Add SqlAlchemy DB init_models(configurator, app_config) # set Hapic context = PyramidContext( configurator=configurator, default_error_builder=ErrorSchema(), debug=app_config.DEBUG ) hapic.set_context(context) # INFO - G.M - 2018-07-04 - global-context exceptions # Not found context.handle_exception(PageNotFound, HTTPStatus.NOT_FOUND) # Bad request context.handle_exception(WorkspaceNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) context.handle_exception(UserNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST) context.handle_exception(WorkspaceNotFound, HTTPStatus.BAD_REQUEST) context.handle_exception(UserDoesNotExist, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentNotFound, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentTypeNotExist, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentInNotEditableState, HTTPStatus.BAD_REQUEST) context.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST) context.handle_exception(InvalidId, HTTPStatus.BAD_REQUEST) context.handle_exception(SameValueError, HTTPStatus.BAD_REQUEST) # Auth exception context.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED) context.handle_exception(UserGivenIsNotTheSameAsAuthenticated, HTTPStatus.FORBIDDEN) context.handle_exception(UserAuthenticatedIsNotActive, HTTPStatus.FORBIDDEN) context.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN) context.handle_exception(InsufficientUserRoleInWorkspace, HTTPStatus.FORBIDDEN) context.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN) # Internal server error context.handle_exception(OperationalError, HTTPStatus.INTERNAL_SERVER_ERROR) context.handle_exception(Exception, HTTPStatus.INTERNAL_SERVER_ERROR) # Add controllers session_controller = SessionController() system_controller = SystemController() user_controller = UserController() account_controller = AccountController() reset_password_controller = ResetPasswordController() workspace_controller = WorkspaceController() comment_controller = CommentController() html_document_controller = HTMLDocumentController() thread_controller = ThreadController() file_controller = FileController() folder_controller = FolderController() configurator.include(session_controller.bind, route_prefix=BASE_API_V2) configurator.include(system_controller.bind, route_prefix=BASE_API_V2) configurator.include(user_controller.bind, route_prefix=BASE_API_V2) configurator.include(account_controller.bind, route_prefix=BASE_API_V2) configurator.include(reset_password_controller.bind, route_prefix=BASE_API_V2) configurator.include(workspace_controller.bind, route_prefix=BASE_API_V2) configurator.include(comment_controller.bind, route_prefix=BASE_API_V2) configurator.include(html_document_controller.bind, route_prefix=BASE_API_V2) configurator.include(thread_controller.bind, route_prefix=BASE_API_V2) configurator.include(file_controller.bind, route_prefix=BASE_API_V2) configurator.include(folder_controller.bind, route_prefix=BASE_API_V2) if app_config.CALDAV__ENABLED: # FIXME - G.M - 2019-03-18 - check if possible to avoid this import here, # import is here because import AgendaController without adding it to # pyramid make trouble in hapic which try to get view related # to controller but failed. from tracim_backend.views.agenda_api.agenda_controller import AgendaController configurator.include(add_www_authenticate_header_for_caldav) # caldav exception context.handle_exception(CaldavNotAuthorized, HTTPStatus.FORBIDDEN) context.handle_exception(CaldavNotAuthenticated, HTTPStatus.UNAUTHORIZED) # controller radicale_proxy_controller = RadicaleProxyController( proxy_base_address=app_config.CALDAV__RADICALE_PROXY__BASE_URL, radicale_base_path=app_config.CALDAV__RADICALE__BASE_PATH, radicale_user_path=app_config.CALDAV__RADICALE__USER_PATH, radicale_workspace_path=app_config.CALDAV_RADICALE_WORKSPACE_PATH, ) agenda_controller = AgendaController() configurator.include(agenda_controller.bind, route_prefix=BASE_API_V2) configurator.include(radicale_proxy_controller.bind) if app_config.FRONTEND__SERVE: configurator.include("pyramid_mako") frontend_controller = FrontendController(app_config.FRONTEND__DIST_FOLDER_PATH) configurator.include(frontend_controller.bind) hapic.add_documentation_view("/api/v2/doc", "Tracim v2 API", "API of Tracim v2") return configurator.make_wsgi_app()