示例#1
0
def get_or_create_contact(contact_from_header: str, mail_from: str,
                          alias: Alias) -> Contact:
    """
    contact_from_header is the RFC 2047 format FROM header
    """
    # contact_from_header can be None, use mail_from in this case instead
    contact_from_header = contact_from_header or mail_from

    # force convert header to string, sometimes contact_from_header is Header object
    contact_from_header = str(contact_from_header)

    contact_name, contact_email = parseaddr_unicode(contact_from_header)
    if not contact_email:
        # From header is wrongly formatted, try with mail_from
        LOG.warning("From header is empty, parse mail_from %s %s", mail_from,
                    alias)
        contact_name, contact_email = parseaddr_unicode(mail_from)
        if not contact_email:
            LOG.exception(
                "Cannot parse contact from from_header:%s, mail_from:%s",
                contact_from_header,
                mail_from,
            )

    contact = Contact.get_by(alias_id=alias.id, website_email=contact_email)
    if contact:
        if contact.name != contact_name:
            LOG.d(
                "Update contact %s name %s to %s",
                contact,
                contact.name,
                contact_name,
            )
            contact.name = contact_name
            db.session.commit()
    else:
        LOG.debug(
            "create contact for alias %s and contact %s",
            alias,
            contact_from_header,
        )

        reply_email = generate_reply_email()

        try:
            contact = Contact.create(
                user_id=alias.user_id,
                alias_id=alias.id,
                website_email=contact_email,
                name=contact_name,
                reply_email=reply_email,
            )
            db.session.commit()
        except IntegrityError:
            LOG.warning("Contact %s %s already exist", alias, contact_email)
            db.session.rollback()
            contact = Contact.get_by(alias_id=alias.id,
                                     website_email=contact_email)

    return contact
示例#2
0
    async def handle_DATA(self, server, session, envelope):
        LOG.debug(">>> New message <<<")

        LOG.debug("Mail from %s", envelope.mail_from)
        LOG.debug("Rcpt to %s", envelope.rcpt_tos)
        message_data = envelope.content.decode("utf8", errors="replace")

        if POSTFIX_SUBMISSION_TLS:
            smtp = SMTP(POSTFIX_SERVER, 587)
            smtp.starttls()
        else:
            smtp = SMTP(POSTFIX_SERVER, 25)

        msg = Parser(policy=SMTPUTF8).parsestr(message_data)

        for rcpt_to in envelope.rcpt_tos:
            # Reply case
            # recipient starts with "reply+" or "ra+" (ra=reverse-alias) prefix
            if rcpt_to.startswith("reply+") or rcpt_to.startswith("ra+"):
                LOG.debug("Reply phase")
                app = new_app()

                with app.app_context():
                    return handle_reply(envelope, smtp, msg, rcpt_to)
            else:  # Forward case
                LOG.debug("Forward phase")
                app = new_app()

                with app.app_context():
                    return handle_forward(envelope, smtp, msg, rcpt_to)
示例#3
0
    async def handle_DATA(self, server, session, envelope):
        LOG.debug(">>> New message <<<")

        LOG.debug("Mail from %s", envelope.mail_from)
        LOG.debug("Rcpt to %s", envelope.rcpt_tos)
        message_data = envelope.content.decode("utf8", errors="replace")

        # Only when debug
        # LOG.debug("Message data:\n")
        # LOG.debug(message_data)

        # host IP, setup via Docker network
        smtp = SMTP(POSTFIX_SERVER, 25)
        msg = Parser(policy=SMTPUTF8).parsestr(message_data)

        rcpt_to = envelope.rcpt_tos[0].lower()

        # Reply case
        # reply+ or ra+ (reverse-alias) prefix
        if rcpt_to.startswith("reply+") or rcpt_to.startswith("ra+"):
            LOG.debug("Reply phase")
            app = new_app()

            with app.app_context():
                return self.handle_reply(envelope, smtp, msg)
        else:  # Forward case
            LOG.debug("Forward phase")
            app = new_app()

            with app.app_context():
                return self.handle_forward(envelope, smtp, msg)
