Exemplo n.º 1
0
    def connect(self):
        if not self._smtp_connection:
            log = 'Connecting from SMTP server {}'
            logger.info(self, log.format(self._smtp_config.server))
            self._smtp_connection = smtplib.SMTP(self._smtp_config.server,
                                                 self._smtp_config.port)
            self._smtp_connection.ehlo()

            if self._smtp_config.login:
                try:
                    starttls_result = self._smtp_connection.starttls()
                    log = 'SMTP start TLS result: {}'
                    logger.debug(self, log.format(starttls_result))
                except Exception as e:
                    log = 'SMTP start TLS error: {}'
                    logger.debug(self, log.format(e.__str__()))

            if self._smtp_config.login:
                try:
                    login_res = self._smtp_connection.login(
                        self._smtp_config.login, self._smtp_config.password)
                    log = 'SMTP login result: {}'
                    logger.debug(self, log.format(login_res))
                except Exception as e:
                    log = 'SMTP login error: {}'
                    logger.debug(self, log.format(e.__str__()))
            logger.info(self, 'Connection OK')
Exemplo n.º 2
0
    def create_index(self) -> None:
        """
        Create the index template in elasticsearch specifying the mappings and any
        settings to be used. This can be run at any time, ideally at every new code
        deploy
        """
        # FIXME BS 2019-06-10: Load ES model only when ES search (see #1892)
        from tracim_backend.lib.search.es_models import IndexedContent

        # INFO - G.M - 2019-05-15 - alias migration mecanism to allow easily updatable index.
        # from https://github.com/elastic/elasticsearch-dsl-py/blob/master/examples/alias_migration.py
        # Configure index with our indexing preferences
        logger.info(self, "Create index settings ...")
        if self._config.SEARCH__ELASTICSEARCH__USE_INGEST:
            self._create_ingest_pipeline()
        # create an index template
        index_template = IndexedContent._index.as_template(
            self.index_document_alias, self.index_document_pattern)
        # upload the template into elasticsearch
        # potentially overriding the one already there
        index_template.save(using=self.es)

        # create the first index if it doesn't exist
        current_index = Index(self.index_document_alias)
        if not current_index.exists(using=self.es):
            self.migrate_index(move_data=False)

        logger.info(self, "ES index is ready")
Exemplo n.º 3
0
    def __init__(self, settings: typing.Dict[str, typing.Any]):
        # INFO - G.M - 2019-12-02 - Store own settings original dict, with copy
        # to avoid issue when serializing CFG object. settings dict is completed
        # with object in some context
        self.settings = settings.copy()
        self.config_info = []  # type: typing.List[ConfigParam]
        logger.debug(self, "CONFIG_PROCESS:1: load enabled apps")
        self.load_enabled_apps()
        logger.debug(self, "CONFIG_PROCESS:3: load config from settings")
        self.load_config()
        logger.debug(self, "CONFIG_PROCESS:4: check validity of config given")
        self._check_consistency()
        self.check_config_validity()
        logger.debug(self, "CONFIG_PROCESS:5: End of config process")

        app_lib = ApplicationApi(app_list=app_list, show_inactive=True)
        for app in app_lib.get_all():
            logger.info(
                self,
                "LOADED_APP:{state}:{slug}:{label}".format(
                    state="ENABLED" if app.is_active else "DISABLED",
                    slug=app.slug,
                    label=app.label,
                ),
            )
Exemplo n.º 4
0
 def notify_content_update(self, content: Content):
     type(self).send_count += 1
     logger.info(
         self,
         'Fake notifier, do not send notification for update of content {}'.
         format(content.content_id)  # nopep8
     )
Exemplo n.º 5
0
def send_email_through(
    config: CFG, sendmail_callable: typing.Callable[[MIMEMultipart], None], message: MIMEMultipart
) -> None:
    """
    Send mail encapsulation to send it in async or sync mode.

    TODO BS 20170126: A global mail/sender management should be a good
                      thing. Actually, this method is an fast solution.
    :param config: system configuration
    :param sendmail_callable: A callable who get message on first parameter
    :param message: The message who have to be sent
    """
    if config.JOBS__PROCESSING_MODE == config.CST.SYNC:
        logger.info(send_email_through, "send email to {} synchronously".format(message["To"]))
        sendmail_callable(message)
    elif config.JOBS__PROCESSING_MODE == config.CST.ASYNC:
        logger.info(
            send_email_through,
            "send email to {} asynchronously:"
            "mail stored in queue in wait for a"
            "mail_notifier daemon".format(message["To"]),
        )
        redis_connection = get_redis_connection(config)
        queue = get_rq_queue(redis_connection, "mail_sender")
        queue.enqueue(sendmail_callable, message)
    else:
        raise NotImplementedError(
            "Mail sender processing mode {} is not implemented".format(config.JOBS__PROCESSING_MODE)
        )
