Skip to content

Commit

Permalink
Fixes for connect with proton on mobile (#1230)
Browse files Browse the repository at this point in the history
* Fixes for connect with proton on mobile

* Added a test

Co-authored-by: Adrià Casajús <[email protected]>
  • Loading branch information
cquintana92 and acasajus authored Aug 12, 2022
1 parent 7476bdd commit 7eb44a5
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 22 deletions.
9 changes: 9 additions & 0 deletions app/api/views/setting.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
SenderFormatEnum,
AliasSuffixEnum,
)
from app.proton.utils import perform_proton_account_unlink


def setting_to_dict(user: User):
Expand Down Expand Up @@ -137,3 +138,11 @@ def get_available_domains_for_random_alias_v2():
]

return jsonify(ret)


@api_bp.route("/setting/unlink_proton_account", methods=["DELETE"])
@require_api_auth
def unlink_proton_account():
user = g.user
perform_proton_account_unlink(user)
return jsonify({"ok": True})
14 changes: 13 additions & 1 deletion app/api/views/user_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@

from flask import jsonify, g, request, make_response
from flask_login import logout_user
from typing import Optional

from app import s3
from app.api.base import api_bp, require_api_auth
from app.config import SESSION_COOKIE_NAME
from app.db import Session
from app.models import ApiKey, File, User
from app.models import ApiKey, File, PartnerUser, User
from app.utils import random_string
from app.proton.utils import get_proton_partner


def get_connected_proton_address(user: User) -> Optional[str]:
proton_partner = get_proton_partner()
partner_user = PartnerUser.get_by(user_id=user.id, partner_id=proton_partner.id)
if partner_user is None:
return None
return partner_user.partner_email


def user_to_dict(user: User) -> dict:
Expand All @@ -19,6 +29,7 @@ def user_to_dict(user: User) -> dict:
"email": user.email,
"in_trial": user.in_trial(),
"max_alias_free_plan": user.max_alias_for_free_account(),
"connected_proton_address": get_connected_proton_address(user),
}