示例#4
0
文件: register.py 项目: havedill/app
def register():
    if current_user.is_authenticated:
        LOG.d("user is already authenticated, redirect to dashboard")
        flash("You are already logged in", "warning")
        return redirect(url_for("dashboard.index"))

    form = RegisterForm(request.form)
    next_url = request.args.get("next")

    if form.validate_on_submit():
        email = form.email.data
        if not can_be_used_as_personal_email(email):
            flash(
                "You cannot use this email address as your personal inbox.",
                "error",
            )
        else:
            user = User.filter_by(email=email).first()

            if user:
                flash(f"Email {form.email.data} already exists", "warning")
            else:
                LOG.debug("create user %s", form.email.data)
                user = User.create(
                    email=form.email.data.lower(),
                    name="",
                    password=form.password.data,
                )
                db.session.commit()

                send_activation_email(user, next_url)

                return render_template("auth/register_waiting_activation.html")

    return render_template("auth/register.html", form=form, next_url=next_url)
示例#5
0
def notify_manual_sub_end():
    for manual_sub in ManualSubscription.query.all():
        need_reminder = False
        if arrow.now().shift(days=14) > manual_sub.end_at > arrow.now().shift(
                days=13):
            need_reminder = True
        elif arrow.now().shift(days=4) > manual_sub.end_at > arrow.now().shift(
                days=3):
            need_reminder = True

        if need_reminder:
            user = manual_sub.user
            LOG.debug("Remind user %s that their manual sub is ending soon",
                      user)
            send_email(
                user.email,
                f"Your subscription will end soon {user.name}",
                render(
                    "transactional/manual-subscription-end.txt",
                    name=user.name,
                    user=user,
                    manual_sub=manual_sub,
                ),
                render(
                    "transactional/manual-subscription-end.html",
                    name=user.name,
                    user=user,
                    manual_sub=manual_sub,
                ),
            )

    extend_subscription_url = URL + "/dashboard/coinbase_checkout"
    for coinbase_subscription in CoinbaseSubscription.query.all():
        need_reminder = False
        if (arrow.now().shift(days=14) > coinbase_subscription.end_at >
                arrow.now().shift(days=13)):
            need_reminder = True
        elif (arrow.now().shift(days=4) > coinbase_subscription.end_at >
              arrow.now().shift(days=3)):
            need_reminder = True

        if need_reminder:
            user = coinbase_subscription.user
            LOG.debug(
                "Remind user %s that their coinbase subscription is ending soon",
                user)
            send_email(
                user.email,
                "Your SimpleLogin subscription will end soon",
                render(
                    "transactional/coinbase/reminder-subscription.txt",
                    coinbase_subscription=coinbase_subscription,
                    extend_subscription_url=extend_subscription_url,
                ),
                render(
                    "transactional/coinbase/reminder-subscription.html",
                    coinbase_subscription=coinbase_subscription,
                    extend_subscription_url=extend_subscription_url,
                ),
            )
示例#6
0
def delete_alias(alias: Alias, user: User):
    """
    Delete an alias and add it to either global or domain trash
    Should be used instead of Alias.delete, DomainDeletedAlias.create, DeletedAlias.create
    """
    # save deleted alias to either global or domain trash
    if alias.custom_domain_id:
        if not DomainDeletedAlias.get_by(
            email=alias.email, domain_id=alias.custom_domain_id
        ):
            LOG.debug("add %s to domain %s trash", alias, alias.custom_domain_id)
            db.session.add(
                DomainDeletedAlias(
                    user_id=user.id, email=alias.email, domain_id=alias.custom_domain_id
                )
            )
            db.session.commit()
    else:
        if not DeletedAlias.get_by(email=alias.email):
            LOG.d("add %s to global trash", alias)
            db.session.add(DeletedAlias(email=alias.email))
            db.session.commit()

    Alias.query.filter(Alias.id == alias.id).delete()
    db.session.commit()
示例#7
0
def mfa():
    # passed from login page
    user_id = session[MFA_USER_ID]
    user = User.get(user_id)

    if not user.enable_otp:
        raise Exception(
            "Only user with MFA enabled should go to this page. %s", user)

    otp_token_form = OtpTokenForm()
    next_url = request.args.get("next")

    if otp_token_form.validate_on_submit():
        totp = pyotp.TOTP(user.otp_secret)

        token = otp_token_form.token.data

        if totp.verify(token):
            del session[MFA_USER_ID]

            login_user(user)
            flash(f"Welcome back {user.name}!")

            # User comes to login page from another page
            if next_url:
                LOG.debug("redirect user to %s", next_url)
                return redirect(next_url)
            else:
                LOG.debug("redirect user to dashboard")
                return redirect(url_for("dashboard.index"))

        else:
            flash("Incorrect token", "warning")

    return render_template("auth/mfa.html", otp_token_form=otp_token_form)
