Exemple #1
0
 def find_key_from_mail_address(
     cls, mail_address: str, pattern: str, marker_str: str
 ) -> typing.Optional[str]:
     """ Parse mail_adress-like string
     to retrieve key.
     :param mail_address: mail_adress like user+key@something / key@something
     :param pattern: pattern like user+{marker_str}@something
     :param marker_str: marker_name with bracket like {content_id}
     :return: key
     """
     # splitting pattern with marker_str,
     # ex with {content_id} as marker_str
     # noreply+{content_id}@website.tld -> ['noreply+','@website.tld']
     static_parts = pattern.split(marker_str)
     assert len(static_parts) > 1
     assert len(static_parts) < 3
     if len(static_parts) == 2:
         before, after = static_parts
         if mail_address.startswith(before) and mail_address.endswith(after):
             key = mail_address.replace(before, "").replace(after, "")
             assert key.isalnum()
             return key
         logger.warning(
             cls, "pattern {} does not match email address {} ".format(pattern, mail_address)
         )
         return None
Exemple #2
0
    def execute_created_user_actions(self, user: User) -> None:
        """
        WARNING! This method will be deprecated soon, see
        https://github.com/tracim/tracim/issues/1589 and
        https://github.com/tracim/tracim/issues/1487

        This method do post-create user actions
        """

        # FIXME - G.M - 2019-03-18 - move this code to another place when
        # event mechanism is ready, see https://github.com/tracim/tracim/issues/1487
        # event on_created_user should start hook use by agenda  app code.

        app_lib = ApplicationApi(app_list=app_list)
        if app_lib.exist(AGENDA__APP_SLUG):
            agenda_api = AgendaApi(current_user=self._user,
                                   session=self._session,
                                   config=self._config)
            try:
                agenda_already_exist = agenda_api.ensure_user_agenda_exists(
                    user)
                if agenda_already_exist:
                    logger.warning(
                        self,
                        "user {} has just been created but their own agenda already exists"
                        .format(user.user_id),
                    )
            except AgendaServerConnectionError as exc:
                logger.error(self, "Cannot connect to the agenda server")
                logger.exception(self, exc)
            except Exception as exc:
                logger.error(
                    self, "Something went wrong during agenda create/update")
                logger.exception(self, exc)
Exemple #3
0
    def add_permission_to_workspace(
        self,
        workspace: Workspace,
        emails: typing.List[str],
        password: typing.Optional[str] = None,
        do_notify=False,
    ) -> typing.List[UploadPermission]:
        upload_permissions = []
        created = datetime.utcnow()
        upload_permission_group_uuid = str(uuid.uuid4().hex)
        for email in emails:
            upload_permission = UploadPermission(
                author=self._user,
                workspace_id=workspace.workspace_id,
                email=email.lower(),
                token=str(uuid.uuid4()),
                password=password,
                type=UploadPermissionType.EMAIL,
                created=created,
                upload_permission_group_uuid=upload_permission_group_uuid,
                enabled=True,
            )
            self.save(upload_permission)
            upload_permissions.append(upload_permission)
            self._session.flush()

        if do_notify:
            userlib = UserApi(config=self._config,
                              current_user=self._user,
                              session=self._session)
            workspace_lib = WorkspaceApi(config=self._config,
                                         current_user=self._user,
                                         session=self._session)
            try:
                email_manager = self._get_email_manager(
                    self._config, self._session)
                email_manager.notify_upload_permission(
                    emitter=userlib.get_user_with_context(self._user),
                    workspace_in_context=workspace_lib.
                    get_workspace_with_context(workspace),
                    upload_permission_receivers=self.
                    get_upload_permissions_in_context(upload_permissions),
                    upload_permission_password=password,
                )
            # FIXME - G.M - 2018-11-02 - hack: accept bad recipient user creation
            # this should be fixed to find a solution to allow "fake" email but
            # also have clear error case for valid mail.
            except SMTPRecipientsRefused:
                logger.warning(
                    self,
                    "Upload Permission initied by user {} but SMTP "
                    "server refuse to send notification".format(
                        self._user.login),
                )
            except SMTPException as exc:
                raise NotificationSendingFailed(
                    "Notification for Upload Permission can't be send "
                    "(SMTP error).") from exc
        return upload_permissions
Exemple #4
0
    def share_content(
        self,
        content: Content,
        emails: typing.List[str],
        password: typing.Optional[str] = None,
        do_notify: bool = False,
    ) -> typing.List[ContentShare]:
        content_shares = []
        created = datetime.now()
        share_group_uuid = str(uuid.uuid4())
        for email in emails:
            content_share = ContentShare(
                author=self._user,
                content_id=content.content_id,
                email=email.lower(),
                share_token=str(uuid.uuid4()),
                password=password,
                type=ContentShareType.EMAIL,
                created=created,
                share_group_uuid=share_group_uuid,
                enabled=True,
            )
            self.save(content_share)
            content_shares.append(content_share)
            self._session.flush()

        if do_notify:
            api = ContentApi(config=self._config,
                             session=self._session,
                             current_user=self._user)
            content_in_context = api.get_content_in_context(content)
            try:
                email_manager = self._get_email_manager(
                    self._config, self._session)
                email_manager.notify__share__content(
                    emitter=self._user,
                    shared_content=content_in_context,
                    content_share_receivers=self.get_content_shares_in_context(
                        content_shares),
                    share_password=password,
                )
            # FIXME - G.M - 2018-11-02 - hack: accept bad recipient user creation
            # this should be fixed to find a solution to allow "fake" email but
            # also have clear error case for valid mail.
            except SMTPRecipientsRefused:
                logger.warning(
                    self,
                    "Share initied by {email} but SMTP server refuse to send notification"
                    .format(email=self._user.email),
                )
            except SMTPException as exc:
                raise NotificationSendingFailed(
                    "Notification for shared file can't be send "
                    "(SMTP error).") from exc
        return content_shares
Exemple #5
0
 def create_user(
     self,
     email,
     password: str = None,
     name: str = None,
     timezone: str = '',
     lang: str= None,
     auth_type: AuthType = AuthType.UNKNOWN,
     groups=[],
     do_save: bool=True,
     do_notify: bool=True,
 ) -> User:
     if do_notify and not self._config.EMAIL_NOTIFICATION_ACTIVATED:
         raise NotificationDisabledCantCreateUserWithInvitation(
             "Can't create user with invitation mail because "
             "notification are disabled."
         )
     new_user = self.create_minimal_user(email, groups, save_now=False)
     self.update(
         user=new_user,
         name=name,
         email=email,
         auth_type=auth_type,
         password=password,
         timezone=timezone,
         lang=lang,
         do_save=False,
     )
     if do_notify:
         try:
             email_manager = get_email_manager(self._config, self._session)
             email_manager.notify_created_account(
                 new_user,
                 password=password
             )
         # FIXME - G.M - 2018-11-02 - hack: accept bad recipient user creation
         # this should be fixed to find a solution to allow "fake" email but
         # also have clear error case for valid mail.
         except SMTPRecipientsRefused as exc:
             logger.warning(
                 self,
                 "Account created for {email} but SMTP server refuse to send notification".format(  # nopep8
                     email=email
                 )
             )
         except SMTPException as exc:
             raise NotificationSendingFailed(
                 "Notification for new created account can't be send "
                 "(SMTP error), new account creation aborted"
             ) from exc
     if do_save:
         self.save(new_user)
     return new_user
Exemple #6
0
 def create_user(
     self,
     email,
     password: str = None,
     name: str = None,
     timezone: str = "",
     lang: str = None,
     auth_type: AuthType = AuthType.UNKNOWN,
     profile: typing.Optional[Profile] = None,
     allowed_space: typing.Optional[int] = None,
     do_save: bool = True,
     do_notify: bool = True,
 ) -> User:
     if do_notify and not self._config.EMAIL__NOTIFICATION__ACTIVATED:
         raise NotificationDisabledCantCreateUserWithInvitation(
             "Can't create user with invitation mail because "
             "notification are disabled.")
     new_user = self.create_minimal_user(email, profile, save_now=False)
     if allowed_space is None:
         allowed_space = self._config.LIMITATION__USER_DEFAULT_ALLOWED_SPACE
     self.update(
         user=new_user,
         name=name,
         email=email,
         auth_type=auth_type,
         password=password,
         timezone=timezone,
         allowed_space=allowed_space,
         lang=lang,
         do_save=False,
     )
     if do_notify:
         try:
             email_manager = get_email_manager(self._config, self._session)
             email_manager.notify_created_account(new_user,
                                                  password=password,
                                                  origin_user=self._user)
         # FIXME - G.M - 2018-11-02 - hack: accept bad recipient user creation
         # this should be fixed to find a solution to allow "fake" email but
         # also have clear error case for valid mail.
         except SMTPRecipientsRefused:
             logger.warning(
                 self,
                 "Account created for {email} but SMTP server refuse to send notification"
                 .format(email=email),
             )
         except SMTPException as exc:
             raise NotificationSendingFailed(
                 "Notification for new created account can't be send "
                 "(SMTP error), new account creation aborted") from exc
     if do_save:
         self.save(new_user)
     return new_user
Exemple #7
0
 def determine_requested_mode(self, request: "TracimRequest") -> DavAuthorization:
     if request.method in CALDAV_READ_METHODS:
         return DavAuthorization.READ
     elif request.method in CALDAV_WRITE_METHODS:
         return DavAuthorization.WRITE
     elif request.method in CALDAV_MANAGER_METHODS:
         return DavAuthorization.MANAGER
     else:
         logger.warning(
             self,
             'Unknown http method "{}" authorization will be MANAGER'.format(request.method),
         )
         return DavAuthorization.MANAGER