Exemplo n.º 6
0
 def __init__(self, **settings):
     logger.info(self, "Add additional radicale config")
     self.radicale_config = load_radicale_config(())
     self.radicale_config = self._parse_additional_radicale_config(
         self.radicale_config, settings)
     self.app_config = CFG(settings)
     self.create_dir_tree(self.radicale_config, self.app_config)
Exemplo n.º 7
0
def init_plugin_manager(app_config: CFG) -> pluggy.PluginManager:
    plugin_manager = create_plugin_manager()

    # Static plugins, imported here to avoid circular reference with hookimpl
    from tracim_backend.lib.core.event import EventBuilder
    from tracim_backend.lib.core.event import EventPublisher
    import tracim_backend.lib.core.mention as mention

    plugin_manager.register(EventBuilder(app_config))
    plugin_manager.register(EventPublisher(app_config))
    mention.register_tracim_plugin(plugin_manager)

    # INFO - G.M - 2019-11-27 - if a plugin path is provided, load plugins from this path
    plugin_path = app_config.PLUGIN__FOLDER_PATH
    plugins = {}  # type: typing.Dict[str, types.ModuleType]
    if plugin_path and plugin_path not in sys.path:
        sys.path.append(plugin_path)
        try:
            plugins = _load_plugins(plugin_manager)
        finally:
            sys.path.remove(plugin_path)

    plugin_manager.hook.add_new_hooks.call_historic(kwargs=dict(
        plugin_manager=plugin_manager))
    _register_all(plugin_manager, plugins)
    logger.info(
        init_plugin_manager,
        "Loaded and registered the following plugins: {}".format(
            tuple(plugins.keys())),
    )
    plugin_manager.hook.on_plugins_loaded(plugin_manager=plugin_manager)
    return plugin_manager
Exemplo n.º 8
0
 def wrapper(self, context,
             request: "TracimRequest") -> typing.Callable:
     authorization_checker.check(tracim_context=request)
     logger.info(
         request,
         "{} {} from authenticated user {}".format(
             request.method, request.path,
             request.current_user.user_id),
     )
     return func(self, context, request)
Exemplo n.º 9
0
    def delete_index(self) -> None:

        # TODO - G.M - 2019-05-31 - This code delete all index related to pattern, check if possible
        # to be more specific here.
        logger.info(
            self,
            "delete index with pattern {}".format(self.index_document_pattern))
        self.es.indices.delete(self.index_document_pattern,
                               allow_no_indices=True)
        self.es.indices.delete_template(self.index_document_alias)
Exemplo n.º 10
0
    def send_mail(self, message: MIMEMultipart):
        if not self._is_active:
            log = "Not sending email to {} (service disabled)"
            logger.info(self, log.format(message["To"]))
        else:
            self.connect()  # Actually, this connects to SMTP only if required
            logger.info(self, "Sending email to {}".format(message["To"]))
            # TODO - G.M - 2019-01-29 - optimisize this code, we should not send
            # email if connection has failed.
            send_action = "{:8s}".format("SENT")
            failed_action = "{:8s}".format("SENDFAIL")
            action = send_action
            try:
                send_message_result = self._smtp_connection.send_message(
                    message)
                # INFO - G.M - 2019-01-29 - send_message return if not failed,
                # dict of refused recipients.

                if send_message_result == {}:
                    logger.debug(self, "One mail correctly sent using SMTP.")
                else:
                    # INFO - G.M - 2019-01-29 - send_message_result != {}
                    # case should not happened
                    # as we send not mail with multiple recipient at the same
                    # time. send_message will not raise exception
                    # just if some recipient work and some other failed.
                    # TODO - G.M - 2019-01-29 - better support for multirecipient email
                    log = "Mail could not be send to some recipient: {}"
                    logger.debug(self, log.format(send_message_result))
                    action = failed_action

            except smtplib.SMTPException as exc:
                log = "SMTP sending message return error: {}"
                logger.error(self, log.format(str(exc)))
                action = failed_action
            except Exception as exc:
                log = "Unexpected exception during sending email message using SMTP: {}"
                logger.error(self, log.format(exc.__str__()))
                logger.error(self, traceback.format_exc())
                action = failed_action

            from tracim_backend.lib.mail_notifier.notifier import EmailManager

            if action == send_action:
                msg = "an email was sended to {}".format(message["To"])
            else:
                msg = "fail to send email to {}".format(message["To"])

            EmailManager.log_email_notification(
                msg=msg,
                action=action,
                email_recipient=message["To"],
                email_subject=message["Subject"],
                config=self.config,
            )