示例#8
0
文件: apple.py 项目: nibblehole/app
def apple_process_payment():
    """
    Process payment
    Input:
        receipt_data: in body
        (optional) is_macapp: in body
    Output:
        200 of the payment is successful, i.e. user is upgraded to premium

    """
    LOG.debug("request for /apple/process_payment")
    user = g.user
    data = request.get_json()
    receipt_data = data.get("receipt_data")
    is_macapp = "is_macapp" in data

    if is_macapp:
        password = MACAPP_APPLE_API_SECRET
    else:
        password = APPLE_API_SECRET

    apple_sub = verify_receipt(receipt_data, user, password)
    if apple_sub:
        return jsonify(ok=True), 200

    return jsonify(ok=False), 400
示例#9
0
文件: cron.py 项目: arnobferdous/app
def notify_manual_sub_end():
    for manual_sub in ManualSubscription.query.all():
        need_reminder = False
        if arrow.now().shift(days=14) > manual_sub.end_at > arrow.now().shift(days=13):
            need_reminder = True
        elif arrow.now().shift(days=4) > manual_sub.end_at > arrow.now().shift(days=3):
            need_reminder = True

        if need_reminder:
            user = manual_sub.user
            LOG.debug("Remind user %s that their manual sub is ending soon", user)
            send_email(
                user.email,
                f"Your trial will end soon {user.name}",
                render(
                    "transactional/manual-subscription-end.txt",
                    name=user.name,
                    user=user,
                    manual_sub=manual_sub,
                ),
                render(
                    "transactional/manual-subscription-end.html",
                    name=user.name,
                    user=user,
                    manual_sub=manual_sub,
                ),
            )
示例#10
0
def create_db():
    if not database_exists(DB_URI):
        LOG.debug("db not exist, create database")
        create_database(DB_URI)

        # Create all tables
        # Use flask-migrate instead of db.create_all()
        flask_migrate.upgrade()
示例#11
0
文件: models.py 项目: cuongpianna/app
def generate_oauth_client_id(client_name) -> str:
    oauth_client_id = convert_to_id(client_name) + "-" + random_string()

    # check that the client does not exist yet
    if not Client.get_by(oauth_client_id=oauth_client_id):
        LOG.debug("generate oauth_client_id %s", oauth_client_id)
        return oauth_client_id

    # Rerun the function
    LOG.warning("client_id %s already exists, generate a new client_id",
                oauth_client_id)
    return generate_oauth_client_id(client_name)
示例#12
0
def auth_activate():
    """
    User enters the activation code to confirm their account.
    Input:
        email
        code
    Output:
        200: user account is now activated, user can login now
        400: wrong email, code
        410: wrong code too many times

    """
    data = request.get_json()
    if not data:
        return jsonify(error="request body cannot be empty"), 400

    email = sanitize_email(data.get("email"))
    code = data.get("code")

    user = User.get_by(email=email)

    # do not use a different message to avoid exposing existing email
    if not user or user.activated:
        # Trigger rate limiter
        g.deduct_limit = True
        return jsonify(error="Wrong email or code"), 400

    account_activation = AccountActivation.get_by(user_id=user.id)
    if not account_activation:
        # Trigger rate limiter
        g.deduct_limit = True
        return jsonify(error="Wrong email or code"), 400

    if account_activation.code != code:
        # decrement nb tries
        account_activation.tries -= 1
        db.session.commit()
        # Trigger rate limiter
        g.deduct_limit = True

        if account_activation.tries == 0:
            AccountActivation.delete(account_activation.id)
            db.session.commit()
            return jsonify(error="Too many wrong tries"), 410

        return jsonify(error="Wrong email or code"), 400

    LOG.debug("activate user %s", user)
    user.activated = True
    AccountActivation.delete(account_activation.id)
    db.session.commit()

    return jsonify(msg="Account is activated, user can login now"), 200
