Respect next when logging in using GitHub

This commit is contained in:
rubenwardy 2023-10-31 18:45:24 +00:00
parent a29715775e
commit 72b608b158
4 changed files with 29 additions and 14 deletions

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from flask import Blueprint from flask import Blueprint, abort
from flask_babel import gettext from flask_babel import gettext
bp = Blueprint("github", __name__) bp = Blueprint("github", __name__)
@ -24,13 +24,19 @@ from flask_login import current_user
from sqlalchemy import func, or_, and_ from sqlalchemy import func, or_, and_
from app import github, csrf from app import github, csrf
from app.models import db, User, APIToken, Package, Permission, AuditSeverity, PackageState from app.models import db, User, APIToken, Package, Permission, AuditSeverity, PackageState
from app.utils import abs_url_for, add_audit_log, login_user_set_active from app.utils import abs_url_for, add_audit_log, login_user_set_active, is_safe_url
from app.blueprints.api.support import error, api_create_vcs_release from app.blueprints.api.support import error, api_create_vcs_release
import hmac, requests import hmac, requests
@bp.route("/github/start/") @bp.route("/github/start/")
def start(): def start():
return github.authorize("", redirect_uri=abs_url_for("github.callback")) next = request.args.get("next")
if next and not is_safe_url(next):
abort(400)
return github.authorize("", redirect_uri=abs_url_for("github.callback", next=next))
@bp.route("/github/view/") @bp.route("/github/view/")
def view_permissions(): def view_permissions():
@ -46,6 +52,14 @@ def callback(oauth_token):
flash(gettext("Authorization failed [err=gh-oauth-login-failed]"), "danger") flash(gettext("Authorization failed [err=gh-oauth-login-failed]"), "danger")
return redirect(url_for("users.login")) return redirect(url_for("users.login"))
next = request.args.get("next")
if next and not is_safe_url(next):
abort(400)
redirect_to = next
if redirect_to is None:
redirect_to = url_for("homepage.home")
# Get GitGub username # Get GitGub username
url = "https://api.github.com/user" url = "https://api.github.com/user"
r = requests.get(url, headers={"Authorization": "token " + oauth_token}) r = requests.get(url, headers={"Authorization": "token " + oauth_token})
@ -60,10 +74,10 @@ def callback(oauth_token):
current_user.github_username = username current_user.github_username = username
db.session.commit() db.session.commit()
flash(gettext("Linked GitHub to account"), "success") flash(gettext("Linked GitHub to account"), "success")
return redirect(url_for("homepage.home")) return redirect(redirect_to)
else: else:
flash(gettext("GitHub account is already associated with another user"), "danger") flash(gettext("GitHub account is already associated with another user"), "danger")
return redirect(url_for("homepage.home")) return redirect(redirect_to)
# If not logged in, log in # If not logged in, log in
else: else:
@ -71,13 +85,13 @@ def callback(oauth_token):
flash(gettext("Unable to find an account for that GitHub user"), "danger") flash(gettext("Unable to find an account for that GitHub user"), "danger")
return redirect(url_for("users.claim_forums")) return redirect(url_for("users.claim_forums"))
ret = login_user_set_active(userByGithub, remember=True) ret = login_user_set_active(userByGithub, next, remember=True)
if ret is None: if ret is None:
flash(gettext("Authorization failed [err=gh-login-failed]"), "danger") flash(gettext("Authorization failed [err=gh-login-failed]"), "danger")
return redirect(url_for("users.login")) return redirect(url_for("users.login"))
add_audit_log(AuditSeverity.USER, userByGithub, "Logged in using GitHub OAuth", add_audit_log(AuditSeverity.USER, userByGithub, "Logged in using GitHub OAuth",
url_for("users.profile", username=userByGithub.username)) url_for("users.profile", username=userByGithub.username))
db.session.commit() db.session.commit()
return ret return ret

@ -71,11 +71,11 @@ def handle_login(form):
@bp.route("/user/login/", methods=["GET", "POST"]) @bp.route("/user/login/", methods=["GET", "POST"])
def login(): def login():
if current_user.is_authenticated: next = request.args.get("next")
next = request.args.get("next") if next and not is_safe_url(next):
if next and not is_safe_url(next): abort(400)
abort(400)
if current_user.is_authenticated:
return redirect(next or url_for("homepage.home")) return redirect(next or url_for("homepage.home"))
form = LoginForm(request.form) form = LoginForm(request.form)
@ -87,7 +87,7 @@ def login():
if request.method == "GET": if request.method == "GET":
form.remember_me.data = True form.remember_me.data = True
return render_template("users/login.html", form=form) return render_template("users/login.html", form=form, next=next)
@bp.route("/user/logout/", methods=["GET", "POST"]) @bp.route("/user/logout/", methods=["GET", "POST"])

@ -25,7 +25,7 @@
<hr class="my-5" /> <hr class="my-5" />
<p> <p>
<a class="btn btn-secondary me-3" href="{{ url_for('github.start') }}"> <a class="btn btn-secondary me-3" href="{{ url_for('github.start', next=next) }}">
<i class="fab fa-github me-1"></i> <i class="fab fa-github me-1"></i>
{{ _("GitHub") }} {{ _("GitHub") }}
</a> </a>

@ -16,6 +16,7 @@
from functools import wraps from functools import wraps
from typing import Optional
from flask_babel import gettext from flask_babel import gettext
from flask_login import login_user, current_user from flask_login import login_user, current_user
@ -57,7 +58,7 @@ def post_login(user: User, next_url):
return redirect(url_for("homepage.home")) return redirect(url_for("homepage.home"))
def login_user_set_active(user: User, next_url: str = None, *args, **kwargs): def login_user_set_active(user: User, next_url: Optional[str] = None, *args, **kwargs):
if user.rank == UserRank.NOT_JOINED and user.email is None: if user.rank == UserRank.NOT_JOINED and user.email is None:
user.rank = UserRank.NEW_MEMBER user.rank = UserRank.NEW_MEMBER
user.notification_preferences = UserNotificationPreferences(user) user.notification_preferences = UserNotificationPreferences(user)