Exemple #8
0
 def get_raw_config(
     self,
     config_file_name: str,
     default_value: typing.Optional[str] = None,
     secret: bool = False,
     deprecated: bool = False,
     deprecated_extended_information: str = "",
 ) -> str:
     """
     Get config parameter according to a config name.
     Priority:
      - 1: Environement variable
      - 2: Config file data (stored in CFG.settings dict)
      - 3: default_value
     :param config_file_name: name of the config parameter name
     :param default_value: default value if not setted value found
     :param secret: is the value of the parameter secret ? (if true, it will not be printed)
     :param deprecated: is the parameter deprecated ?
     :param deprecated_extended_information: some more information about deprecated parameter
     :return:
     """
     param = ConfigParam(
         config_file_name=config_file_name,
         secret=secret,
         default_value=default_value,
         settings=self.settings,
         deprecated=deprecated,
         deprecated_extended_information=deprecated_extended_information,
     )
     self.config_info.append(param)
     logger.info(
         self,
         CONFIG_LOG_TEMPLATE.format(
             config_value=param.config_value,
             config_source=param.config_source,
             config_name=param.config_name,
             config_name_source=param.config_name_source,
         ),
     )
     if param.deprecated and param.config_value:
         logger.warning(
             self,
             "{parameter_name} parameter is deprecated. {extended_information}"
             .format(
                 parameter_name=param.config_name,
                 extended_information=param.deprecated_extended_information,
             ),
         )
     return param.real_config_value
Exemple #9
0
    def _notify_tracim(self, mails: typing.List[DecodedMail],
                       imapc: imapclient.IMAPClient) -> None:
        """
        Send http request to tracim endpoint
        :param mails: list of mails to send
        :return: none
        """
        logger.debug(self,
                     "Notify tracim about {} new responses".format(len(mails)))
        # TODO BS 20171124: Look around mail.get_from_address(), mail.get_key()
        # , mail.get_body() etc ... for raise InvalidEmailError if missing
        #  required informations (actually get_from_address raise IndexError
        #  if no from address for example) and catch it here
        while mails:
            mail = mails.pop()
            try:
                method, endpoint, json_body_dict = self._create_comment_request(
                    mail)
            except NoKeyFound:
                log = "Failed to create comment request due to missing specialkey in mail"
                logger.exception(self, log)
                continue
            except EmptyEmailBody:
                log = "Empty body, skip mail"
                logger.error(self, log)
                continue
            except AutoReplyEmailNotAllowed:
                log = "Autoreply mail, skip mail"
                logger.warning(self, log)
                continue
            except Exception:
                log = "Failed to create comment request in mail fetcher error"
                logger.exception(self, log)
                continue

            try:
                self._send_request(
                    mail=mail,
                    imapc=imapc,
                    method=method,
                    endpoint=endpoint,
                    json_body_dict=json_body_dict,
                )
            except requests.exceptions.Timeout:
                log = "Timeout error to transmit fetched mail to tracim"
                logger.exception(self, log)
            except requests.exceptions.RequestException:
                log = "Fail to transmit fetched mail to tracim"
                logger.exception(self, log)
Exemple #10
0
def _register_all(plugin_manager: pluggy.PluginManager,
                  plugins: typing.Dict[str, types.ModuleType]):
    # INFO - G.M - 2019-12-02 - sort plugins by name here to have predictable
    # order for plugin registration according to plugin_name.
    plugins = collections.OrderedDict(sorted(plugins.items()))
    for plugin_name, module in plugins.items():
        plugin_manager.register(module)
        try:
            entry_point = getattr(module, PLUGIN_ENTRY_POINT_NAME)
            entry_point(plugin_manager)
        except AttributeError:
            logger.warning(
                plugin_manager,
                "Cannot find plugin entry point '{}' in '{}' plugin".format(
                    PLUGIN_ENTRY_POINT_NAME, plugin_name),
            )
 def size(self) -> typing.Optional[int]:
     """
     :return: size of content if available, None if unavailable
     """
     if not self.revision.depot_file:
         return None
     try:
         return self.revision.depot_file.file.content_length
     except IOError:
         logger.warning(
             self, "IO Exception Occured when trying to get content size", exc_info=True
         )
     except Exception:
         logger.warning(
             self, "Unknown Exception Occured when trying to get content size", exc_info=True
         )
     return None
Exemple #12
0
 def create_user(
     self,
     email,
     password: str = None,
     name: str = None,
     timezone: str = '',
     lang: str = None,
     groups=[],
     do_save: bool = True,
     do_notify: bool = True,
 ) -> User:
     if do_notify and not self._config.EMAIL_NOTIFICATION_ACTIVATED:
         raise NotificationDisabledCantCreateUserWithInvitation(
             "Can't create user with invitation mail because "
             "notification are disabled.")
     new_user = self.create_minimal_user(email, groups, save_now=False)
     self.update(
         user=new_user,
         name=name,
         email=email,
         password=password,
         timezone=timezone,
         lang=lang,
         do_save=False,
     )
     if do_notify:
         try:
             email_manager = get_email_manager(self._config, self._session)
             email_manager.notify_created_account(new_user,
                                                  password=password)
         # FIXME - G.M - 2018-11-02 - hack: accept bad recipient user creation
         # this should be fixed to find a solution to allow "fake" email but
         # also have clear error case for valid mail.
         except SMTPRecipientsRefused as exc:
             logger.warning(
                 self,
                 "Account created for {email} but SMTP server refuse to send notification"
                 .format(  # nopep8
                     email=email))
         except SMTPException as exc:
             raise NotificationSendingFailed(
                 "Notification for new created account can't be send "
                 "(SMTP error), new account creation aborted") from exc
     if do_save:
         self.save(new_user)
     return new_user
Exemple #13
0
    def _check_email_config_validity(self) -> None:
        """
        Check if config is correctly setted for email features
        """
        if not self.EMAIL__NOTIFICATION__ACTIVATED:
            logger.warning(
                self,
                "Notification by email mecanism is disabled ! "
                "Notification and mail invitation mecanisms will not work.",
            )

        if not self.EMAIL__REPLY__LOCKFILE_PATH and self.EMAIL__REPLY__ACTIVATED:
            self.check_mandatory_param(
                "EMAIL__REPLY__LOCKFILE_PATH",
                self.EMAIL__REPLY__LOCKFILE_PATH,
                when_str="when email reply is activated",
            )
        # INFO - G.M - 2019-02-01 - check if template are available,
        # do not allow running with email_notification_activated
        # if templates needed are not available
        if self.EMAIL__NOTIFICATION__ACTIVATED:
            templates = {
                "content_update notification":
                self.EMAIL__NOTIFICATION__CONTENT_UPDATE__TEMPLATE__HTML,
                "created account":
                self.EMAIL__NOTIFICATION__CREATED_ACCOUNT__TEMPLATE__HTML,
                "password reset":
                self.
                EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__TEMPLATE__HTML,
            }
            for template_description, template_path in templates.items():
                if not template_path or not os.path.isfile(template_path):
                    raise ConfigurationError(
                        "ERROR: email template for {template_description} "
                        'not found at "{template_path}."'.format(
                            template_description=template_description,
                            template_path=template_path))

        if self.EMAIL__PROCESSING_MODE not in (self.CST.ASYNC, self.CST.SYNC):
            raise Exception("EMAIL__PROCESSING_MODE "
                            "can "
                            'be "{}" or "{}", not "{}"'.format(
                                self.CST.ASYNC, self.CST.SYNC,
                                self.EMAIL__PROCESSING_MODE))
Exemple #14
0
    def execute_created_workspace_actions(self, workspace: Workspace) -> None:
        """
        WARNING ! This method Will be Deprecated soon, see
        https://github.com/tracim/tracim/issues/1589 and
        https://github.com/tracim/tracim/issues/1487

        This method do post creation workspace actions
        """

        # FIXME - G.M - 2019-03-18 - move this code to another place when
        # event mechanism is ready, see https://github.com/tracim/tracim/issues/1487
        # event on_created_workspace should start hook use by agenda app code.

        # TODO - G.M - 2019-04-11 - Circular Import, will probably be remove
        # with event refactor, see https://github.com/tracim/tracim/issues/1487
        from tracim_backend.applications.agenda.lib import AgendaApi

        app_lib = ApplicationApi(app_list=app_list)
        if app_lib.exist(AGENDA__APP_SLUG):
            if workspace.agenda_enabled:
                agenda_api = AgendaApi(current_user=self._user,
                                       session=self._session,
                                       config=self._config)
                try:
                    agenda_already_exist = agenda_api.ensure_workspace_agenda_exists(
                        workspace)
                    if agenda_already_exist:
                        logger.warning(
                            self,
                            "workspace {} is just created but it own agenda already exist !!"
                            .format(workspace.workspace_id),
                        )
                except AgendaServerConnectionError as exc:
                    logger.error(self, "Cannot connect to agenda server")
                    logger.exception(self, exc)
                except Exception as exc:
                    logger.error(
                        self,
                        "Something goes wrong during agenda create/update")
                    logger.exception(self, exc)