Exemplo n.º 11
0
 def log_config_header(self, title: str) -> None:
     logger.info(self, title)
     logger.info(
         self,
         CONFIG_LOG_TEMPLATE.format(
             config_value="<config_value>",
             config_source="<config_source>",
             config_name="<config_name>",
             config_name_source="<config_name_source>",
         ),
     )
Exemplo n.º 12
0
 def __init__(self,
              config: CFG,
              session: Session,
              current_user: User = None) -> None:
     INotifier.__init__(
         self,
         config,
         session,
         current_user,
     )
     logger.info(self, 'Instantiating Dummy Notifier')
Exemplo n.º 13
0
    def send_mail(self, message: MIMEMultipart):
        if not self._is_active:
            log = 'Not sending email to {} (service disabled)'
            logger.info(self, log.format(message['To']))
        else:
            self.connect()  # Actually, this connects to SMTP only if required
            logger.info(self, 'Sending email to {}'.format(message['To']))
            # TODO - G.M - 2019-01-29 - optimisize this code, we should not send
            # email if connection has failed.
            send_action = '{:8s}'.format('SENT')
            failed_action = '{:8s}'.format('SENDFAIL')
            action = send_action
            try:
                send_message_result = self._smtp_connection.send_message(message)
                # INFO - G.M - 2019-01-29 - send_message return if not failed,
                # dict of refused recipients.

                if send_message_result == {}:
                    logger.debug(self, 'One mail correctly sent using SMTP.')
                else:
                    # INFO - G.M - 2019-01-29 - send_message_result != {}
                    # case should not happened
                    # as we send not mail with multiple recipient at the same
                    # time. send_message will not raise exception
                    # just if some recipient work and some other failed.
                    # TODO - G.M - 2019-01-29 - better support for multirecipient email
                    log = 'Mail could not be send to some recipient: {}'
                    logger.debug(self, log.format(send_message_result))
                    action = failed_action

            except smtplib.SMTPException as exc:
                log = 'SMTP sending message return error: {}'
                logger.error(self, log.format(str(exc)))
                action = failed_action
            except Exception as exc:
                log = 'Unexpected exception during sending email message using SMTP: {}'
                logger.error(self, log.format(exc.__str__()))
                logger.error(self, traceback.format_exc())
                action = failed_action

            from tracim_backend.lib.mail_notifier.notifier import EmailManager
            if action == send_action:
                msg = 'an email was sended to {}'.format(message['To'])
            else:
                msg = 'fail to send email to {}'.format(message['To'])

            EmailManager.log_email_notification(
                msg=msg,
                action=action,
                email_recipient=message['To'],
                email_subject=message['Subject'],
                config=self.config,
            )
Exemplo n.º 14
0
    def notify_upload_permission(
        self,
        emitter: UserInContext,
        workspace_in_context: WorkspaceInContext,
        upload_permission_receivers: typing.List[UploadPermissionInContext],
        upload_permission_password: str,
    ) -> None:
        """
        Send mails to notify users for sharing content
        :param emitter: User emitter of the sharing
        :param workspace_in_context: workspace where receivers can now upload file
        :param upload_permission_receivers: list of upload_permission
        :param upload_permission_password: cleartext password of the sharing
        """

        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        upload_permission_password_enabled = False
        if upload_permission_password:
            upload_permission_password_enabled = True
        translator = Translator(self.config, default_lang=emitter.lang)
        message = self._notify_emitter(
            emitter=emitter,
            workspace_in_context=workspace_in_context,
            upload_permission_receivers=upload_permission_receivers,
            upload_permission_password=upload_permission_password,
            translator=translator,
        )
        send_email_through(config=self.config,
                           sendmail_callable=email_sender.send_mail,
                           message=message)
        emails_receivers_list = [
            upload_permission.email
            for upload_permission in upload_permission_receivers
        ]
        logger.info(
            self,
            'Generating upload permission mail from user "{}" to "{}"'.format(
                emitter.user_id, "".join(emails_receivers_list)),
        )
        for upload_permission in upload_permission_receivers:
            message = self._notify_receiver(
                emitter=emitter,
                workspace=workspace_in_context,
                upload_permission=upload_permission,
                upload_permission_password_enabled=
                upload_permission_password_enabled,
                translator=translator,
            )
            send_email_through(config=self.config,
                               sendmail_callable=email_sender.send_mail,
                               message=message)
Exemplo n.º 15
0
    def on_content_created(self, content: Content, context: TracimContext) -> None:
        try:
            parser = self._parsers[content.type]
        except KeyError:
            logger.info(
                self, "No mention parser for '{}' content type, doing nothing".format(content.type),
            )
            return

        mentions = parser.get_mentions(content.current_revision)
        if not mentions:
            return
        self._create_mention_events(mentions, content, context)