示例#13
0
def handle(envelope: Envelope, smtp: SMTP) -> str:
    """Return SMTP status"""
    # unsubscribe request
    if UNSUBSCRIBER and envelope.rcpt_tos == [UNSUBSCRIBER]:
        LOG.d("Handle unsubscribe request from %s", envelope.mail_from)
        return handle_unsubscribe(envelope)

    # emails sent to sender. Probably bounce emails
    if SENDER and envelope.rcpt_tos == [SENDER]:
        LOG.d("Handle email sent to sender from %s", envelope.mail_from)
        return handle_sender_email(envelope)

    # Whether it's necessary to apply greylisting
    if greylisting_needed(envelope.mail_from, envelope.rcpt_tos):
        LOG.warning("Grey listing applied for %s %s", envelope.mail_from,
                    envelope.rcpt_tos)
        return "421 SL Retry later"

    # result of all deliveries
    # each element is a couple of whether the delivery is successful and the smtp status
    res: [(bool, str)] = []

    for rcpt_to in envelope.rcpt_tos:
        msg = email.message_from_bytes(envelope.original_content)

        # Reply case
        # recipient starts with "reply+" or "ra+" (ra=reverse-alias) prefix
        if rcpt_to.startswith("reply+") or rcpt_to.startswith("ra+"):
            LOG.debug(">>> Reply phase %s(%s) -> %s", envelope.mail_from,
                      msg["From"], rcpt_to)
            is_delivered, smtp_status = handle_reply(envelope, smtp, msg,
                                                     rcpt_to)
            res.append((is_delivered, smtp_status))
        else:  # Forward case
            LOG.debug(
                ">>> Forward phase %s(%s) -> %s",
                envelope.mail_from,
                msg["From"],
                rcpt_to,
            )
            for is_delivered, smtp_status in handle_forward(
                    envelope, smtp, msg, rcpt_to):
                res.append((is_delivered, smtp_status))

    for (is_success, smtp_status) in res:
        # Consider all deliveries successful if 1 delivery is successful
        if is_success:
            return smtp_status

    # Failed delivery for all, return the first failure
    return res[0][1]
示例#14
0
def handle_batch_import(batch_import: BatchImport):
    user = batch_import.user

    batch_import.processed = True
    db.session.commit()

    LOG.debug("Start batch import for %s %s", batch_import, user)
    file_url = s3.get_url(batch_import.file.path)

    LOG.d("Download file %s from %s", batch_import.file, file_url)
    r = requests.get(file_url)
    lines = [l.decode() for l in r.iter_lines()]
    reader = csv.DictReader(lines)

    for row in reader:
        try:
            full_alias = row["alias"].lower().strip().replace(" ", "")
            note = row["note"]
        except KeyError:
            LOG.warning("Cannot parse row %s", row)
            continue

        alias_domain = get_email_domain_part(full_alias)
        custom_domain = CustomDomain.get_by(domain=alias_domain)

        if (
            not custom_domain
            or not custom_domain.verified
            or custom_domain.user_id != user.id
        ):
            LOG.debug("domain %s can't be used %s", alias_domain, user)
            continue

        if (
            Alias.get_by(email=full_alias)
            or DeletedAlias.get_by(email=full_alias)
            or DomainDeletedAlias.get_by(email=full_alias)
        ):
            LOG.d("alias already used %s", full_alias)
            continue

        alias = Alias.create(
            user_id=user.id,
            email=full_alias,
            note=note,
            mailbox_id=user.default_mailbox_id,
            custom_domain_id=custom_domain.id,
            batch_import_id=batch_import.id,
        )
        db.session.commit()
        LOG.d("Create %s", alias)
示例#15
0
def recovery_route():
    # passed from login page
    user_id = session.get(MFA_USER_ID)

    # user access this page directly without passing by login page
    if not user_id:
        flash("Unknown error, redirect back to main page", "warning")
        return redirect(url_for("auth.login"))

    user = User.get(user_id)

    if not user.two_factor_authentication_enabled():
        flash("Only user with MFA enabled should go to this page", "warning")
        return redirect(url_for("auth.login"))

    recovery_form = RecoveryForm()
    next_url = request.args.get("next")

    if recovery_form.validate_on_submit():
        code = recovery_form.code.data
        recovery_code = RecoveryCode.get_by(user_id=user.id, code=code)

        if recovery_code:
            if recovery_code.used:
                # Trigger rate limiter
                g.deduct_limit = True
                flash("Code already used", "error")
            else:
                del session[MFA_USER_ID]

                login_user(user)
                flash(f"Welcome back!", "success")

                recovery_code.used = True
                recovery_code.used_at = arrow.now()
                db.session.commit()

                # User comes to login page from another page
                if next_url:
                    LOG.debug("redirect user to %s", next_url)
                    return redirect(next_url)
                else:
                    LOG.debug("redirect user to dashboard")
                    return redirect(url_for("dashboard.index"))
        else:
            # Trigger rate limiter
            g.deduct_limit = True
            flash("Incorrect code", "error")

    return render_template("auth/recovery.html", recovery_form=recovery_form)