Exemple #15
0
    def execute_created_user_actions(self, user: User) -> None:
        """
        WARNING ! This method Will be Deprecated soon, see
        https://github.com/tracim/tracim/issues/1589 and
        https://github.com/tracim/tracim/issues/1487

        This method do post-create user actions
        """

        # TODO - G.M - 04-04-2018 - [auth]
        # Check if this is already needed with
        # new auth system
        user.ensure_auth_token(
            validity_seconds=self._config.USER__AUTH_TOKEN__VALIDITY)

        # FIXME - G.M - 2019-03-18 - move this code to another place when
        # event mechanism is ready, see https://github.com/tracim/tracim/issues/1487
        # event on_created_user should start hook use by agenda  app code.

        app_lib = ApplicationApi(app_list=app_list)
        if app_lib.exist(AGENDA__APP_SLUG):
            agenda_api = AgendaApi(current_user=self._user,
                                   session=self._session,
                                   config=self._config)
            try:
                agenda_already_exist = agenda_api.ensure_user_agenda_exists(
                    user)
                if agenda_already_exist:
                    logger.warning(
                        self,
                        "user {} is just created but his own agenda already exist !!"
                        .format(user.user_id),
                    )
            except AgendaServerConnectionError as exc:
                logger.error(self, "Cannot connect to agenda server")
                logger.exception(self, exc)
            except Exception as exc:
                logger.error(
                    self, "Something goes wrong during agenda create/update")
                logger.exception(self, exc)
Exemple #16
0
 def size(self) -> typing.Optional[int]:
     """
     :return: size of content if available, None if unavailable
     """
     if not self.content.depot_file:
         return None
     try:
         return self.content.depot_file.file.content_length
     except IOError as e:
         logger.warning(
             self,
             "IO Exception Occured when trying to get content size  : {}".format(str(e))
         )
         logger.warning(self, traceback.format_exc())
     except Exception as e:
         logger.warning(
             self,
             "Unknown Exception Occured when trying to get content size  : {}".format(str(e))
         )
         logger.warning(self, traceback.format_exc())
     return None
Exemple #17
0
    def run(self) -> None:
        logger.info(self, "Starting MailFetcher")
        while self._is_active:
            imapc = None
            sleep_after_connection = True
            try:
                imapc = imapclient.IMAPClient(
                    self.host, self.port, ssl=self.use_ssl, timeout=MAIL_FETCHER_CONNECTION_TIMEOUT
                )
                imapc.login(self.user, self.password)

                logger.debug(self, "Select folder {}".format(self.folder))
                imapc.select_folder(self.folder)

                # force renew connection when deadline is reached
                deadline = time.time() + self.connection_max_lifetime
                while True:
                    if not self._is_active:
                        logger.warning(self, "Mail Fetcher process aborted")
                        sleep_after_connection = False
                        break

                    if time.time() > deadline:
                        logger.debug(
                            self,
                            "MailFetcher Connection Lifetime limit excess"
                            ", Try Re-new connection",
                        )
                        sleep_after_connection = False
                        break

                    # check for new mails
                    self._check_mail(imapc)

                    if self.use_idle and imapc.has_capability("IDLE"):
                        # IDLE_mode wait until event from server
                        logger.debug(self, "wail for event(IDLE)")
                        imapc.idle()
                        imapc.idle_check(timeout=MAIL_FETCHER_IDLE_RESPONSE_TIMEOUT)
                        imapc.idle_done()
                    else:
                        if self.use_idle and not imapc.has_capability("IDLE"):
                            log = (
                                "IDLE mode activated but server do not"
                                "support it, use polling instead."
                            )
                            logger.warning(self, log)

                        if self.burst:
                            self.stop()
                            break
                        # normal polling mode : sleep a define duration
                        logger.debug(self, "sleep for {}".format(self.heartbeat))
                        time.sleep(self.heartbeat)

                    if self.burst:
                        self.stop()
                        break
            # Socket
            except (socket.error, socket.gaierror, socket.herror) as e:
                log = "Socket fail with IMAP connection {}"
                logger.error(self, log.format(e.__str__()))

            except socket.timeout as e:
                log = "Socket timeout on IMAP connection {}"
                logger.error(self, log.format(e.__str__()))

            # SSL
            except ssl.SSLError as e:
                log = "SSL error on IMAP connection"
                logger.error(self, log.format(e.__str__()))

            except ssl.CertificateError as e:
                log = "SSL Certificate verification failed on IMAP connection"
                logger.error(self, log.format(e.__str__()))

            # Filelock
            except filelock.Timeout as e:
                log = "Mail Fetcher Lock Timeout {}"
                logger.warning(self, log.format(e.__str__()))

            # IMAP
            # TODO - G.M - 10-01-2017 - Support imapclient exceptions
            # when Imapclient stable will be 2.0+

            except BadIMAPFetchResponse as e:
                log = (
                    "Imap Fetch command return bad response."
                    "Is someone else connected to the mailbox ?: "
                    "{}"
                )
                logger.error(self, log.format(e.__str__()))
            # Others
            except Exception as e:
                log = "Mail Fetcher error {}"
                logger.error(self, log.format(e.__str__()))

            finally:
                # INFO - G.M - 2018-01-09 - Connection closing
                # Properly close connection according to
                # https://github.com/mjs/imapclient/pull/279/commits/043e4bd0c5c775c5a08cb5f1baa93876a46732ee
                # TODO : Use __exit__ method instead when imapclient stable will
                # be 2.0+ .
                if imapc:
                    logger.debug(self, "Try logout")
                    try:
                        imapc.logout()
                    except Exception:
                        try:
                            imapc.shutdown()
                        except Exception as e:
                            log = "Can't logout, connection broken ? {}"
                            logger.error(self, log.format(e.__str__()))

            if self.burst:
                self.stop()
                break

            if sleep_after_connection:
                logger.debug(self, "sleep for {}".format(self.heartbeat))
                time.sleep(self.heartbeat)

        log = "Mail Fetcher stopped"
        logger.debug(self, log)
Exemple #18
0
 def append_thread_callback(self, callback: typing.Callable) -> None:
     logger.warning('MailSenderDaemon not implement append_thread_callback')
     pass
