Add /api/v2/alias/custom/new

This commit is contained in:
Son NK 2020-05-02 16:22:17 +02:00
parent 72e9b52b29
commit d32669f515
3 changed files with 123 additions and 4 deletions

View file

@ -691,7 +691,7 @@ For ex:
} }
``` ```
#### POST /api/alias/custom/new #### POST /api/v2/alias/custom/new
Create a new custom alias. Create a new custom alias.
@ -700,7 +700,7 @@ Input:
- (Optional but recommended) `hostname` passed in query string - (Optional but recommended) `hostname` passed in query string
- Request Message Body in json (`Content-Type` is `application/json`) - Request Message Body in json (`Content-Type` is `application/json`)
- alias_prefix: string. The first part of the alias that user can choose. - alias_prefix: string. The first part of the alias that user can choose.
- alias_suffix: should be one of the suffixes returned in the `GET /api/v2/alias/options` endpoint. - signed_suffix: should be one of the suffixes returned in the `GET /api/v4/alias/options` endpoint.
- (Optional) note: alias note - (Optional) note: alias note
Output: Output:

View file

@ -1,11 +1,12 @@
from flask import g from flask import g
from flask import jsonify, request from flask import jsonify, request
from flask_cors import cross_origin from flask_cors import cross_origin
from itsdangerous import SignatureExpired
from app.api.base import api_bp, require_api_auth from app.api.base import api_bp, require_api_auth
from app.api.serializer import serialize_alias_info, get_alias_info from app.api.serializer import serialize_alias_info, get_alias_info
from app.config import MAX_NB_EMAIL_FREE_PLAN, ALIAS_DOMAINS from app.config import MAX_NB_EMAIL_FREE_PLAN, ALIAS_DOMAINS
from app.dashboard.views.custom_alias import verify_prefix_suffix from app.dashboard.views.custom_alias import verify_prefix_suffix, signer
from app.extensions import db from app.extensions import db
from app.log import LOG from app.log import LOG
from app.models import Alias, AliasUsedOn, User, CustomDomain from app.models import Alias, AliasUsedOn, User, CustomDomain
@ -39,7 +40,6 @@ def new_custom_alias():
400, 400,
) )
user_custom_domains = [cd.domain for cd in user.verified_custom_domains()]
hostname = request.args.get("hostname") hostname = request.args.get("hostname")
data = request.get_json() data = request.get_json()
@ -77,3 +77,81 @@ def new_custom_alias():
db.session.commit() db.session.commit()
return jsonify(alias=full_alias, **serialize_alias_info(get_alias_info(alias))), 201 return jsonify(alias=full_alias, **serialize_alias_info(get_alias_info(alias))), 201
@api_bp.route("/v2/alias/custom/new", methods=["POST"])
@cross_origin()
@require_api_auth
def new_custom_alias_v2():
"""
Create a new custom alias
Same as v1 but signed_suffix is actually the suffix with signature, e.g.
.random_word@SL.co.Xq19rQ.s99uWQ7jD1s5JZDZqczYI5TbNNU
Input:
alias_prefix, for ex "www_groupon_com"
signed_suffix, either .random_letters@simplelogin.co or @my-domain.com
optional "hostname" in args
optional "note"
Output:
201 if success
409 if the alias already exists
"""
user: User = g.user
if not user.can_create_new_alias():
LOG.d("user %s cannot create any custom alias", user)
return (
jsonify(
error="You have reached the limitation of a free account with the maximum of "
f"{MAX_NB_EMAIL_FREE_PLAN} aliases, please upgrade your plan to create more aliases"
),
400,
)
hostname = request.args.get("hostname")
data = request.get_json()
if not data:
return jsonify(error="request body cannot be empty"), 400
alias_prefix = data.get("alias_prefix", "").strip()
signed_suffix = data.get("signed_suffix", "").strip()
note = data.get("note")
alias_prefix = convert_to_id(alias_prefix)
# hypothesis: user will click on the button in the 300 secs
try:
alias_suffix = signer.unsign(signed_suffix, max_age=300).decode()
except SignatureExpired:
LOG.error("Alias creation time expired")
return jsonify(error="alias creation is expired, please try again"), 400
except Exception:
LOG.error("Alias suffix is tampered, user %s", user)
return jsonify(error="Tampered suffix"), 400
if not verify_prefix_suffix(user, alias_prefix, alias_suffix):
return jsonify(error="wrong alias prefix or suffix"), 400
full_alias = alias_prefix + alias_suffix
if Alias.get_by(email=full_alias):
LOG.d("full alias already used %s", full_alias)
return jsonify(error=f"alias {full_alias} already exists"), 409
alias = Alias.create(
user_id=user.id, email=full_alias, mailbox_id=user.default_mailbox_id, note=note
)
if alias_suffix.startswith("@"):
alias_domain = alias_suffix[1:]
if alias_domain not in ALIAS_DOMAINS:
domain = CustomDomain.get_by(domain=alias_domain)
LOG.d("set alias %s to domain %s", full_alias, domain)
alias.custom_domain_id = domain.id
db.session.commit()
if hostname:
AliasUsedOn.create(alias_id=alias.id, hostname=hostname, user_id=alias.user_id)
db.session.commit()
return jsonify(alias=full_alias, **serialize_alias_info(get_alias_info(alias))), 201

View file

@ -1,6 +1,7 @@
from flask import url_for from flask import url_for
from app.config import EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN from app.config import EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN
from app.dashboard.views.custom_alias import signer
from app.extensions import db from app.extensions import db
from app.models import User, ApiKey, Alias from app.models import User, ApiKey, Alias
from app.utils import random_word from app.utils import random_word
@ -98,3 +99,43 @@ def test_out_of_quota(flask_client):
assert r.json == { assert r.json == {
"error": "You have reached the limitation of a free account with the maximum of 3 aliases, please upgrade your plan to create more aliases" "error": "You have reached the limitation of a free account with the maximum of 3 aliases, please upgrade your plan to create more aliases"
} }
def test_success_v2(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
# create new alias with note
word = random_word()
suffix = f".{word}@{EMAIL_DOMAIN}"
suffix = signer.sign(suffix).decode()
r = flask_client.post(
url_for("api.new_custom_alias_v2", hostname="www.test.com"),
headers={"Authentication": api_key.code},
json={"alias_prefix": "prefix", "signed_suffix": suffix, "note": "test note",},
)
assert r.status_code == 201
assert r.json["alias"] == f"prefix.{word}@{EMAIL_DOMAIN}"
# assert returned field
res = r.json
assert "id" in res
assert "email" in res
assert "creation_date" in res
assert "creation_timestamp" in res
assert "nb_forward" in res
assert "nb_block" in res
assert "nb_reply" in res
assert "enabled" in res
assert "note" in res
new_ge = Alias.get_by(email=r.json["alias"])
assert new_ge.note == "test note"