示例#16
0
def activate():
    if current_user.is_authenticated:
        return (
            render_template("auth/activate.html", error="You are already logged in"),
            400,
        )

    code = request.args.get("code")

    activation_code: ActivationCode = ActivationCode.get_by(code=code)

    if not activation_code:
        # Trigger rate limiter
        g.deduct_limit = True
        return (
            render_template(
                "auth/activate.html", error="Activation code cannot be found"
            ),
            400,
        )

    if activation_code.is_expired():
        return (
            render_template(
                "auth/activate.html",
                error="Activation code was expired",
                show_resend_activation=True,
            ),
            400,
        )

    user = activation_code.user
    user.activated = True
    login_user(user)
    email_utils.send_welcome_email(user)

    # activation code is to be used only once
    ActivationCode.delete(activation_code.id)
    db.session.commit()

    flash("Your account has been activated", "success")

    # The activation link contains the original page, for ex authorize page
    if "next" in request.args:
        next_url = request.args.get("next")
        LOG.debug("redirect user to %s", next_url)
        return redirect(next_url)
    else:
        LOG.debug("redirect user to dashboard")
        return redirect(url_for("dashboard.index"))
示例#17
0
文件: models.py 项目: dttr278/app
    def get_user_info(self) -> dict:
        """return user info according to client scope
        Return dict with key being scope name. For now all the fields are the same for all clients:

        {
          "client": "Demo",
          "email": "*****@*****.**",
          "email_verified": true,
          "id": 1,
          "name": "Son GM",
          "avatar_url": "http://s3..."
        }

        """
        res = {
            "id": self.id,
            "client": self.client.name,
            "email_verified": True,
            "sub": str(self.id),
        }

        for scope in self.client.get_scopes():
            if scope == Scope.NAME:
                if self.name:
                    res[Scope.NAME.value] = self.name
                else:
                    res[Scope.NAME.value] = self.user.name
            elif scope == Scope.AVATAR_URL:
                if self.user.profile_picture_id:
                    if self.default_avatar:
                        res[Scope.AVATAR_URL.value] = URL + "/static/default-avatar.png"
                    else:
                        res[Scope.AVATAR_URL.value] = self.user.profile_picture.get_url(
                            AVATAR_URL_EXPIRATION
                        )
                else:
                    res[Scope.AVATAR_URL.value] = None
            elif scope == Scope.EMAIL:
                # Use generated email
                if self.gen_email_id:
                    LOG.debug(
                        "Use gen email for user %s, client %s", self.user, self.client
                    )
                    res[Scope.EMAIL.value] = self.gen_email.email
                # Use user original email
                else:
                    res[Scope.EMAIL.value] = self.user.email

        return res
示例#18
0
    def after_request(res):
        # not logging /static call
        if (not request.path.startswith("/static")
                and not request.path.startswith("/admin/static")
                and not request.path.startswith("/_debug_toolbar")):
            LOG.debug(
                "%s %s %s %s %s",
                request.remote_addr,
                request.method,
                request.path,
                request.args,
                res.status_code,
            )

        return res
示例#19
0
    async def handle_DATA(self, server, session, envelope: Envelope):
        LOG.debug(
            "===>> New message, mail from %s, rctp tos %s ",
            envelope.mail_from,
            envelope.rcpt_tos,
        )

        if POSTFIX_SUBMISSION_TLS:
            smtp = SMTP(POSTFIX_SERVER, 587)
            smtp.starttls()
        else:
            smtp = SMTP(POSTFIX_SERVER, 25)

        app = new_app()
        with app.app_context():
            return handle(envelope, smtp)