Exemple #19
0
    def __init__(self, settings: typing.Dict[str, typing.Any]):
        """Parse configuration file."""

        ###
        # General
        ###
        backend_folder = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # nopep8
        tracim_v2_folder = os.path.dirname(backend_folder)
        default_color_config_file_path = os.path.join(tracim_v2_folder, 'color.json')  # nopep8
        self.COLOR_CONFIG_FILE_PATH = settings.get(
            'color.config_file_path', default_color_config_file_path
        )
        if not os.path.exists(self.COLOR_CONFIG_FILE_PATH):
            raise Exception(
                'ERROR: {} file does not exist. '
                'please create it or set color.config_file_path'
                'with a correct value'.format(self.COLOR_CONFIG_FILE_PATH)
            )

        try:
            with open(self.COLOR_CONFIG_FILE_PATH) as json_file:
                self.APPS_COLORS = json.load(json_file)
        except Exception as e:
            raise Exception(
                'Error: {} file could not be load as json'.format(self.COLOR_CONFIG_FILE_PATH)  # nopep8
            ) from e

        try:
            self.APPS_COLORS['primary']
        except KeyError as e:
            raise Exception(
                'Error: primary color is required in {} file'.format(
                    self.COLOR_CONFIG_FILE_PATH)  # nopep8
            ) from e

        default_enabled_app = [
            'contents/thread',
            'contents/file',
            'contents/html-document',
            'contents/folder',
        ]
        enabled_app = []
        enabled_app_str = settings.get('app.enabled', None)
        if enabled_app_str:
            for app in enabled_app_str.split(','):
                app_name = app.strip()
                enabled_app.append(app_name)
        else:
            enabled_app = default_enabled_app
        self.ENABLED_APP = enabled_app
        self._set_default_app(self.ENABLED_APP)
        mandatory_msg = \
            'ERROR: {} configuration is mandatory. Set it before continuing.'
        self.DEPOT_STORAGE_DIR = settings.get(
            'depot_storage_dir',
        )
        if not self.DEPOT_STORAGE_DIR:
            raise Exception(
                mandatory_msg.format('depot_storage_dir')
            )
        self.DEPOT_STORAGE_NAME = settings.get(
            'depot_storage_name',
        )
        if not self.DEPOT_STORAGE_NAME:
            raise Exception(
                mandatory_msg.format('depot_storage_name')
            )
        self.PREVIEW_CACHE_DIR = settings.get(
            'preview_cache_dir',
        )
        if not self.PREVIEW_CACHE_DIR:
            raise Exception(
                'ERROR: preview_cache_dir configuration is mandatory. '
                'Set it before continuing.'
            )
        auth_type_str = settings.get(
            'auth_types', 'internal'
        )
        self.AUTH_TYPES = [AuthType(auth.strip()) for auth in auth_type_str.split(',')]
        if AuthType.REMOTE is self.AUTH_TYPES:
            raise Exception(
                'ERROR: "remote" auth not allowed in auth_types'
                ' list, use remote_user_header instead'
            )
        self.REMOTE_USER_HEADER = settings.get('remote_user_header', None)
        # TODO - G.M - 2018-09-11 - Deprecated param
        # self.DATA_UPDATE_ALLOWED_DURATION = int(settings.get(
        #     'content.update.allowed.duration',
        #     0,
        # ))

        self.API_KEY = settings.get(
            'api.key',
            ''
        )
        self.SESSION_REISSUE_TIME = int(settings.get(
            'session.reissue_time',
            120
        ))
        self.WEBSITE_TITLE = settings.get(
            'website.title',
            'TRACIM',
        )

        # base url of the frontend
        self.WEBSITE_BASE_URL = settings.get(
            'website.base_url',
            '',
        )
        if not self.WEBSITE_BASE_URL:
            raise Exception(
                'website.base_url is needed in order to have correct path in'
                'few place like in email.'
                'You should set it with frontend root url.'
            )

        self.API_BASE_URL = settings.get(
            'api.base_url',
            self.WEBSITE_BASE_URL,
        )
        allowed_origin = []
        allowed_origin_string = settings.get(
            'cors.access-control-allowed-origin',
            ''
        )
        if allowed_origin_string:
            allowed_origin.extend(allowed_origin_string.split(','))  # nopep8
        if not allowed_origin:
            allowed_origin.append(self.WEBSITE_BASE_URL)
            if self.API_BASE_URL != self.WEBSITE_BASE_URL:
                allowed_origin.append(self.API_BASE_URL)
        self.CORS_ALLOWED_ORIGIN = allowed_origin

        # TODO - G.M - 26-03-2018 - [Cleanup] These params seems deprecated for tracimv2,  # nopep8
        # Verify this
        #
        # self.WEBSITE_HOME_TITLE_COLOR = settings.get(
        #     'website.title.color',
        #     '#555',
        # )
        # self.WEBSITE_HOME_IMAGE_PATH = settings.get(
        #     '/assets/img/home_illustration.jpg',
        # )
        # self.WEBSITE_HOME_BACKGROUND_IMAGE_PATH = settings.get(
        #     '/assets/img/bg.jpg',
        # )
        #
        website_server_name = settings.get(
            'website.server_name',
            None,
        )
        if not website_server_name:
            website_server_name = urlparse(self.WEBSITE_BASE_URL).hostname
            logger.warning(
                self,
                'NOTE: Generated website.server_name parameter from '
                'website.base_url parameter -> {0}'
                .format(website_server_name)
            )
        self.WEBSITE_SERVER_NAME = website_server_name
        # TODO - G.M - 2018-09-11 - Deprecated params
        # self.WEBSITE_HOME_TAG_LINE = settings.get(
        #     'website.home.tag_line',
        #     '',
        # )
        # self.WEBSITE_SUBTITLE = settings.get(
        #     'website.home.subtitle',
        #     '',
        # )
        # self.WEBSITE_HOME_BELOW_LOGIN_FORM = settings.get(
        #     'website.home.below_login_form',
        #     '',
        # )
        #
        # self.WEBSITE_TREEVIEW_CONTENT = settings.get(
        #     'website.treeview.content',
        # )

        self.USER_AUTH_TOKEN_VALIDITY = int(settings.get(
            'user.auth_token.validity',
            '604800',
        ))
        self.USER_RESET_PASSWORD_TOKEN_VALIDITY = int(settings.get(
            'user.reset_password.validity',
            '900'
        ))

        self.DEBUG = asbool(settings.get('debug', False))
        # TODO - G.M - 27-03-2018 - [Email] Restore email config
        ###
        # EMAIL related stuff (notification, reply)
        ##
        self.EMAIl_NOTIFICATION_ENABLED_ON_INVITATION = asbool(settings.get(
            'email.notification.enabled_on_invitation',
            True
        ))
        self.EMAIL_NOTIFICATION_NOTIFIED_EVENTS = [
            ActionDescription.COMMENT,
            ActionDescription.CREATION,
            ActionDescription.EDITION,
            ActionDescription.REVISION,
            ActionDescription.STATUS_UPDATE
        ]

        self.EMAIL_NOTIFICATION_NOTIFIED_CONTENTS = [
            content_type_list.Page.slug,
            content_type_list.Thread.slug,
            content_type_list.File.slug,
            content_type_list.Comment.slug,
            # content_type_list.Folder.slug -- Folder is skipped
        ]
        if settings.get('email.notification.from'):
            raise Exception(
                'email.notification.from configuration is deprecated. '
                'Use instead email.notification.from.email and '
                'email.notification.from.default_label.'
            )
        self.EMAIL_NOTIFICATION_FROM_EMAIL = settings.get(
            'email.notification.from.email',
            'noreply+{user_id}@trac.im'
        )
        self.EMAIL_NOTIFICATION_FROM_DEFAULT_LABEL = settings.get(
            'email.notification.from.default_label',
            'Tracim Notifications'
        )
        self.EMAIL_NOTIFICATION_REPLY_TO_EMAIL = settings.get(
            'email.notification.reply_to.email',
        )
        self.EMAIL_NOTIFICATION_REFERENCES_EMAIL = settings.get(
            'email.notification.references.email'
        )
        # Content update notification

        self.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML = settings.get(
            'email.notification.content_update.template.html',
        )

        self.EMAIL_NOTIFICATION_CONTENT_UPDATE_SUBJECT = settings.get(
            'email.notification.content_update.subject',
            _("[{website_title}] [{workspace_label}] {content_label} ({content_status_label})")  # nopep8
        )
        # Created account notification
        self.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML = settings.get(
            'email.notification.created_account.template.html',
        )
        self.EMAIL_NOTIFICATION_CREATED_ACCOUNT_SUBJECT = settings.get(
            'email.notification.created_account.subject',
            _('[{website_title}] Someone created an account for you'),
        )

        # Reset password notification
        self.EMAIL_NOTIFICATION_RESET_PASSWORD_TEMPLATE_HTML = settings.get(
            'email.notification.reset_password_request.template.html',
        )
        self.EMAIL_NOTIFICATION_RESET_PASSWORD_SUBJECT = settings.get(
            'email.notification.reset_password_request.subject',
            _('[{website_title}] A password reset has been requested'),
        )

        self.EMAIL_NOTIFICATION_PROCESSING_MODE = settings.get(
            'email.notification.processing_mode',
        )
        self.EMAIL_NOTIFICATION_ACTIVATED = asbool(settings.get(
            'email.notification.activated',
        ))
        if not self.EMAIL_NOTIFICATION_ACTIVATED:
            logger.warning(
                self,
                'Notification by email mecanism is disabled ! '
                'Notification and mail invitation mecanisms will not work.'
            )

        # INFO - G.M - 2019-02-01 - check if template are available,
        # do not allow running with email_notification_activated
        # if templates needed are not available
        if self.EMAIL_NOTIFICATION_ACTIVATED:
            templates = {
                'content_update notification': self.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML,
                'created account': self.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML,
                'password reset': self.EMAIL_NOTIFICATION_RESET_PASSWORD_TEMPLATE_HTML
            }
            for template_description, template_path in templates.items():
                if not template_path or not os.path.isfile(template_path):
                    raise ConfigurationError(
                        'ERROR: email template for {template_description} '
                        'not found at "{template_path}."'.format(
                            template_description=template_description,
                            template_path=template_path
                        )
                    )

        self.EMAIL_NOTIFICATION_SMTP_SERVER = settings.get(
            'email.notification.smtp.server',
        )
        self.EMAIL_NOTIFICATION_SMTP_PORT = settings.get(
            'email.notification.smtp.port',
        )
        self.EMAIL_NOTIFICATION_SMTP_USER = settings.get(
            'email.notification.smtp.user',
        )
        self.EMAIL_NOTIFICATION_SMTP_PASSWORD = settings.get(
            'email.notification.smtp.password',
        )

        self.EMAIL_REPLY_ACTIVATED = asbool(settings.get(
            'email.reply.activated',
            False,
        ))

        self.EMAIL_REPLY_IMAP_SERVER = settings.get(
            'email.reply.imap.server',
        )
        self.EMAIL_REPLY_IMAP_PORT = settings.get(
            'email.reply.imap.port',
        )
        self.EMAIL_REPLY_IMAP_USER = settings.get(
            'email.reply.imap.user',
        )
        self.EMAIL_REPLY_IMAP_PASSWORD = settings.get(
            'email.reply.imap.password',
        )
        self.EMAIL_REPLY_IMAP_FOLDER = settings.get(
            'email.reply.imap.folder',
        )
        self.EMAIL_REPLY_CHECK_HEARTBEAT = int(settings.get(
            'email.reply.check.heartbeat',
            60,
        ))
        self.EMAIL_REPLY_IMAP_USE_SSL = asbool(settings.get(
            'email.reply.imap.use_ssl',
        ))
        self.EMAIL_REPLY_IMAP_USE_IDLE = asbool(settings.get(
            'email.reply.imap.use_idle',
            True,
        ))
        self.EMAIL_REPLY_CONNECTION_MAX_LIFETIME = int(settings.get(
            'email.reply.connection.max_lifetime',
            600,  # 10 minutes
        ))
        self.EMAIL_REPLY_USE_HTML_PARSING = asbool(settings.get(
            'email.reply.use_html_parsing',
            True,
        ))
        self.EMAIL_REPLY_USE_TXT_PARSING = asbool(settings.get(
            'email.reply.use_txt_parsing',
            True,
        ))
        self.EMAIL_REPLY_LOCKFILE_PATH = settings.get(
            'email.reply.lockfile_path',
            ''
        )
        if not self.EMAIL_REPLY_LOCKFILE_PATH and self.EMAIL_REPLY_ACTIVATED:
            raise Exception(
                mandatory_msg.format('email.reply.lockfile_path')
            )

        self.EMAIL_PROCESSING_MODE = settings.get(
            'email.processing_mode',
            'sync',
        ).upper()

        if self.EMAIL_PROCESSING_MODE not in (
                self.CST.ASYNC,
                self.CST.SYNC,
        ):
            raise Exception(
                'email.processing_mode '
                'can ''be "{}" or "{}", not "{}"'.format(
                    self.CST.ASYNC,
                    self.CST.SYNC,
                    self.EMAIL_PROCESSING_MODE,
                )
            )

        self.EMAIL_SENDER_REDIS_HOST = settings.get(
            'email.async.redis.host',
            'localhost',
        )
        self.EMAIL_SENDER_REDIS_PORT = int(settings.get(
            'email.async.redis.port',
            6379,
        ))
        self.EMAIL_SENDER_REDIS_DB = int(settings.get(
            'email.async.redis.db',
            0,
        ))
        self.NEW_USER_INVITATION_DO_NOTIFY = asbool(settings.get(
            'new_user.invitation.do_notify',
            'True'
        ))

        self.NEW_USER_INVITATION_MINIMAL_PROFILE = settings.get(
            'new_user.invitation.minimal_profile',
            Group.TIM_MANAGER_GROUPNAME
        )
        ###
        # WSGIDAV (Webdav server)
        ###
        tracim_website = 'http://tracim.fr/'
        tracim_name = 'Tracim'
        wsgidav_website = 'https://github.com/mar10/wsgidav/'
        wsgidav_name = 'WsgiDAV'

        self.WEBDAV_VERBOSE_LEVEL = int(settings.get('webdav.verbose.level', 1))
        self.WEBDAV_ROOT_PATH = settings.get('webdav.root_path', '/')
        self.WEBDAV_BLOCK_SIZE = int(settings.get('webdav.block_size', 8192))
        self.WEBDAV_DIR_BROWSER_ENABLED = asbool(settings.get('webdav.dir_browser.enabled', True))
        default_webdav_footnote = '<a href="{instance_url}">{instance_name}</a>.' \
                                  ' This Webdav is serve by'  \
                                  ' <a href="{tracim_website}">{tracim_name} software</a> using' \
                                  ' <a href="{wsgidav_website}">{wsgidav_name}</a>.'.format(
                                      instance_name=self.WEBSITE_TITLE,
                                      instance_url=self.WEBSITE_BASE_URL,
                                      tracim_name=tracim_name,
                                      tracim_website=tracim_website,
                                      wsgidav_name=wsgidav_name,
                                      wsgidav_website=wsgidav_website,
                                  )
        self.WEBDAV_DIR_BROWSER_FOOTER = settings.get('webdav.dir_browser.footer', default_webdav_footnote)
        # TODO : check if tweaking those param does work
        self.WEBDAV_SHOW_DELETED = False
        self.WEBDAV_SHOW_ARCHIVED = False
        self.WEBDAV_SHOW_HISTORY = False
        self.WEBDAV_MANAGE_LOCK = True
        # TODO - G.M - 27-03-2018 - [Caldav] Restore radicale config
        ###
        # RADICALE (Caldav server)
        ###
        # self.RADICALE_SERVER_HOST = settings.get(
        #     'radicale.server.host',
        #     '127.0.0.1',
        # )
        # self.RADICALE_SERVER_PORT = int(settings.get(
        #     'radicale.server.port',
        #     5232,
        # ))
        # # Note: Other parameters needed to work in SSL (cert file, etc)
        # self.RADICALE_SERVER_SSL = asbool(settings.get(
        #     'radicale.server.ssl',
        #     False,
        # ))
        # self.RADICALE_SERVER_FILE_SYSTEM_FOLDER = settings.get(
        #     'radicale.server.filesystem.folder',
        # )
        # if not self.RADICALE_SERVER_FILE_SYSTEM_FOLDER:
        #     raise Exception(
        #         mandatory_msg.format('radicale.server.filesystem.folder')
        #     )
        # self.RADICALE_SERVER_ALLOW_ORIGIN = settings.get(
        #     'radicale.server.allow_origin',
        #     None,
        # )
        # if not self.RADICALE_SERVER_ALLOW_ORIGIN:
        #     self.RADICALE_SERVER_ALLOW_ORIGIN = self.WEBSITE_BASE_URL
        #     logger.warning(self,
        #         'NOTE: Generated radicale.server.allow_origin parameter with '
        #         'followings parameters: website.base_url ({0})'
        #         .format(self.WEBSITE_BASE_URL)
        #     )
        #
        # self.RADICALE_SERVER_REALM_MESSAGE = settings.get(
        #     'radicale.server.realm_message',
        #     'Tracim Calendar - Password Required',
        # )
        #
        # self.RADICALE_CLIENT_BASE_URL_HOST = settings.get(
        #     'radicale.client.base_url.host',
        #     'http://{}:{}'.format(
        #         self.RADICALE_SERVER_HOST,
        #         self.RADICALE_SERVER_PORT,
        #     ),
        # )
        #
        # self.RADICALE_CLIENT_BASE_URL_PREFIX = settings.get(
        #     'radicale.client.base_url.prefix',
        #     '/',
        # )
        # # Ensure finished by '/'
        # if '/' != self.RADICALE_CLIENT_BASE_URL_PREFIX[-1]:
        #     self.RADICALE_CLIENT_BASE_URL_PREFIX += '/'
        # if '/' != self.RADICALE_CLIENT_BASE_URL_PREFIX[0]:
        #     self.RADICALE_CLIENT_BASE_URL_PREFIX \
        #         = '/' + self.RADICALE_CLIENT_BASE_URL_PREFIX
        #
        # if not self.RADICALE_CLIENT_BASE_URL_HOST:
        #     logger.warning(self,
        #         'Generated radicale.client.base_url.host parameter with '
        #         'followings parameters: website.server_name -> {}'
        #         .format(self.WEBSITE_SERVER_NAME)
        #     )
        #     self.RADICALE_CLIENT_BASE_URL_HOST = self.WEBSITE_SERVER_NAME
        #
        # self.RADICALE_CLIENT_BASE_URL_TEMPLATE = '{}{}'.format(
        #     self.RADICALE_CLIENT_BASE_URL_HOST,
        #     self.RADICALE_CLIENT_BASE_URL_PREFIX,
        # )
        self.PREVIEW_JPG_RESTRICTED_DIMS = asbool(settings.get(
            'preview.jpg.restricted_dims', False
        ))
        preview_jpg_allowed_dims_str = settings.get('preview.jpg.allowed_dims', '')  # nopep8
        allowed_dims = []
        if preview_jpg_allowed_dims_str:
            for sizes in preview_jpg_allowed_dims_str.split(','):
                parts = sizes.split('x')
                assert len(parts) == 2
                width, height = parts
                assert width.isdecimal()
                assert height.isdecimal()
                size = PreviewDim(int(width), int(height))
                allowed_dims.append(size)

        if not allowed_dims:
            size = PreviewDim(256, 256)
            allowed_dims.append(size)

        self.PREVIEW_JPG_ALLOWED_DIMS = allowed_dims

        self.FRONTEND_SERVE = asbool(settings.get(
            'frontend.serve', False
        ))
        # INFO - G.M - 2018-08-06 - we pretend that frontend_dist_folder
        # is probably in frontend subfolder
        # of tracim_v2 parent of both backend and frontend
        backend_folder = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # nopep8
        tracim_v2_folder = os.path.dirname(backend_folder)
        backend_i18n_folder = os.path.join(backend_folder, 'tracim_backend', 'locale')  # nopep8

        self.BACKEND_I18N_FOLDER = settings.get(
            'backend.18n_folder_path', backend_i18n_folder
        )
        if not os.path.isdir(self.BACKEND_I18N_FOLDER):
            raise Exception(
                'ERROR: {} folder does not exist as folder. '
                'please set backend.i8n_folder_path'
                'with a correct value'.format(self.BACKEND_I18N_FOLDER)
            )

        frontend_dist_folder = os.path.join(tracim_v2_folder, 'frontend', 'dist')  # nopep8
        self.FRONTEND_DIST_FOLDER_PATH = settings.get(
            'frontend.dist_folder_path', frontend_dist_folder
        )

        # INFO - G.M - 2018-08-06 - We check dist folder existence
        if self.FRONTEND_SERVE and not os.path.isdir(self.FRONTEND_DIST_FOLDER_PATH):  # nopep8
            raise Exception(
                'ERROR: {} folder does not exist as folder. '
                'please set frontend.dist_folder.path'
                'with a correct value'.format(self.FRONTEND_DIST_FOLDER_PATH)
            )
        self.load_ldap_settings(settings)
