From ff8bf992a9048e55b58ece46cf25cc3a43edcef7 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 13 May 2018 23:31:42 +0100 Subject: [PATCH] Add user account claiming --- app/tasks/__init__.py | 2 +- app/tasks/forumtasks.py | 33 ++++++++++ app/tasks/phpbbparser.py | 72 +++++++++++++++++++++ app/templates/flask_user/login.html | 17 ++--- app/templates/users/claim.html | 98 +++++++++++++++++++++++++++++ app/views/githublogin.py | 12 +--- app/views/tasks.py | 2 - app/views/users.py | 42 ++++++++++++- requirements.txt | 2 + 9 files changed, 253 insertions(+), 27 deletions(-) create mode 100644 app/tasks/forumtasks.py create mode 100644 app/tasks/phpbbparser.py create mode 100644 app/templates/users/claim.html diff --git a/app/tasks/__init__.py b/app/tasks/__init__.py index c431fae7..9ee293ea 100644 --- a/app/tasks/__init__.py +++ b/app/tasks/__init__.py @@ -41,4 +41,4 @@ def make_celery(app): celery = make_celery(app) -from . import importtasks +from . import importtasks, forumtasks diff --git a/app/tasks/forumtasks.py b/app/tasks/forumtasks.py new file mode 100644 index 00000000..e4ddc594 --- /dev/null +++ b/app/tasks/forumtasks.py @@ -0,0 +1,33 @@ +import flask +from flask.ext.sqlalchemy import SQLAlchemy +from app import app +from app.models import * +from app.tasks import celery +from .phpbbparser import getProfile + +@celery.task() +def checkForumAccount(username, token=None): + try: + profile = getProfile("https://forum.minetest.net", username) + except OSError: + return + + user = User.query.filter_by(forums_username=username).first() + + # Create user + needsSaving = False + if user is None: + user = User(username) + user.forums_username = username + db.session.add(user) + + # Get github username + github_username = profile.get("github") + if github_username is not None and github_username.strip() != "": + print("Updated github username") + user.github_username = github_username + needsSaving = True + + # Save + if needsSaving: + db.session.commit() diff --git a/app/tasks/phpbbparser.py b/app/tasks/phpbbparser.py new file mode 100644 index 00000000..3932b943 --- /dev/null +++ b/app/tasks/phpbbparser.py @@ -0,0 +1,72 @@ +import urllib, socket +from bs4 import * +from urllib.parse import urljoin +import urllib.request +import os.path +import time + +class Profile: + def __init__(self, username): + self.username = username + self.signature = "" + self.properties = {} + + def set(self, key, value): + self.properties[key] = value + + def get(self, key): + return self.properties[key] if key in self.properties else None + + def __str__(self): + return self.username + "\n" + str(self.signature) + "\n" + str(self.properties) + +def __extract_properties(profile, soup): + el = soup.find(id="viewprofile") + if el is None: + return None + + res = el.find_all("dl", class_ = "left-box details") + if len(res) != 1: + return None + + catch_next_key = None + + # Look through + for element in res[0].children: + if element.name == "dt": + if catch_next_key is None: + catch_next_key = element.text.lower()[:-1].strip() + else: + print("Unexpected dt!") + + elif element.name == "dd": + if catch_next_key is None: + print("Unexpected dd!") + else: + if catch_next_key != "groups": + profile.set(catch_next_key, element.text) + catch_next_key = None + + elif element and element.name is not None: + print("Unexpected other") + +def __extract_signature(soup): + res = soup.find_all("div", class_="signature") + if (len(res) != 1): + return None + else: + return res[0] + +def getProfile(url, username): + url = url + "/memberlist.php?mode=viewprofile&un=" + username + + contents = urllib.request.urlopen(url).read().decode("utf-8") + soup = BeautifulSoup(contents, "lxml") + if soup is None: + return None + else: + profile = Profile(username) + profile.signature = __extract_signature(soup) + __extract_properties(profile, soup) + + return profile diff --git a/app/templates/flask_user/login.html b/app/templates/flask_user/login.html index c19f1f3a..c676aca7 100644 --- a/app/templates/flask_user/login.html +++ b/app/templates/flask_user/login.html @@ -61,26 +61,17 @@ Sign in {# Submit button #} {{ render_submit_field(form.submit, tabindex=180) }} + + GitHub
- - -
diff --git a/app/templates/users/claim.html b/app/templates/users/claim.html new file mode 100644 index 00000000..f4333d9a --- /dev/null +++ b/app/templates/users/claim.html @@ -0,0 +1,98 @@ +{% extends "base.html" %} + +{% block title %} +Verify forum account +{% endblock %} + +{% block content %} +
+