Exemplo n.º 16
0
 def __init__(
         self,
         config: CFG,
         session: Session,
         current_user: User=None
 ) -> None:
     INotifier.__init__(
         self,
         config,
         session,
         current_user,
     )
     logger.info(self, 'Instantiating Dummy Notifier')
Exemplo n.º 17
0
 def _notify_receiver(
     self,
     emitter: User,
     workspace: Workspace,
     upload_permission: UploadPermissionInContext,
     upload_permission_password_enabled: bool,
     translator: Translator,
 ) -> Message:
     logger.info(
         self,
         'preparing email from user "{}" for the upload permission on workspace "{}" to "{}"'
         .format(emitter.user_id, workspace.workspace_id,
                 upload_permission.email),
     )
     translated_subject = translator.get_translation(
         self.config.
         EMAIL__NOTIFICATION__UPLOAD_PERMISSION_TO_RECEIVER__SUBJECT)
     subject = translated_subject.format(
         website_title=self.config.WEBSITE__TITLE,
         emitter_name=emitter.display_name)
     from_header = self._get_sender(emitter)
     to_header = EmailAddress.from_rfc_email_address(
         upload_permission.email)
     html_template_file_path = (
         self.config.
         EMAIL__NOTIFICATION__UPLOAD_PERMISSION_TO_RECEIVER__TEMPLATE__HTML)
     receiver = EmailUser(user_email=upload_permission.email)
     context = {
         "emitter":
         emitter,
         "workspace":
         workspace,
         "upload_permission":
         upload_permission,
         "receiver":
         receiver,
         "upload_permission_password_enabled":
         upload_permission_password_enabled,
     }
     body_html = self._render_template(
         mako_template_filepath=html_template_file_path,
         context=context,
         translator=translator)
     message = EmailNotificationMessage(
         subject=subject,
         from_header=from_header,
         to_header=to_header,
         body_html=body_html,
         lang=translator.default_lang,
     )
     return message
Exemplo n.º 18
0
    def notify__share__content(
        self,
        emitter: User,
        shared_content: ContentInContext,
        content_share_receivers: typing.List[ContentShareInContext],
        share_password: str,
    ) -> None:
        """
        Send mails to notify users for sharing content
        :param emitter: User emitter of the sharing
        :param shared_content: content that is now shared
        :param content_share_receivers: list of content share
        :param share_password: cleartext password of the sharing
        """

        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        share_password_enabled = False
        if share_password:
            share_password_enabled = True
        translator = Translator(self.config, default_lang=emitter.lang)
        message = self._notify_emitter(
            emitter=emitter,
            shared_content=shared_content,
            content_share_receivers=content_share_receivers,
            share_password=share_password,
            translator=translator,
        )
        send_email_through(config=self.config,
                           sendmail_callable=email_sender.send_mail,
                           message=message)
        for content_share in content_share_receivers:
            emails_receivers_list = [
                share_content.email
                for share_content in content_share_receivers
            ]
            logger.info(
                self,
                'Generating share mail from user "{}" to "{}"'.format(
                    emitter.user_id, "".join(emails_receivers_list)),
            )
            message = self._notify_receiver(
                emitter=emitter,
                shared_content=shared_content,
                content_share=content_share,
                share_password_enabled=share_password_enabled,
                translator=translator,
            )
            send_email_through(config=self.config,
                               sendmail_callable=email_sender.send_mail,
                               message=message)
Exemplo n.º 19
0
 def delete_full_user(self, user: User) -> int:
     """
     Full deletion of user in database (including all his revisions)
     /!\\ May cause inconsistent database
     :param user: user to delete
     :return: user_id
     """
     self.delete_user_associated_data(user)
     self.delete_user_owned_workspace(user)
     self.delete_user_revisions(user)
     logger.info(self, "delete user {}".format(user.user_id))
     user_id = user.user_id
     self.safe_delete(user)
     return user_id
Exemplo n.º 20
0
    def _notify_receiver(
        self,
        emitter: User,
        shared_content: ContentInContext,
        content_share: ContentShareInContext,
        share_password_enabled: bool,
        translator: Translator,
    ) -> Message:
        logger.info(
            self,
            'preparing email from user "{}" for the share on content "{}" to "{}"'
            .format(emitter.user_id, shared_content.content_id,
                    content_share.email),
        )
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__SHARE_CONTENT_TO_RECEIVER__SUBJECT
        )
        subject = translated_subject.format(
            website_title=self.config.WEBSITE__TITLE,
            content_filename=shared_content.filename,
            emitter_name=emitter.display_name,
        )
        from_header = self._get_sender(emitter)
        to_header = EmailAddress.from_rfc_email_address(content_share.email)
        username, address = email.utils.parseaddr(content_share.email)
        html_template_file_path = (
            self.config.
            EMAIL__NOTIFICATION__SHARE_CONTENT_TO_RECEIVER__TEMPLATE__HTML)
        receiver = EmailUser(user_email=content_share.email)
        context = {
            "emitter": emitter,
            "shared_content": shared_content,
            "content_share": content_share,
            "share_password_enabled": share_password_enabled,
            "receiver": receiver,
        }
        body_html = self._render_template(
            mako_template_filepath=html_template_file_path,
            context=context,
            translator=translator)

        message = EmailNotificationMessage(
            subject=subject,
            from_header=from_header,
            to_header=to_header,
            body_html=body_html,
            lang=translator.default_lang,
        )
        return message