Exemple #20
0
 def append_thread_callback(self, callback: typing.Callable) -> None:
     logger.warning(
         "MailFetcherrDaemon not implement append_thread_callback")
     pass
Exemple #21
0
    def __init__(self, settings):
        """Parse configuration file."""

        ###
        # General
        ###
        backend_folder = os.path.dirname(
            os.path.dirname(os.path.abspath(__file__)))  # nopep8
        tracim_v2_folder = os.path.dirname(backend_folder)
        default_color_config_file_path = os.path.join(tracim_v2_folder,
                                                      'color.json')  # nopep8
        self.COLOR_CONFIG_FILE_PATH = settings.get(
            'color.config_file_path', default_color_config_file_path)
        if not os.path.exists(self.COLOR_CONFIG_FILE_PATH):
            raise Exception('ERROR: {} file does not exist. '
                            'please create it or set color.config_file_path'
                            'with a correct value'.format(
                                self.COLOR_CONFIG_FILE_PATH))

        try:
            with open(self.COLOR_CONFIG_FILE_PATH) as json_file:
                self.APPS_COLORS = json.load(json_file)
        except Exception as e:
            raise Exception('Error: {} file could not be load as json'.format(
                self.COLOR_CONFIG_FILE_PATH)  # nopep8
                            ) from e

        try:
            self.APPS_COLORS['primary']
        except KeyError as e:
            raise Exception(
                'Error: primary color is required in {} file'.format(
                    self.COLOR_CONFIG_FILE_PATH)  # nopep8
            ) from e

        default_enabled_app = [
            'contents/thread',
            'contents/file',
            'contents/html-document',
        ]
        enabled_app = []
        enabled_app_str = settings.get('app.enabled', None)
        if enabled_app_str:
            for app in enabled_app_str.split(','):
                app_name = app.strip()
                enabled_app.append(app_name)
        else:
            enabled_app = default_enabled_app
        self.ENABLED_APP = enabled_app
        self._set_default_app(self.ENABLED_APP)
        mandatory_msg = \
            'ERROR: {} configuration is mandatory. Set it before continuing.'
        self.DEPOT_STORAGE_DIR = settings.get('depot_storage_dir', )
        if not self.DEPOT_STORAGE_DIR:
            raise Exception(mandatory_msg.format('depot_storage_dir'))
        self.DEPOT_STORAGE_NAME = settings.get('depot_storage_name', )
        if not self.DEPOT_STORAGE_NAME:
            raise Exception(mandatory_msg.format('depot_storage_name'))
        self.PREVIEW_CACHE_DIR = settings.get('preview_cache_dir', )
        if not self.PREVIEW_CACHE_DIR:
            raise Exception(
                'ERROR: preview_cache_dir configuration is mandatory. '
                'Set it before continuing.')

        # TODO - G.M - 2018-09-11 - Deprecated param
        # self.DATA_UPDATE_ALLOWED_DURATION = int(settings.get(
        #     'content.update.allowed.duration',
        #     0,
        # ))

        self.API_KEY = settings.get('api.key', '')
        self.SESSION_REISSUE_TIME = int(
            settings.get('session.reissue_time', 120))
        self.WEBSITE_TITLE = settings.get(
            'website.title',
            'TRACIM',
        )

        # base url of the frontend
        self.WEBSITE_BASE_URL = settings.get(
            'website.base_url',
            '',
        )
        if not self.WEBSITE_BASE_URL:
            raise Exception(
                'website.base_url is needed in order to have correct path in'
                'few place like in email.'
                'You should set it with frontend root url.')

        self.API_BASE_URL = settings.get(
            'api.base_url',
            self.WEBSITE_BASE_URL,
        )
        allowed_origin = []
        allowed_origin_string = settings.get(
            'cors.access-control-allowed-origin', '')
        if allowed_origin_string:
            allowed_origin.extend(allowed_origin_string.split(','))  # nopep8
        if not allowed_origin:
            allowed_origin.append(self.WEBSITE_BASE_URL)
            if self.API_BASE_URL != self.WEBSITE_BASE_URL:
                allowed_origin.append(self.API_BASE_URL)
        self.CORS_ALLOWED_ORIGIN = allowed_origin

        # TODO - G.M - 26-03-2018 - [Cleanup] These params seems deprecated for tracimv2,  # nopep8
        # Verify this
        #
        # self.WEBSITE_HOME_TITLE_COLOR = settings.get(
        #     'website.title.color',
        #     '#555',
        # )
        # self.WEBSITE_HOME_IMAGE_PATH = settings.get(
        #     '/assets/img/home_illustration.jpg',
        # )
        # self.WEBSITE_HOME_BACKGROUND_IMAGE_PATH = settings.get(
        #     '/assets/img/bg.jpg',
        # )
        #
        website_server_name = settings.get(
            'website.server_name',
            None,
        )
        if not website_server_name:
            website_server_name = urlparse(self.WEBSITE_BASE_URL).hostname
            logger.warning(
                self, 'NOTE: Generated website.server_name parameter from '
                'website.base_url parameter -> {0}'.format(
                    website_server_name))
        self.WEBSITE_SERVER_NAME = website_server_name
        # TODO - G.M - 2018-09-11 - Deprecated params
        # self.WEBSITE_HOME_TAG_LINE = settings.get(
        #     'website.home.tag_line',
        #     '',
        # )
        # self.WEBSITE_SUBTITLE = settings.get(
        #     'website.home.subtitle',
        #     '',
        # )
        # self.WEBSITE_HOME_BELOW_LOGIN_FORM = settings.get(
        #     'website.home.below_login_form',
        #     '',
        # )
        #
        # self.WEBSITE_TREEVIEW_CONTENT = settings.get(
        #     'website.treeview.content',
        # )

        self.USER_AUTH_TOKEN_VALIDITY = int(
            settings.get(
                'user.auth_token.validity',
                '604800',
            ))
        self.USER_RESET_PASSWORD_TOKEN_VALIDITY = int(
            settings.get('user.reset_password.validity', '900'))

        self.DEBUG = asbool(settings.get('debug', False))
        # TODO - G.M - 27-03-2018 - [Email] Restore email config
        ###
        # EMAIL related stuff (notification, reply)
        ##

        self.EMAIL_NOTIFICATION_NOTIFIED_EVENTS = [
            ActionDescription.COMMENT, ActionDescription.CREATION,
            ActionDescription.EDITION, ActionDescription.REVISION,
            ActionDescription.STATUS_UPDATE
        ]

        self.EMAIL_NOTIFICATION_NOTIFIED_CONTENTS = [
            content_type_list.Page.slug,
            content_type_list.Thread.slug,
            content_type_list.File.slug,
            content_type_list.Comment.slug,
            # content_type_list.Folder.slug -- Folder is skipped
        ]
        if settings.get('email.notification.from'):
            raise Exception(
                'email.notification.from configuration is deprecated. '
                'Use instead email.notification.from.email and '
                'email.notification.from.default_label.')
        self.EMAIL_NOTIFICATION_FROM_EMAIL = settings.get(
            'email.notification.from.email', 'noreply+{user_id}@trac.im')
        self.EMAIL_NOTIFICATION_FROM_DEFAULT_LABEL = settings.get(
            'email.notification.from.default_label', 'Tracim Notifications')
        self.EMAIL_NOTIFICATION_REPLY_TO_EMAIL = settings.get(
            'email.notification.reply_to.email', )
        self.EMAIL_NOTIFICATION_REFERENCES_EMAIL = settings.get(
            'email.notification.references.email')
        # Content update notification
        self.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML = settings.get(
            'email.notification.content_update.template.html', )
        self.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_TEXT = settings.get(
            'email.notification.content_update.template.text', )
        self.EMAIL_NOTIFICATION_CONTENT_UPDATE_SUBJECT = settings.get(
            'email.notification.content_update.subject', )
        # Created account notification
        self.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML = settings.get(
            'email.notification.created_account.template.html',
            './tracim_backend/templates/mail/created_account_body_html.mak',
        )
        self.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_TEXT = settings.get(
            'email.notification.created_account.template.text',
            './tracim_backend/templates/mail/created_account_body_text.mak',
        )
        self.EMAIL_NOTIFICATION_CREATED_ACCOUNT_SUBJECT = settings.get(
            'email.notification.created_account.subject',
            '[{website_title}] Created account',
        )

        # Reset password notification
        self.EMAIL_NOTIFICATION_RESET_PASSWORD_TEMPLATE_HTML = settings.get(
            'email.notification.reset_password_request.template.html',
            './tracim_backend/templates/mail/reset_password_body_html.mak',
        )
        self.EMAIL_NOTIFICATION_RESET_PASSWORD_TEMPLATE_TEXT = settings.get(
            'email.notification.reset_password_request.template.text',
            './tracim_backend/templates/mail/reset_password_body_text.mak',
        )
        self.EMAIL_NOTIFICATION_RESET_PASSWORD_SUBJECT = settings.get(
            'email.notification.reset_password_request.subject',
            '[{website_title}] Reset Password Request')

        self.EMAIL_NOTIFICATION_PROCESSING_MODE = settings.get(
            'email.notification.processing_mode', )

        self.EMAIL_NOTIFICATION_ACTIVATED = asbool(
            settings.get('email.notification.activated', ))
        if not self.EMAIL_NOTIFICATION_ACTIVATED:
            logger.warning(
                self, 'Notification by email mecanism is disabled ! '
                'Notification and mail invitation mecanisms will not work.')
        self.EMAIL_NOTIFICATION_SMTP_SERVER = settings.get(
            'email.notification.smtp.server', )
        self.EMAIL_NOTIFICATION_SMTP_PORT = settings.get(
            'email.notification.smtp.port', )
        self.EMAIL_NOTIFICATION_SMTP_USER = settings.get(
            'email.notification.smtp.user', )
        self.EMAIL_NOTIFICATION_SMTP_PASSWORD = settings.get(
            'email.notification.smtp.password', )
        self.EMAIL_NOTIFICATION_LOG_FILE_PATH = settings.get(
            'email.notification.log_file_path',
            None,
        )

        self.EMAIL_REPLY_ACTIVATED = asbool(
            settings.get(
                'email.reply.activated',
                False,
            ))

        self.EMAIL_REPLY_IMAP_SERVER = settings.get(
            'email.reply.imap.server', )
        self.EMAIL_REPLY_IMAP_PORT = settings.get('email.reply.imap.port', )
        self.EMAIL_REPLY_IMAP_USER = settings.get('email.reply.imap.user', )
        self.EMAIL_REPLY_IMAP_PASSWORD = settings.get(
            'email.reply.imap.password', )
        self.EMAIL_REPLY_IMAP_FOLDER = settings.get(
            'email.reply.imap.folder', )
        self.EMAIL_REPLY_CHECK_HEARTBEAT = int(
            settings.get(
                'email.reply.check.heartbeat',
                60,
            ))
        self.EMAIL_REPLY_TOKEN = settings.get('email.reply.token', )
        self.EMAIL_REPLY_IMAP_USE_SSL = asbool(
            settings.get('email.reply.imap.use_ssl', ))
        self.EMAIL_REPLY_IMAP_USE_IDLE = asbool(
            settings.get(
                'email.reply.imap.use_idle',
                True,
            ))
        self.EMAIL_REPLY_CONNECTION_MAX_LIFETIME = int(
            settings.get(
                'email.reply.connection.max_lifetime',
                600,  # 10 minutes
            ))
        self.EMAIL_REPLY_USE_HTML_PARSING = asbool(
            settings.get(
                'email.reply.use_html_parsing',
                True,
            ))
        self.EMAIL_REPLY_USE_TXT_PARSING = asbool(
            settings.get(
                'email.reply.use_txt_parsing',
                True,
            ))
        self.EMAIL_REPLY_LOCKFILE_PATH = settings.get(
            'email.reply.lockfile_path', '')
        if not self.EMAIL_REPLY_LOCKFILE_PATH and self.EMAIL_REPLY_ACTIVATED:
            raise Exception(mandatory_msg.format('email.reply.lockfile_path'))

        self.EMAIL_PROCESSING_MODE = settings.get(
            'email.processing_mode',
            'sync',
        ).upper()

        if self.EMAIL_PROCESSING_MODE not in (
                self.CST.ASYNC,
                self.CST.SYNC,
        ):
            raise Exception('email.processing_mode '
                            'can '
                            'be "{}" or "{}", not "{}"'.format(
                                self.CST.ASYNC,
                                self.CST.SYNC,
                                self.EMAIL_PROCESSING_MODE,
                            ))

        self.EMAIL_SENDER_REDIS_HOST = settings.get(
            'email.async.redis.host',
            'localhost',
        )
        self.EMAIL_SENDER_REDIS_PORT = int(
            settings.get(
                'email.async.redis.port',
                6379,
            ))
        self.EMAIL_SENDER_REDIS_DB = int(
            settings.get(
                'email.async.redis.db',
                0,
            ))
        self.INVITE_NEW_USER_MINIMAL_PROFILE = settings.get(
            'invitation.new_user.minimal_profile', Group.TIM_MANAGER_GROUPNAME)
        ###
        # WSGIDAV (Webdav server)
        ###

        # TODO - G.M - 27-03-2018 - [WebDav] Restore wsgidav config
        #self.WSGIDAV_CONFIG_PATH = settings.get(
        #    'wsgidav.config_path',
        #    'wsgidav.conf',
        #)
        # TODO: Convert to importlib
        # http://stackoverflow.com/questions/41063938/use-importlib-instead-imp-for-non-py-file
        #self.wsgidav_config = imp.load_source(
        #    'wsgidav_config',
        #    self.WSGIDAV_CONFIG_PATH,
        #)
        # self.WSGIDAV_PORT = self.wsgidav_config.port
        # self.WSGIDAV_CLIENT_BASE_URL = settings.get(
        #     'wsgidav.client.base_url',
        #     None,
        # )
        #
        # if not self.WSGIDAV_CLIENT_BASE_URL:
        #     self.WSGIDAV_CLIENT_BASE_URL = \
        #         '{0}:{1}'.format(
        #             self.WEBSITE_SERVER_NAME,
        #             self.WSGIDAV_PORT,
        #         )
        #     logger.warning(self,
        #         'NOTE: Generated wsgidav.client.base_url parameter with '
        #         'followings parameters: website.server_name and '
        #         'wsgidav.conf port'.format(
        #             self.WSGIDAV_CLIENT_BASE_URL,
        #         )
        #     )
        #
        # if not self.WSGIDAV_CLIENT_BASE_URL.endswith('/'):
        #     self.WSGIDAV_CLIENT_BASE_URL += '/'

        # TODO - G.M - 27-03-2018 - [Caldav] Restore radicale config
        ###
        # RADICALE (Caldav server)
        ###
        # self.RADICALE_SERVER_HOST = settings.get(
        #     'radicale.server.host',
        #     '127.0.0.1',
        # )
        # self.RADICALE_SERVER_PORT = int(settings.get(
        #     'radicale.server.port',
        #     5232,
        # ))
        # # Note: Other parameters needed to work in SSL (cert file, etc)
        # self.RADICALE_SERVER_SSL = asbool(settings.get(
        #     'radicale.server.ssl',
        #     False,
        # ))
        # self.RADICALE_SERVER_FILE_SYSTEM_FOLDER = settings.get(
        #     'radicale.server.filesystem.folder',
        # )
        # if not self.RADICALE_SERVER_FILE_SYSTEM_FOLDER:
        #     raise Exception(
        #         mandatory_msg.format('radicale.server.filesystem.folder')
        #     )
        # self.RADICALE_SERVER_ALLOW_ORIGIN = settings.get(
        #     'radicale.server.allow_origin',
        #     None,
        # )
        # if not self.RADICALE_SERVER_ALLOW_ORIGIN:
        #     self.RADICALE_SERVER_ALLOW_ORIGIN = self.WEBSITE_BASE_URL
        #     logger.warning(self,
        #         'NOTE: Generated radicale.server.allow_origin parameter with '
        #         'followings parameters: website.base_url ({0})'
        #         .format(self.WEBSITE_BASE_URL)
        #     )
        #
        # self.RADICALE_SERVER_REALM_MESSAGE = settings.get(
        #     'radicale.server.realm_message',
        #     'Tracim Calendar - Password Required',
        # )
        #
        # self.RADICALE_CLIENT_BASE_URL_HOST = settings.get(
        #     'radicale.client.base_url.host',
        #     'http://{}:{}'.format(
        #         self.RADICALE_SERVER_HOST,
        #         self.RADICALE_SERVER_PORT,
        #     ),
        # )
        #
        # self.RADICALE_CLIENT_BASE_URL_PREFIX = settings.get(
        #     'radicale.client.base_url.prefix',
        #     '/',
        # )
        # # Ensure finished by '/'
        # if '/' != self.RADICALE_CLIENT_BASE_URL_PREFIX[-1]:
        #     self.RADICALE_CLIENT_BASE_URL_PREFIX += '/'
        # if '/' != self.RADICALE_CLIENT_BASE_URL_PREFIX[0]:
        #     self.RADICALE_CLIENT_BASE_URL_PREFIX \
        #         = '/' + self.RADICALE_CLIENT_BASE_URL_PREFIX
        #
        # if not self.RADICALE_CLIENT_BASE_URL_HOST:
        #     logger.warning(self,
        #         'Generated radicale.client.base_url.host parameter with '
        #         'followings parameters: website.server_name -> {}'
        #         .format(self.WEBSITE_SERVER_NAME)
        #     )
        #     self.RADICALE_CLIENT_BASE_URL_HOST = self.WEBSITE_SERVER_NAME
        #
        # self.RADICALE_CLIENT_BASE_URL_TEMPLATE = '{}{}'.format(
        #     self.RADICALE_CLIENT_BASE_URL_HOST,
        #     self.RADICALE_CLIENT_BASE_URL_PREFIX,
        # )
        self.PREVIEW_JPG_RESTRICTED_DIMS = asbool(
            settings.get('preview.jpg.restricted_dims', False))
        preview_jpg_allowed_dims_str = settings.get('preview.jpg.allowed_dims',
                                                    '')  # nopep8
        allowed_dims = []
        if preview_jpg_allowed_dims_str:
            for sizes in preview_jpg_allowed_dims_str.split(','):
                parts = sizes.split('x')
                assert len(parts) == 2
                width, height = parts
                assert width.isdecimal()
                assert height.isdecimal()
                size = PreviewDim(int(width), int(height))
                allowed_dims.append(size)

        if not allowed_dims:
            size = PreviewDim(256, 256)
            allowed_dims.append(size)

        self.PREVIEW_JPG_ALLOWED_DIMS = allowed_dims

        self.FRONTEND_SERVE = asbool(settings.get('frontend.serve', False))
        # INFO - G.M - 2018-08-06 - we pretend that frontend_dist_folder
        # is probably in frontend subfolder
        # of tracim_v2 parent of both backend and frontend
        backend_folder = os.path.dirname(
            os.path.dirname(os.path.abspath(__file__)))  # nopep8
        tracim_v2_folder = os.path.dirname(backend_folder)
        backend_i18n_folder = os.path.join(backend_folder, 'tracim_backend',
                                           'locale')  # nopep8

        self.BACKEND_I18N_FOLDER = settings.get('backend.18n_folder_path',
                                                backend_i18n_folder)
        if not os.path.isdir(self.BACKEND_I18N_FOLDER):
            raise Exception('ERROR: {} folder does not exist as folder. '
                            'please set backend.i8n_folder_path'
                            'with a correct value'.format(
                                self.BACKEND_I18N_FOLDER))

        frontend_dist_folder = os.path.join(tracim_v2_folder, 'frontend',
                                            'dist')  # nopep8
        self.FRONTEND_DIST_FOLDER_PATH = settings.get(
            'frontend.dist_folder_path', frontend_dist_folder)

        # INFO - G.M - 2018-08-06 - We check dist folder existence
        if self.FRONTEND_SERVE and not os.path.isdir(
                self.FRONTEND_DIST_FOLDER_PATH):  # nopep8
            raise Exception('ERROR: {} folder does not exist as folder. '
                            'please set frontend.dist_folder.path'
                            'with a correct value'.format(
                                self.FRONTEND_DIST_FOLDER_PATH))
