diff --git a/README.md b/README.md index 39f9b6b..992365d 100644 --- a/README.md +++ b/README.md @@ -68,13 +68,6 @@ Create `.env` file in the root directory or pass as environment variables. | MAIL_SERVER | " | | MAIL_USERNAME | " | -### Login with Google via OAuth - -| Variable | Function | -| --- | --- | -| GOOGLE_OAUTH_CLIENT_ID | Client Id | -| GOOGLE_OAUTH_CLIENT_SECRET | Secret | - ### Resolve addresses with Google Maps | Variable | Function | diff --git a/project/__init__.py b/project/__init__.py index a84680b..9b22a5f 100644 --- a/project/__init__.py +++ b/project/__init__.py @@ -23,10 +23,6 @@ app.config["SECURITY_RECOVERABLE"] = True app.config["SECURITY_CHANGEABLE"] = True app.config["SECURITY_EMAIL_SENDER"] = os.getenv("MAIL_DEFAULT_SENDER") app.config["LANGUAGES"] = ["en", "de"] -app.config["GOOGLE_OAUTH_CLIENT_ID"] = os.getenv("GOOGLE_OAUTH_CLIENT_ID") -app.config["GOOGLE_OAUTH_CLIENT_SECRET"] = os.getenv("GOOGLE_OAUTH_CLIENT_SECRET") -app.config["OAUTHLIB_INSECURE_TRANSPORT"] = True -app.config["OAUTHLIB_RELAX_TOKEN_SCOPE"] = True # Generate a nice key using secrets.token_urlsafe() app.config["SECRET_KEY"] = os.environ.get( @@ -86,14 +82,10 @@ app.json_encoder = DateTimeEncoder # Setup Flask-Security from project.models import User, Role +from project.forms.security import ExtendedRegisterForm user_datastore = SQLAlchemySessionUserDatastore(db.session, User, Role) -security = Security(app, user_datastore) - -# OAuth -from project.oauth import blueprint - -app.register_blueprint(blueprint, url_prefix="/login") +security = Security(app, user_datastore, register_form=ExtendedRegisterForm) from project import i10n from project import jinja_filters diff --git a/project/forms/common.py b/project/forms/common.py index 5ad8265..b833814 100644 --- a/project/forms/common.py +++ b/project/forms/common.py @@ -1,8 +1,10 @@ +from flask import url_for from flask_babelex import lazy_gettext from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileAllowed from wtforms import StringField, BooleanField, HiddenField from wtforms.validators import Optional +from markupsafe import Markup import re import base64 @@ -62,6 +64,24 @@ class Base64ImageForm(BaseImageForm): obj.encoding_format = None +def get_accept_tos_markup(): + tos_open = '' % url_for("tos") + tos_close = "" + + privacy_open = '' % url_for("privacy") + privacy_close = "" + + return Markup( + lazy_gettext( + "I read and accept %(tos_open)sTerms of Service%(tos_close)s and %(privacy_open)sPrivacy%(privacy_close)s.", + tos_open=tos_open, + tos_close=tos_close, + privacy_open=privacy_open, + privacy_close=privacy_close, + ) + ) + + event_rating_choices = [ (0, lazy_gettext("0 (Little relevant)")), (10, "1"), diff --git a/project/forms/event_suggestion.py b/project/forms/event_suggestion.py index 9185ac0..7001e63 100644 --- a/project/forms/event_suggestion.py +++ b/project/forms/event_suggestion.py @@ -14,7 +14,7 @@ from project.models import ( EventRejectionReason, Image, ) -from project.forms.common import Base64ImageForm +from project.forms.common import Base64ImageForm, get_accept_tos_markup from project.forms.widgets import CustomDateTimeField, TagSelectField @@ -88,15 +88,14 @@ class CreateEventSuggestionForm(FlaskForm): "We recommend uploading a photo for the event. It looks a lot more, but of course it works without it." ), ) - accept_tos = BooleanField( - lazy_gettext( - "I confirm that I have clarified all information (text, images, etc.) that I upload into the system with regard to their rights of use and declare that they may be passed on." - ), - validators=[DataRequired()], - ) + accept_tos = BooleanField(validators=[DataRequired()]) submit = SubmitField(lazy_gettext("Create event suggestion")) + def __init__(self, **kwargs): + super(CreateEventSuggestionForm, self).__init__(**kwargs) + self._fields["accept_tos"].label.text = get_accept_tos_markup() + def populate_obj(self, obj): for name, field in self._fields.items(): if name == "photo" and not obj.photo: diff --git a/project/forms/security.py b/project/forms/security.py new file mode 100644 index 0000000..62ad06e --- /dev/null +++ b/project/forms/security.py @@ -0,0 +1,12 @@ +from flask_security.forms import RegisterForm +from wtforms import BooleanField +from wtforms.validators import DataRequired +from project.forms.common import get_accept_tos_markup + + +class ExtendedRegisterForm(RegisterForm): + accept_tos = BooleanField(validators=[DataRequired()]) + + def __init__(self, *args, **kwargs): + super(ExtendedRegisterForm, self).__init__(*args, **kwargs) + self._fields["accept_tos"].label.text = get_accept_tos_markup() diff --git a/project/oauth.py b/project/oauth.py deleted file mode 100644 index e12fc59..0000000 --- a/project/oauth.py +++ /dev/null @@ -1,67 +0,0 @@ -from flask import flash -from flask_security import current_user, login_user -from flask_dance.contrib.google import make_google_blueprint -from flask_dance.consumer import oauth_authorized, oauth_error -from flask_dance.consumer.storage.sqla import SQLAlchemyStorage -from project.models import OAuth -from project import db, user_datastore -from flask_babelex import gettext - -blueprint = make_google_blueprint( - scope=["profile", "email"], - storage=SQLAlchemyStorage(OAuth, db.session, user=current_user), -) - - -# create/login local user on successful OAuth login -@oauth_authorized.connect_via(blueprint) -def google_logged_in(blueprint, token): # pragma: no cover - if not token: - flash("Failed to log in.", category="error") - return False - - resp = blueprint.session.get("/oauth2/v1/userinfo") - if not resp.ok: - msg = "Failed to fetch user info." - flash(msg, category="error") - return False - - info = resp.json() - user_id = info["id"] - - # Find this OAuth token in the database, or create it - oauth = OAuth.query.filter_by( - provider=blueprint.name, provider_user_id=user_id - ).first() - if oauth is None: - oauth = OAuth(provider=blueprint.name, provider_user_id=user_id, token=token) - - if oauth.user: - login_user(oauth.user, authn_via=["google"]) - user_datastore.commit() - flash(gettext("Successfully signed in."), "success") - - else: - # Create a new local user account for this user - user = user_datastore.create_user(email=info["email"]) - # Associate the new local user account with the OAuth token - oauth.user = user - # Save and commit our database models - db.session.add_all([user, oauth]) - db.session.commit() - # Log in the new local user account - login_user(user, authn_via=["google"]) - user_datastore.commit() - flash(gettext("Successfully signed in."), "success") - - # Disable Flask-Dance's default behavior for saving the OAuth token - return False - - -# notify on OAuth provider error -@oauth_error.connect_via(blueprint) -def google_error(blueprint, message, response): # pragma: no cover - msg = "OAuth error from {name}! message={message} response={response}".format( - name=blueprint.name, message=message, response=response - ) - flash(msg, category="error") diff --git a/project/templates/_macros.html b/project/templates/_macros.html index 59c82be..4dfe7f7 100644 --- a/project/templates/_macros.html +++ b/project/templates/_macros.html @@ -438,10 +438,6 @@ {% endmacro %} -{% macro render_google_sign_in_button() %} - {{ _('Sign in with Google') }} -{% endmacro %} - {% macro render_google_place_autocomplete_header(location_only = False) %}