Exemplo n.º 21
0
    def notify_reset_password(self, user: User,
                              reset_password_token: str) -> None:
        """
        Reset password link for user
        :param user: user to notify
        :param reset_password_token: token for resetting password
        """
        logger.debug(self, "user: {}".format(user.user_id))
        logger.info(self,
                    "Generating reset password email to {}".format(user.email))
        translator = Translator(self.config, default_lang=user.lang)
        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__SUBJECT)
        subject = translated_subject.replace(EST.WEBSITE_TITLE,
                                             str(self.config.WEBSITE__TITLE))
        from_header = self._get_sender()
        to_header = EmailAddress(user.get_display_name(), user.email)

        html_template_file_path = (
            self.config.
            EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__TEMPLATE__HTML)
        # TODO - G.M - 2018-08-17 - Generate token
        context = {
            "user":
            user,
            "logo_url":
            get_email_logo_frontend_url(self.config),
            "reset_password_url":
            get_reset_password_frontend_url(self.config,
                                            token=reset_password_token,
                                            email=user.email),
        }

        body_html = self._render_template(
            mako_template_filepath=html_template_file_path,
            context=context,
            translator=translator)
        message = EmailNotificationMessage(
            subject=subject,
            from_header=from_header,
            to_header=to_header,
            body_html=body_html,
            lang=translator.default_lang,
        )
        send_email_through(config=self.config,
                           sendmail_callable=email_sender.send_mail,
                           message=message)
Exemplo n.º 22
0
    def delete_revision(
        self,
        revision: ContentRevisionRO,
        do_update_content_last_revision: bool = True,
    ) -> int:
        """
        :param do_update_content_last_revision: update last revision of content associated to last one if needed. Set only
        to False when needed to delete all content data and will delete associated content after.
        :param revision: revision to delete
        :return: revision_id of revision to delete
        """

        if do_update_content_last_revision and revision.node.revision_id == revision.revision_id:
            try:
                new_last_revision = (
                    self.session.query(ContentRevisionRO).filter(
                        and_(
                            ContentRevisionRO.content_id == revision.node.id,
                            ContentRevisionRO.revision_id !=
                            revision.revision_id,
                        )).order_by(
                            ContentRevisionRO.revision_id.desc()).limit(
                                1).one())
                revision.node.current_revision = new_last_revision
                self.safe_update(revision.node)
            except NoResultFound:
                raise CannotDeleteUniqueRevisionWithoutDeletingContent(
                    'revision "{}" is the only revision for content "{}", it is not possible to delete'
                    .format(revision.revision_id, revision.content_id))

        # INFO - G.M - 2019-12-11 - delete revision read status
        read_statuses = self.session.query(RevisionReadStatus).filter(
            RevisionReadStatus.revision_id == revision.revision_id)
        for read_status in read_statuses:
            logger.info(
                self,
                "delete read status of user {} from revision {}".format(
                    read_status.user_id, read_status.revision_id),
            )
            self.safe_delete(read_status)

        logger.info(
            self,
            "delete revision {} of content {}".format(revision.revision_id,
                                                      revision.content_id),
        )
        revision_id = revision.revision_id
        self.safe_delete(revision)
        return revision_id