Exemple #22
0
    def notify_content_update(self, content: Content):

        if content.get_last_action(
        ).id not in self.config.EMAIL__NOTIFICATION__NOTIFIED_EVENTS:
            logger.info(
                self,
                "Skip email notification for update of content {}"
                "by user {} (the action is {})".format(
                    content.content_id,
                    # below: 0 means "no user"
                    self._user.user_id if self._user else 0,
                    content.get_last_action().id,
                ),
            )
            return

        logger.info(
            self,
            "About to email-notify update"
            "of content {} by user {}".format(
                content.content_id,
                # Below: 0 means "no user"
                self._user.user_id if self._user else 0,
            ),
        )

        if content.type not in self.config.EMAIL__NOTIFICATION__NOTIFIED_CONTENTS:
            logger.info(
                self,
                "Skip email notification for update of content {}"
                "by user {} (the content type is {})".format(
                    content.type,
                    # below: 0 means "no user"
                    self._user.user_id if self._user else 0,
                    content.get_last_action().id,
                ),
            )
            return

        logger.info(
            self,
            "About to email-notify update"
            "of content {} by user {}".format(
                content.content_id,
                # Below: 0 means "no user"
                self._user.user_id if self._user else 0,
            ),
        )

        ####
        #
        # INFO - D.A. - 2014-11-05 - Emails are sent through asynchronous jobs.
        # For that reason, we do not give SQLAlchemy objects but ids only
        # (SQLA objects are related to a given thread/session)
        #
        try:
            # TODO - D.A - 2014-11-06
            # This feature must be implemented in order to be able to scale to large communities
            if self.config.JOBS__PROCESSING_MODE == self.config.CST.ASYNC:
                logger.warning(
                    self,
                    "Creating mails in SYNC mode as ASYNC is not supported yet."
                )
            else:
                logger.info(self, "Creating email in SYNC mode")
            EmailManager(self._smtp_config, self.config,
                         self.session).notify_content_update(
                             self._user.user_id, content.content_id)
        except Exception:
            logger.exception(self,
                             "Exception catched during email notification")