{{ self.title() }}

+ +

+ Create an account by linking it to your forum account and optionally + your github account. +

+ + {% if current_user.is_authenticated %} +

+ Please log out to continue. +

+

+ Logout +

+ {% else %} +

+ Don't have a forum account? + Unfortunately, you need a forum account to register. + This is because you also need to create forum topics for any packages + you may upload. +

+ + + Create a Forum Account + + {% endif %} +
+ + {% if not current_user.is_authenticated %} +
+

Option 1 - Use GitHub field in forum profile

+ +
+ + + +

+ Enter your forum username here: +

+ + + +

+ You'll need to have the GitHub field in your forum profile + filled out. Log into the forum and + + do that here. +

+ + +
+
+ + + {% endif %} +{% endblock %} diff --git a/app/views/githublogin.py b/app/views/githublogin.py index 875f1b27..114bb2b5 100644 --- a/app/views/githublogin.py +++ b/app/views/githublogin.py @@ -43,16 +43,8 @@ def github_authorized(oauth_token): # If not logged in, log in else: if userByGithub is None: - newUser = User(username) - newUser.github_username = username - db.session.add(newUser) - db.session.commit() - - if not loginUser(newUser): - raise Exception("Unable to login as user we just created") - - flash("Created an account", "success") - return redirect(url_for("user_profile_page", username=username)) + flash("Unable to find an account for that Github user", "error") + return redirect(url_for("user_claim_page")) elif loginUser(userByGithub): return redirect(next_url or url_for("home_page")) else: diff --git a/app/views/tasks.py b/app/views/tasks.py index 9b27f61a..e6a5dc4e 100644 --- a/app/views/tasks.py +++ b/app/views/tasks.py @@ -22,7 +22,6 @@ def new_getmeta_page(): }) @app.route("/tasks//") -@login_required def check_task(id): result = celery.AsyncResult(id) status = result.status @@ -51,7 +50,6 @@ def check_task(id): abort(422) if status == "SUCCESS": - flash("Task complete!", "success") return redirect(r) else: return render_template("tasks/view.html", info=info) diff --git a/app/views/users.py b/app/views/users.py index 995f09c1..d3ed1aa6 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -8,7 +8,8 @@ from flask_wtf import FlaskForm from flask_user.forms import RegisterForm from wtforms import * from wtforms.validators import * -from .utils import rank_required +from .utils import rank_required, randomString +from app.tasks.forumtasks import checkForumAccount class MyRegisterForm(RegisterForm): display_name = StringField("Display name") @@ -59,3 +60,42 @@ def user_profile_page(username): # Process GET or invalid POST return render_template("users/user_profile_page.html", user=user, form=form) + + +@app.route("/users/claim/", methods=["GET", "POST"]) +def user_claim_page(): + username = request.args.get("username") + if username is None: + username = "" + else: + method = request.args.get("method") + user = User.query.filter_by(forums_username=username).first() + if user and user.rank.atLeast(UserRank.NEW_MEMBER): + flash("User has already been claimed", "error") + return redirect(url_for("user_claim_page")) + elif user is None and method == "github": + flash("Unable to get Github username for user", "error") + return redirect(url_for("user_claim_page")) + elif user is None: + flash("Unable to find that user", "error") + return redirect(url_for("user_claim_page")) + + if user is not None and method == "github": + return redirect(url_for("github_signin_page")) + + if request.method == "POST": + ctype = request.form.get("claim_type") + username = request.form.get("username") + + if username is None or len(username.strip()) < 2: + flash("Invalid username", "error") + elif ctype == "github": + task = checkForumAccount.delay(username) + return redirect(url_for("check_task", id=task.id, r=url_for("user_claim_page", username=username, method="github"))) + elif ctype == "forum": + token = request.form.get("token") + flash("Unimplemented", "error") + else: + flash("Unknown claim type", "error") + + return render_template("users/claim.html", username=username, key=randomString(32)) diff --git a/requirements.txt b/requirements.txt index 903f984f..9e79b270 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,5 @@ GitHub-Flask>=3.2.0 pyScss==1.3.4 celery==4.0.2 redis==2.10.6 +beautifulsoup4==4.6.0 +lxml==4.2.1