示例#20
0
def should_disable(alias: Alias) -> bool:
    """Disable an alias if it has more than 5 bounces in the last 24h"""
    # Bypass the bounce rule
    if alias.cannot_be_disabled:
        LOG.warning("%s cannot be disabled", alias)
        return False

    yesterday = arrow.now().shift(days=-1)
    nb_bounced_last_24h = (
        db.session.query(EmailLog)
        .join(Contact, EmailLog.contact_id == Contact.id)
        .filter(
            EmailLog.bounced.is_(True),
            EmailLog.is_reply.is_(False),
            EmailLog.created_at > yesterday,
        )
        .filter(Contact.alias_id == alias.id)
        .count()
    )
    # if more than 10 bounces in 24h -> disable alias
    if nb_bounced_last_24h > 10:
        LOG.debug("more than 10 bounces in the last 24h on %s", alias)
        return True

    # if between 5-10 bounces but has bounces last week -> disable alias
    elif nb_bounced_last_24h > 5:
        one_week_ago = arrow.now().shift(days=-8)
        nb_bounced_7d_1d = (
            db.session.query(EmailLog)
            .join(Contact, EmailLog.contact_id == Contact.id)
            .filter(
                EmailLog.bounced.is_(True),
                EmailLog.is_reply.is_(False),
                EmailLog.created_at > one_week_ago,
                EmailLog.created_at < yesterday,
            )
            .filter(Contact.alias_id == alias.id)
            .count()
        )
        if nb_bounced_7d_1d > 1:
            LOG.debug(
                "more than 5 bounces in the last 24h and more than 1 bounces in the last 7 days on %s",
                alias,
            )
            return True

    return False
示例#21
0
文件: mfa.py 项目: n89nanda/app
def mfa():
    # passed from login page
    user_id = session.get(MFA_USER_ID)

    # user access this page directly without passing by login page
    if not user_id:
        flash("Unknown error, redirect back to main page", "warning")
        return redirect(url_for("auth.login"))

    user = User.get(user_id)

    if not (user and user.enable_otp):
        flash("Only user with MFA enabled should go to this page", "warning")
        return redirect(url_for("auth.login"))

    otp_token_form = OtpTokenForm()
    next_url = request.args.get("next")

    if otp_token_form.validate_on_submit():
        totp = pyotp.TOTP(user.otp_secret)

        token = otp_token_form.token.data

        if totp.verify(token):
            del session[MFA_USER_ID]

            login_user(user)
            flash(f"Welcome back {user.name}!")

            # User comes to login page from another page
            if next_url:
                LOG.debug("redirect user to %s", next_url)
                return redirect(next_url)
            else:
                LOG.debug("redirect user to dashboard")
                return redirect(url_for("dashboard.index"))

        else:
            flash("Incorrect token", "warning")

    return render_template(
        "auth/mfa.html",
        otp_token_form=otp_token_form,
        enable_fido=(user.fido_enabled()),
    )
示例#22
0
def login():
    next_url = request.args.get("next")

    if current_user.is_authenticated:
        if next_url:
            LOG.debug("user is already authenticated, redirect to %s",
                      next_url)
            return redirect(next_url)
        else:
            LOG.d("user is already authenticated, redirect to dashboard")
            return redirect(url_for("dashboard.index"))

    form = LoginForm(request.form)

    show_resend_activation = False

    if form.validate_on_submit():
        user = User.filter_by(email=sanitize_email(form.email.data)).first()

        if not user or not user.check_password(form.password.data):
            # Trigger rate limiter
            g.deduct_limit = True
            form.password.data = None
            flash("Email or password incorrect", "error")
        elif user.disabled:
            flash(
                "Your account is disabled. Please contact SimpleLogin team to re-enable your account.",
                "error",
            )
        elif not user.activated:
            show_resend_activation = True
            flash(
                "Please check your inbox for the activation email. You can also have this email re-sent",
                "error",
            )
        else:
            return after_login(user, next_url)

    return render_template(
        "auth/login.html",
        form=form,
        next_url=next_url,
        show_resend_activation=show_resend_activation,
    )