Exemple #23
0
    def _load_global_config(self) -> None:
        """
        Load generic config
        """
        ###
        # General
        ###
        self.SQLALCHEMY__URL = self.get_raw_config("sqlalchemy.url", "")
        self.DEFAULT_LANG = self.get_raw_config("default_lang",
                                                DEFAULT_FALLBACK_LANG)
        backend_folder = os.path.dirname(
            os.path.dirname(os.path.abspath(__file__)))
        tracim_v2_folder = os.path.dirname(backend_folder)
        default_color_config_file_path = os.path.join(tracim_v2_folder,
                                                      "color.json")
        self.COLOR__CONFIG_FILE_PATH = self.get_raw_config(
            "color.config_file_path", default_color_config_file_path)

        default_enabled_app = ("contents/thread,"
                               "contents/file,"
                               "contents/html-document,"
                               "contents/folder,"
                               "agenda")

        self.APP__ENABLED = string_to_list(
            self.get_raw_config("app.enabled", default_enabled_app),
            separator=",",
            cast_func=str,
            do_strip=True,
        )

        self.DEPOT_STORAGE_DIR = self.get_raw_config("depot_storage_dir")
        self.DEPOT_STORAGE_NAME = self.get_raw_config("depot_storage_name")
        self.PREVIEW_CACHE_DIR = self.get_raw_config("preview_cache_dir")

        self.AUTH_TYPES = string_to_list(
            self.get_raw_config("auth_types", "internal"),
            separator=",",
            cast_func=AuthType,
            do_strip=True,
        )
        self.REMOTE_USER_HEADER = self.get_raw_config("remote_user_header",
                                                      None)
        # TODO - G.M - 2018-09-11 - Deprecated param
        # self.DATA_UPDATE_ALLOWED_DURATION = int(self.get_raw_config(
        #     'content.update.allowed.duration',
        #     0,
        # ))

        self.API__KEY = self.get_raw_config("api.key", "", secret=True)
        self.SESSION__REISSUE_TIME = int(
            self.get_raw_config("session.reissue_time", "120"))
        self.SESSION__DATA_DIR = self.get_raw_config("session.data_dir")
        self.SESSION__LOCK_DIR = self.get_raw_config("session.lock_dir")
        self.WEBSITE__TITLE = self.get_raw_config("website.title", "TRACIM")

        # base url of the frontend
        self.WEBSITE__BASE_URL = self.get_raw_config("website.base_url", "")

        self.API__BASE_URL = self.get_raw_config("api.base_url",
                                                 self.WEBSITE__BASE_URL)

        if self.API__BASE_URL != self.WEBSITE__BASE_URL:
            default_cors_allowed_origin = "{},{}".format(
                self.WEBSITE__BASE_URL, self.API__BASE_URL)
        else:
            default_cors_allowed_origin = self.WEBSITE__BASE_URL

        self.CORS__ACCESS_CONTROL_ALLOWED_ORIGIN = string_to_list(
            self.get_raw_config("cors.access-control-allowed-origin",
                                default_cors_allowed_origin),
            separator=",",
            cast_func=str,
            do_strip=True,
        )

        self.USER__AUTH_TOKEN__VALIDITY = int(
            self.get_raw_config("user.auth_token.validity", "604800"))

        # TODO - G.M - 2019-03-14 - retrocompat code,
        # will be deleted in the future (https://github.com/tracim/tracim/issues/1483)
        defaut_reset_password_validity = "900"
        self.USER__RESET_PASSWORD__VALIDITY = self.get_raw_config(
            "user.reset_password.validity")
        if self.USER__RESET_PASSWORD__VALIDITY:
            logger.warning(
                self,
                "user.reset_password.validity parameter is deprecated ! "
                "please use user.reset_password.token_lifetime instead.",
            )
            self.USER__RESET_PASSWORD__TOKEN_LIFETIME = self.USER__RESET_PASSWORD__VALIDITY
        else:
            self.USER__RESET_PASSWORD__TOKEN_LIFETIME = int(
                self.get_raw_config("user.reset_password.token_lifetime",
                                    defaut_reset_password_validity))

        self.DEBUG = asbool(self.get_raw_config("debug", "false"))

        self.PREVIEW__JPG__RESTRICTED_DIMS = asbool(
            self.get_raw_config("preview.jpg.restricted_dims", "false"))
        self.PREVIEW__JPG__ALLOWED_DIMS = string_to_list(
            self.get_raw_config("preview.jpg.allowed_dims", "256x256"),
            cast_func=PreviewDim.from_string,
            separator=",",
        )

        self.FRONTEND__SERVE = asbool(
            self.get_raw_config("frontend.serve", "false"))
        # INFO - G.M - 2018-08-06 - we pretend that frontend_dist_folder
        # is probably in frontend subfolder
        # of tracim_v2 parent of both backend and frontend
        backend_folder = os.path.dirname(
            os.path.dirname(os.path.abspath(__file__)))
        tracim_v2_folder = os.path.dirname(backend_folder)
        backend_i18n_folder = os.path.join(backend_folder, "tracim_backend",
                                           "locale")

        self.BACKEND__I18N_FOLDER_PATH = self.get_raw_config(
            "backend.i18n_folder_path", backend_i18n_folder)

        frontend_dist_folder = os.path.join(tracim_v2_folder, "frontend",
                                            "dist")
        self.FRONTEND__DIST_FOLDER_PATH = self.get_raw_config(
            "frontend.dist_folder_path", frontend_dist_folder)