Exemplo n.º 23
0
    def connect(self):
        if not self._smtp_connection:
            log = "Connecting to SMTP server {}"
            logger.info(self, log.format(self._smtp_config.server))
            if self._smtp_config.use_implicit_ssl:
                self._smtp_connection = smtplib.SMTP_SSL(
                    self._smtp_config.server, self._smtp_config.port
                )
            else:
                self._smtp_connection = smtplib.SMTP(
                    self._smtp_config.server, self._smtp_config.port
                )
            self._smtp_connection.ehlo()

            # TODO - G.M - 2020-04-02 - Starttls usage should be explicit in configuration, see
            # https://github.com/tracim/tracim/issues/2815
            if self._smtp_config.login and not self._smtp_config.use_implicit_ssl:
                try:
                    starttls_result = self._smtp_connection.starttls()

                    if starttls_result[0] == 220:
                        logger.info(self, "SMTP Start TLS OK")

                    log = "SMTP Start TLS return code: {} with message: {}"
                    logger.debug(
                        self, log.format(starttls_result[0], starttls_result[1].decode("utf-8"))
                    )
                except smtplib.SMTPResponseException as exc:
                    log = "SMTP start TLS return error code: {} with message: {}"
                    logger.error(self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8")))
                except Exception:
                    log = "Unexpected exception during SMTP start TLS process"
                    logger.exception(self, log)

            if self._smtp_config.login:
                try:
                    login_res = self._smtp_connection.login(
                        self._smtp_config.login, self._smtp_config.password
                    )

                    if login_res[0] == 235:
                        logger.info(self, "SMTP Authentication Successful")
                    if login_res[0] == 503:
                        logger.info(self, "SMTP Already Authenticated")

                    log = "SMTP login return code: {} with message: {}"
                    logger.debug(self, log.format(login_res[0], login_res[1].decode("utf-8")))
                except smtplib.SMTPAuthenticationError as exc:
                    log = "SMTP auth return error code: {} with message: {}"
                    logger.error(self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8")))
                    logger.error(
                        self, "check your auth params combinaison " "(login/password) for SMTP"
                    )
                except smtplib.SMTPResponseException as exc:
                    log = "SMTP login return error code: {} with message: {}"
                    logger.error(self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8")))
                except Exception:
                    log = "Unexpected exception during SMTP login"
                    logger.exception(self, log)
Exemplo n.º 24
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
Exemplo n.º 25
0
 def send_mail(self, message: MIMEMultipart):
     if not self._is_active:
         log = 'Not sending email to {} (service disabled)'
         logger.info(self, log.format(message['To']))
     else:
         self.connect()  # Actually, this connects to SMTP only if required
         logger.info(self, 'Sending email to {}'.format(message['To']))
         self._smtp_connection.send_message(message)
         from tracim_backend.lib.mail_notifier.notifier import EmailManager
         EmailManager.log_notification(
             action='   SENT',
             recipient=message['To'],
             subject=message['Subject'],
             config=self.config,
         )
Exemplo n.º 26
0
    def notify_created_account(
            self,
            user: User,
            password: str,
            origin_user: typing.Optional[User] = None) -> None:
        """
        Send created account email to given user.

        :param password: choosed password
        :param user: user to notify
        """
        logger.info(self,
                    "Generating created account mail to {}".format(user.email))

        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        translator = Translator(self.config, default_lang=user.lang)
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__CREATED_ACCOUNT__SUBJECT)
        subject = translated_subject.replace(EST.WEBSITE_TITLE,
                                             str(self.config.WEBSITE__TITLE))
        from_header = self._get_sender(origin_user)
        to_header = EmailAddress(user.get_display_name(), user.email)
        html_template_file_path = self.config.EMAIL__NOTIFICATION__CREATED_ACCOUNT__TEMPLATE__HTML

        context = {
            "origin_user": origin_user,
            "user": user,
            "password": password,
            "logo_url": get_email_logo_frontend_url(self.config),
            "login_url": get_login_frontend_url(self.config),
        }
        translator = Translator(self.config, default_lang=user.lang)
        body_html = self._render_template(
            mako_template_filepath=html_template_file_path,
            context=context,
            translator=translator)
        message = EmailNotificationMessage(
            subject=subject,
            from_header=from_header,
            to_header=to_header,
            body_html=body_html,
            lang=translator.default_lang,
        )

        send_email_through(config=self.config,
                           sendmail_callable=email_sender.send_mail,
                           message=message)
Exemplo n.º 27
0
 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')
Exemplo n.º 28
0
 def load_config(self) -> None:
     """Parse configuration file and env variables"""
     logger.info(
         self,
         CONFIG_LOG_TEMPLATE.format(
             config_value="<config_value>",
             config_source="<config_source>",
             config_name="<config_name>",
             config_name_source="<config_name_source>",
         ),
     )
     self._load_global_config()
     self._load_email_config()
     self._load_ldap_config()
     self._load_webdav_config()
     self._load_caldav_config()
Exemplo n.º 29
0
    def notify_reset_password(self, user: User, reset_password_token: str) -> None:
        """
        Reset password link for user
        :param user: user to notify
        :param reset_password_token: token for resetting password
        """
        logger.debug(self, "user: {}".format(user.user_id))
        logger.info(self, "Generating reset password email to {}".format(user.email))
        translator = Translator(self.config, default_lang=user.lang)
        email_sender = EmailSender(
            self.config, self._smtp_config, self.config.EMAIL__NOTIFICATION__ACTIVATED
        )
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__SUBJECT
        )
        subject = translated_subject.replace(EST.WEBSITE_TITLE, str(self.config.WEBSITE__TITLE))
        message = MIMEMultipart("alternative")
        message["Subject"] = subject
        message["From"] = self._get_sender()
        message["To"] = formataddr((user.get_display_name(), user.email))

        html_template_file_path = (
            self.config.EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__TEMPLATE__HTML
        )
        # TODO - G.M - 2018-08-17 - Generate token
        context = {
            "user": user,
            "logo_url": get_email_logo_frontend_url(self.config),
            "reset_password_url": get_reset_password_frontend_url(
                self.config, token=reset_password_token, email=user.email
            ),
        }

        body_html = self._render_template(
            mako_template_filepath=html_template_file_path, context=context, translator=translator
        )

        part2 = MIMEText(body_html, "html", "utf-8")

        # Attach parts into message container.
        # According to RFC 2046, the last part of a multipart message,
        # in this case the HTML message, is best and preferred.
        message.attach(part2)

        send_email_through(
            config=self.config, sendmail_callable=email_sender.send_mail, message=message
        )
Exemplo n.º 30
0
    def __setattr__(self, key: str, value: typing.Any) -> None:
        """
        Log-ready setter.

        Logs all configuration parameters except secret ones.
        """
        is_value_secret = False
        for secret in SECRET_ENDING_STR:
            if key.endswith(secret):
                is_value_secret = True

        if is_value_secret:
            logger.info(self, 'CONFIG: [ {} | <value not shown> ]'.format(key))
        else:
            logger.info(self, 'CONFIG: [ {} | {} ]'.format(key, value))

        self.__dict__[key] = value
Exemplo n.º 31
0
    def __init__(self, config: CFG, session: Session, current_user: User = None):
        """
        :param current_user: the user that has triggered the notification
        :return:
        """
        INotifier.__init__(self, config, session, current_user)
        logger.info(self, "Instantiating Email Notifier")

        self._user = current_user
        self.session = session
        self.config = config
        self._smtp_config = SmtpConfiguration(
            self.config.EMAIL__NOTIFICATION__SMTP__SERVER,
            self.config.EMAIL__NOTIFICATION__SMTP__PORT,
            self.config.EMAIL__NOTIFICATION__SMTP__USER,
            self.config.EMAIL__NOTIFICATION__SMTP__PASSWORD,
        )
Exemplo n.º 32
0
    def _notify_emitter(
        self,
        emitter: UserInContext,
        workspace_in_context: WorkspaceInContext,
        upload_permission_receivers: typing.List[UploadPermissionInContext],
        upload_permission_password: str,
        translator: Translator,
    ) -> Message:
        logger.info(
            self,
            'preparing email to user "{}" about upload_permission on workspace "{}" info created'
            .format(emitter.user_id, workspace_in_context.workspace_id),
        )
        translated_subject = translator.get_translation(
            self.config.
            EMAIL__NOTIFICATION__UPLOAD_PERMISSION_TO_EMITTER__SUBJECT)
        subject = translated_subject.format(
            website_title=self.config.WEBSITE__TITLE,
            nb_receivers=len(upload_permission_receivers),
            workspace_name=workspace_in_context.label,
        )
        from_header = self._get_sender()
        to_header = EmailAddress(emitter.display_name, emitter.email)
        html_template_file_path = (
            self.config.
            EMAIL__NOTIFICATION__UPLOAD_PERMISSION_TO_EMITTER__TEMPLATE__HTML)
        context = {
            "emitter": emitter,
            "workspace": workspace_in_context,
            "upload_permission_receivers": upload_permission_receivers,
            "upload_permission_password": upload_permission_password,
        }
        body_html = self._render_template(
            mako_template_filepath=html_template_file_path,
            context=context,
            translator=translator)

        message = EmailNotificationMessage(
            subject=subject,
            from_header=from_header,
            to_header=to_header,
            body_html=body_html,
            lang=translator.default_lang,
        )
        return message
Exemplo n.º 33
0
    def _notify_emitter(
        self,
        emitter: User,
        shared_content: ContentInContext,
        content_share_receivers: typing.List[ContentShareInContext],
        share_password: str,
        translator: Translator,
    ) -> Message:
        logger.info(
            self,
            'preparing email to user "{}" about share on content "{}" info created'.format(
                emitter.user_id, shared_content.content_id
            ),
        )
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__SHARE_CONTENT_TO_EMITTER__SUBJECT
        )
        subject = translated_subject.format(
            website_title=self.config.WEBSITE__TITLE,
            content_filename=shared_content.filename,
            nb_receivers=len(content_share_receivers),
        )
        from_header = self._get_sender()
        to_header = EmailAddress(emitter.display_name, emitter.email)
        html_template_file_path = (
            self.config.EMAIL__NOTIFICATION__SHARE_CONTENT_TO_EMITTER__TEMPLATE__HTML
        )
        context = {
            "emitter": emitter,
            "shared_content": shared_content,
            "content_share_receivers": content_share_receivers,
            "share_password": share_password,
        }
        body_html = self._render_template(
            mako_template_filepath=html_template_file_path, context=context, translator=translator
        )
        message = EmailNotificationMessage(
            subject=subject,
            from_header=from_header,
            to_header=to_header,
            body_html=body_html,
            lang=translator.default_lang,
        )

        return message