示例#23
0
文件: auth.py 项目: n89nanda/app
def auth_register():
    """
    User signs up - will need to activate their account with an activation code.
    Input:
        email
        password
    Output:
        200: user needs to confirm their account

    """
    data = request.get_json()
    if not data:
        return jsonify(error="request body cannot be empty"), 400

    email = data.get("email").strip().lower()
    password = data.get("password")

    if DISABLE_REGISTRATION:
        return jsonify(error="registration is closed"), 400
    if not email_domain_can_be_used_as_mailbox(email) or personal_email_already_used(
        email
    ):
        return jsonify(error=f"cannot use {email} as personal inbox"), 400

    if not password or len(password) < 8:
        return jsonify(error="password too short"), 400

    LOG.debug("create user %s", email)
    user = User.create(email=email, name="", password=password)
    db.session.flush()

    # create activation code
    code = "".join([str(random.randint(0, 9)) for _ in range(6)])
    AccountActivation.create(user_id=user.id, code=code)
    db.session.commit()

    send_email(
        email,
        f"Just one more step to join SimpleLogin",
        render("transactional/code-activation.txt", code=code),
        render("transactional/code-activation.html", code=code),
    )

    return jsonify(msg="User needs to confirm their account"), 200
示例#24
0
def get_or_create_forward_email(
    website_from_header: str, gen_email: GenEmail
) -> ForwardEmail:
    """
    website_from_header can be the full-form email, i.e. "First Last <*****@*****.**>"
    """
    website_email = get_email_part(website_from_header)
    forward_email = ForwardEmail.get_by(
        gen_email_id=gen_email.id, website_email=website_email
    )
    if forward_email:
        # update the website_from if needed
        if forward_email.website_from != website_from_header:
            LOG.d("Update From header for %s", forward_email)
            forward_email.website_from = website_from_header
            db.session.commit()
    else:
        LOG.debug(
            "create forward email for alias %s and website email %s",
            gen_email,
            website_from_header,
        )

        # generate a reply_email, make sure it is unique
        # not use while loop to avoid infinite loop
        reply_email = f"reply+{random_string(30)}@{EMAIL_DOMAIN}"
        for _ in range(1000):
            if not ForwardEmail.get_by(reply_email=reply_email):
                # found!
                break
            reply_email = f"reply+{random_string(30)}@{EMAIL_DOMAIN}"

        forward_email = ForwardEmail.create(
            gen_email_id=gen_email.id,
            website_email=website_email,
            website_from=website_from_header,
            reply_email=reply_email,
        )
        db.session.commit()

    return forward_email
示例#25
0
文件: register.py 项目: n89nanda/app
def register():
    if current_user.is_authenticated:
        LOG.d("user is already authenticated, redirect to dashboard")
        flash("You are already logged in", "warning")
        return redirect(url_for("dashboard.index"))

    if config.DISABLE_REGISTRATION:
        flash("Registration is closed", "error")
        return redirect(url_for("auth.login"))

    form = RegisterForm(request.form)
    next_url = request.args.get("next")

    if form.validate_on_submit():
        email = form.email.data.strip().lower()
        if not email_domain_can_be_used_as_mailbox(email):
            flash("You cannot use this email address as your personal inbox.",
                  "error")
        else:
            if personal_email_already_used(email):
                flash(f"Email {email} already used", "error")
            else:
                LOG.debug("create user %s", form.email.data)
                user = User.create(
                    email=email,
                    name="",
                    password=form.password.data,
                    referral=get_referral(),
                )
                db.session.commit()

                try:
                    send_activation_email(user, next_url)
                except:
                    flash("Invalid email, are you sure the email is correct?",
                          "error")
                    return redirect(url_for("auth.register"))

                return render_template("auth/register_waiting_activation.html")

    return render_template("auth/register.html", form=form, next_url=next_url)
示例#26
0
文件: models.py 项目: cuongpianna/app
def generate_email(scheme: int = AliasGeneratorEnum.word.value,
                   in_hex: bool = False) -> str:
    """generate an email address that does not exist before
    :param scheme: int, value of AliasGeneratorEnum, indicate how the email is generated
    :type in_hex: bool, if the generate scheme is uuid, is hex favorable?
    """
    if scheme == AliasGeneratorEnum.uuid.value:
        name = uuid.uuid4().hex if in_hex else uuid.uuid4().__str__()
        random_email = name + "@" + EMAIL_DOMAIN
    else:
        random_email = random_words() + "@" + EMAIL_DOMAIN

    # check that the client does not exist yet
    if not GenEmail.get_by(email=random_email) and not DeletedAlias.get_by(
            email=random_email):
        LOG.debug("generate email %s", random_email)
        return random_email

    # Rerun the function
    LOG.warning("email %s already exists, generate a new email", random_email)
    return generate_email(scheme=scheme, in_hex=in_hex)