Exemple #24
0
    def _check_email_config_validity(self) -> None:
        """
        Check if config is correctly setted for email features
        """
        if not self.EMAIL__NOTIFICATION__ACTIVATED:
            logger.warning(
                self,
                "Notification by email mechanism is disabled! "
                "Notification and mail invitation mechanisms will not work.",
            )

        if not self.EMAIL__REPLY__LOCKFILE_PATH and self.EMAIL__REPLY__ACTIVATED:
            self.check_mandatory_param(
                "EMAIL__REPLY__LOCKFILE_PATH",
                self.EMAIL__REPLY__LOCKFILE_PATH,
                when_str="when email reply is activated",
            )

        if self.EMAIL__REPLY__ACTIVATED:
            # INFO - G.M - 2019-12-10 - check imap config provided
            self.check_mandatory_param(
                "EMAIL__REPLY__IMAP__SERVER",
                self.EMAIL__REPLY__IMAP__SERVER,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__REPLY__IMAP__PORT",
                self.EMAIL__REPLY__IMAP__PORT,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__REPLY__IMAP__USER",
                self.EMAIL__REPLY__IMAP__USER,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__REPLY__IMAP__PASSWORD",
                self.EMAIL__REPLY__IMAP__PASSWORD,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__REPLY__IMAP__FOLDER",
                self.EMAIL__REPLY__IMAP__FOLDER,
                when_str="when email notification is activated",
            )

        if self.EMAIL__NOTIFICATION__ACTIVATED:
            # INFO - G.M - 2019-12-10 - check smtp config provided
            self.check_mandatory_param(
                "EMAIL__NOTIFICATION__SMTP__SERVER",
                self.EMAIL__NOTIFICATION__SMTP__SERVER,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__NOTIFICATION__SMTP__PORT",
                self.EMAIL__NOTIFICATION__SMTP__PORT,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__NOTIFICATION__SMTP__USER",
                self.EMAIL__NOTIFICATION__SMTP__USER,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__NOTIFICATION__SMTP__PASSWORD",
                self.EMAIL__NOTIFICATION__SMTP__PASSWORD,
                when_str="when email notification is activated",
            )
            # INFO - G.M - 2019-12-10 - check value provided for headers
            self.check_mandatory_param(
                "EMAIL__NOTIFICATION__FROM__EMAIL",
                self.EMAIL__NOTIFICATION__FROM__EMAIL,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__NOTIFICATION__FROM__DEFAULT_LABEL",
                self.EMAIL__NOTIFICATION__FROM__DEFAULT_LABEL,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__NOTIFICATION__REPLY_TO__EMAIL",
                self.EMAIL__NOTIFICATION__REPLY_TO__EMAIL,
                when_str="when email notification is activated",
            )
            self.check_mandatory_param(
                "EMAIL__NOTIFICATION__REFERENCES__EMAIL",
                self.EMAIL__NOTIFICATION__REFERENCES__EMAIL,
                when_str="when email notification is activated",
            )
            # INFO - G.M - 2019-02-01 - check if template are available,
            # do not allow running with email_notification_activated
            # if templates needed are not available
            templates = {
                "content_update notification":
                self.EMAIL__NOTIFICATION__CONTENT_UPDATE__TEMPLATE__HTML,
                "created account":
                self.EMAIL__NOTIFICATION__CREATED_ACCOUNT__TEMPLATE__HTML,
                "password reset":
                self.
                EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__TEMPLATE__HTML,
            }
            for template_description, template_path in templates.items():
                if not template_path or not os.path.isfile(template_path):
                    raise ConfigurationError(
                        "ERROR: email template for {template_description} "
                        'not found at "{template_path}."'.format(
                            template_description=template_description,
                            template_path=template_path,
                        ))