if user.profile_picture_id:
Expand All @@ -41,6 +52,7 @@ def user_info():
- email
- in_trial
- max_alias_free
- is_connected_with_proton
"""
user = g.user

Expand Down
9 changes: 3 additions & 6 deletions app/auth/views/api_to_cookie.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import arrow
from flask import redirect, url_for, request, flash
from flask_login import current_user, login_user
from flask_login import login_user

from app.auth.base import auth_bp
from app.log import LOG
from app.models import ApiToCookieToken
from app.utils import sanitize_next_url


@auth_bp.route("/api_to_cookie", methods=["GET"])
def api_to_cookie():
if current_user.is_authenticated:
LOG.d("user is already authenticated, redirect to dashboard")
return redirect(url_for("dashboard.index"))
code = request.args.get("token")
if not code:
flash("Missing token", "error")
Expand All @@ -23,8 +19,9 @@ def api_to_cookie():
flash("Missing token", "error")
return redirect(url_for("auth.login"))

login_user(token.user)
user = token.user
ApiToCookieToken.delete(token.id, commit=True)
login_user(user)

next_url = sanitize_next_url(request.args.get("next"))
if next_url:
Expand Down
10 changes: 8 additions & 2 deletions app/auth/views/proton.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def extract_action() -> Action:
if action is not None:
if action == "link":
return Action.Link
elif action == "login":
return Action.Login
else:
raise Exception(f"Unknown action: {action}")
return Action.Login
Expand All @@ -70,6 +72,10 @@ def proton_login():
if PROTON_CLIENT_ID is None or PROTON_CLIENT_SECRET is None:
return redirect(url_for("auth.login"))

action = extract_action()
if action == Action.Link and not current_user.is_authenticated:
return redirect(url_for("auth.login"))

next_url = sanitize_next_url(request.args.get("next"))
if next_url:
session["oauth_next"] = next_url
Expand All @@ -93,7 +99,7 @@ def proton_login():

# State is used to prevent CSRF, keep this for later.
session[SESSION_STATE_KEY] = state
session[SESSION_ACTION_KEY] = extract_action().value
session[SESSION_ACTION_KEY] = action.value
return redirect(authorization_url)


Expand Down Expand Up @@ -168,7 +174,7 @@ def check_status_code(response: requests.Response) -> requests.Response:
if session.get("oauth_mode", "session") == "apikey":
apikey = get_api_key_for_user(res.user)
scheme = oauth_scheme or DEFAULT_SCHEME
return redirect(f"{scheme}:///login_callback?apikey={apikey}")
return redirect(f"{scheme}:///login?apikey={apikey}")

if res.redirect_to_login:
return redirect(url_for("auth.login"))
Expand Down
12 changes: 2 additions & 10 deletions app/dashboard/views/setting.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from flask_login import login_required, current_user
from flask_wtf import FlaskForm
from flask_wtf.file import FileField
from newrelic import agent
from wtforms import StringField, validators
from wtforms.fields.html5 import EmailField

Expand Down Expand Up @@ -53,7 +52,7 @@
PartnerSubscription,
UnsubscribeBehaviourEnum,
)
from app.proton.utils import get_proton_partner
from app.proton.utils import get_proton_partner, perform_proton_account_unlink
from app.utils import random_string, sanitize_email


Expand Down Expand Up @@ -481,13 +480,6 @@ def cancel_email_change():
@dashboard_bp.route("/unlink_proton_account", methods=["GET", "POST"])
@login_required
def unlink_proton_account():
proton_partner = get_proton_partner()
partner_user = PartnerUser.get_by(
user_id=current_user.id, partner_id=proton_partner.id
)
if partner_user is not None:
PartnerUser.delete(partner_user.id)
Session.commit()
perform_proton_account_unlink(current_user)
flash("Your Proton account has been unlinked", "success")
agent.record_custom_event("AccountUnlinked", {"partner": proton_partner.name})
return redirect(url_for("dashboard.setting"))
14 changes: 13 additions & 1 deletion app/proton/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from newrelic import agent
from typing import Optional

from app.db import Session
from app.errors import ProtonPartnerNotSetUp
from app.models import Partner
from app.models import Partner, PartnerUser, User

PROTON_PARTNER_NAME = "Proton"
_PROTON_PARTNER: Optional[Partner] = None
Expand All @@ -21,3 +22,14 @@ def get_proton_partner() -> Partner:

def is_proton_partner(partner: Partner) -> bool:
return partner.name == PROTON_PARTNER_NAME


def perform_proton_account_unlink(current_user: User):
proton_partner = get_proton_partner()
partner_user = PartnerUser.get_by(
user_id=current_user.id, partner_id=proton_partner.id
)
if partner_user is not None:
PartnerUser.delete(partner_user.id)
Session.commit()
agent.record_custom_event("AccountUnlinked", {"partner": proton_partner.name})
34 changes: 32 additions & 2 deletions tests/api/test_user_info.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from flask import url_for

from app import config
from app.models import User
from app.models import User, PartnerUser
from app.proton.utils import get_proton_partner
from tests.api.utils import get_new_user_and_api_key
from tests.utils import login
from tests.utils import login, random_token, random_email


def test_user_in_trial(flask_client):
Expand All @@ -21,6 +22,35 @@ def test_user_in_trial(flask_client):
"in_trial": True,
"profile_picture_url": None,
"max_alias_free_plan": config.MAX_NB_EMAIL_FREE_PLAN,
"connected_proton_address": None,
}


def test_user_linked_to_proton(flask_client):
user, api_key = get_new_user_and_api_key()
partner = get_proton_partner()
partner_email = random_email()
PartnerUser.create(
user_id=user.id,
partner_id=partner.id,
external_user_id=random_token(),
partner_email=partner_email,
commit=True,
)

r = flask_client.get(
url_for("api.user_info"), headers={"Authentication": api_key.code}
)

assert r.status_code == 200
assert r.json == {
"is_premium": True,
"name": "Test User",
"email": user.email,
"in_trial": True,
"profile_picture_url": None,
"max_alias_free_plan": config.MAX_NB_EMAIL_FREE_PLAN,
"connected_proton_address": partner_email,
}


Expand Down

0 comments on commit 7eb44a5

Please sign in to comment.