Exemplo n.º 34
0
def send_email_through(
        config: CFG,
        sendmail_callable: typing.Callable[[Message], None],
        message: Message,
) -> None:
    """
    Send mail encapsulation to send it in async or sync mode.

    TODO BS 20170126: A global mail/sender management should be a good
                      thing. Actually, this method is an fast solution.
    :param config: system configuration
    :param sendmail_callable: A callable who get message on first parameter
    :param message: The message who have to be sent
    """
    if config.EMAIL_PROCESSING_MODE == config.CST.SYNC:
        logger.info(
            send_email_through,
            'send email to {} synchronously'.format(
                message['To']
            )
        )
        sendmail_callable(message)
    elif config.EMAIL_PROCESSING_MODE == config.CST.ASYNC:
        logger.info(
            send_email_through,
            'send email to {} asynchronously:'
            'mail stored in queue in wait for a'
            'mail_notifier daemon'.format(
                message['To']
            )
        )
        redis_connection = get_redis_connection(config)
        queue = get_rq_queue(redis_connection, 'mail_sender')
        queue.enqueue(sendmail_callable, message)
    else:
        raise NotImplementedError(
            'Mail sender processing mode {} is not implemented'.format(
                config.EMAIL_PROCESSING_MODE,
            )
        )
Exemplo n.º 35
0
    def connect(self):
        if not self._smtp_connection:
            log = 'Connecting to SMTP server {}'
            logger.info(self, log.format(self._smtp_config.server))
            # TODO - G.M - 2019-01-29 - Support for SMTP SSL-only port connection
            # using smtplib.SMTP_SSL
            self._smtp_connection = smtplib.SMTP(
                self._smtp_config.server,
                self._smtp_config.port
            )
            self._smtp_connection.ehlo()

            if self._smtp_config.login:
                try:
                    starttls_result = self._smtp_connection.starttls()

                    if starttls_result[0] == 220:
                        logger.info(self, 'SMTP Start TLS OK')

                    log = 'SMTP Start TLS return code: {} with message: {}'
                    logger.debug(
                        self,
                        log.format(
                            starttls_result[0],
                            starttls_result[1].decode('utf-8')
                        )
                    )
                except smtplib.SMTPResponseException as exc:
                    log = 'SMTP start TLS return error code: {} with message: {}'
                    logger.error(
                        self,
                        log.format(
                            exc.smtp_code,
                            exc.smtp_error.decode('utf-8')
                        )
                    )
                except Exception as exc:
                    log = 'Unexpected exception during SMTP start TLS process: {}'
                    logger.error(self, log.format(exc.__str__()))
                    logger.error(self, traceback.format_exc())


            if self._smtp_config.login:
                try:
                    login_res = self._smtp_connection.login(
                        self._smtp_config.login,
                        self._smtp_config.password
                    )

                    if login_res[0] == 235:
                        logger.info(self, 'SMTP Authentication Successful')
                    if login_res[0] == 503:
                        logger.info(self, 'SMTP Already Authenticated')

                    log = 'SMTP login return code: {} with message: {}'
                    logger.debug(
                        self,
                        log.format(
                            login_res[0],
                            login_res[1].decode('utf-8')
                        )
                    )
                except smtplib.SMTPAuthenticationError as exc:

                    log = 'SMTP auth return error code: {} with message: {}'
                    logger.error(
                        self,
                        log.format(
                            exc.smtp_code,
                            exc.smtp_error.decode('utf-8')
                        )
                    )
                    logger.error(self,
                                 'check your auth params combinaison '
                                 '(login/password) for SMTP'
                    )
                except smtplib.SMTPResponseException as exc:
                    log = 'SMTP login return error code: {} with message: {}'
                    logger.error(
                        self,
                        log.format(
                            exc.smtp_code,
                            exc.smtp_error.decode('utf-8')
                        )
                    )
                except Exception as exc:
                    log = 'Unexpected exception during SMTP login {}'
                    logger.error(self, log.format(exc.__str__()))
                    logger.error(self, traceback.format_exc())
Exemplo n.º 36
0
 def notify_content_update(self, content: Content):
     type(self).send_count += 1
     logger.info(
         self,
         'Fake notifier, do not send notification for update of content {}'.format(content.content_id)  # nopep8
     )
Exemplo n.º 37
0
 def disconnect(self):
     if self._smtp_connection:
         log = 'Disconnecting from SMTP server {}'
         logger.info(self, log.format(self._smtp_config.server))
         self._smtp_connection.quit()
         logger.info(self, 'Connection closed.')