示例#27
0
def after_login(user, next_url):
    """
    Redirect to the correct page after login.
    If user enables MFA: redirect user to MFA page
    Otherwise redirect to dashboard page if no next_url
    """
    if user.fido_enabled():
        # Use the same session for FIDO so that we can easily
        # switch between these two 2FA option
        session[MFA_USER_ID] = user.id
        if next_url:
            return redirect(url_for("auth.fido", next=next_url))
        else:
            return redirect(url_for("auth.fido"))
    elif user.enable_otp:
        session[MFA_USER_ID] = user.id
        if next_url:
            return redirect(url_for("auth.mfa", next=next_url))
        else:
            return redirect(url_for("auth.mfa"))
    else:
        LOG.debug("log user %s in", user)
        login_user(user)

        # User comes to login page from another page
        if next_url:
            LOG.debug("redirect user to %s", next_url)
            return redirect(next_url)
        else:
            LOG.debug("redirect user to dashboard")
            return redirect(url_for("dashboard.index"))
示例#28
0
def enter_sudo():
    password_check_form = LoginForm()

    if password_check_form.validate_on_submit():
        password = password_check_form.password.data

        if current_user.check_password(password):
            session["sudo_time"] = int(time())

            # User comes to sudo page from another page
            next_url = request.args.get("next")
            if next_url:
                LOG.debug("redirect user to %s", next_url)
                return redirect(next_url)
            else:
                LOG.debug("redirect user to dashboard")
                return redirect(url_for("dashboard.index"))
        else:
            flash("Incorrect password", "warning")

    return render_template("dashboard/enter_sudo.html",
                           password_check_form=password_check_form)
示例#29
0
def batch_import_route():
    # only for users who have custom domains
    if not current_user.verified_custom_domains():
        flash("Alias batch import is only available for custom domains",
              "warning")

    batch_imports = BatchImport.query.filter_by(user_id=current_user.id).all()

    if request.method == "POST":
        alias_file = request.files["alias-file"]

        file_path = random_string(20) + ".csv"
        file = File.create(user_id=current_user.id, path=file_path)
        s3.upload_from_bytesio(file_path, alias_file)
        db.session.flush()
        LOG.d("upload file %s to s3 at %s", file, file_path)

        bi = BatchImport.create(user_id=current_user.id, file_id=file.id)
        db.session.flush()
        LOG.debug("Add a batch import job %s for %s", bi, current_user)

        # Schedule batch import job
        Job.create(
            name=JOB_BATCH_IMPORT,
            payload={"batch_import_id": bi.id},
            run_at=arrow.now(),
        )
        db.session.commit()

        flash(
            "The file has been uploaded successfully and the import will start shortly",
            "success",
        )

        return redirect(url_for("dashboard.batch_import_route"))

    return render_template("dashboard/batch_import.html",
                           batch_imports=batch_imports)
示例#30
0
def login():
    if current_user.is_authenticated:
        LOG.d("user is already authenticated, redirect to dashboard")
        return redirect(url_for("dashboard.index"))

    form = LoginForm(request.form)
    next_url = request.args.get("next")
    show_resend_activation = False

    if form.validate_on_submit():
        user = User.filter_by(email=form.email.data).first()

        if not user:
            flash("Email or password incorrect", "error")
        elif not user.check_password(form.password.data):
            flash("Email or password incorrect", "error")
        elif not user.activated:
            show_resend_activation = True
            flash(
                "Please check your inbox for the activation email. You can also have this email re-sent",
                "error",
            )
        else:
            LOG.debug("log user %s in", user)
            login_user(user)

            # User comes to login page from another page
            if next_url:
                LOG.debug("redirect user to %s", next_url)
                return redirect(next_url)
            else:
                LOG.debug("redirect user to dashboard")
                return redirect(url_for("dashboard.index"))

    return render_template(
        "auth/login.html",
        form=form,
        next_url=next_url,
        show_resend_activation=show_resend_activation,
    )