mirror of
https://github.com/lucaspalomodevelop/eventcally.git
synced 2026-03-13 00:07:22 +00:00
Google Sign in
This commit is contained in:
parent
c797b450df
commit
370a78913b
11
README.md
11
README.md
@ -4,7 +4,8 @@ python manage.py db init
|
||||
python manage.py db migrate
|
||||
python manage.py db upgrade
|
||||
|
||||
## Developemt !!!
|
||||
## Development only
|
||||
|
||||
python manage.py db history
|
||||
python manage.py db downgrade
|
||||
// reset git: migrations/versions
|
||||
@ -12,18 +13,22 @@ python manage.py db migrate
|
||||
python manage.py db upgrade
|
||||
|
||||
## Kill local detached server
|
||||
|
||||
lsof -i :5000
|
||||
kill -9 PIDNUMBER
|
||||
|
||||
# i18n
|
||||
## i18n
|
||||
|
||||
https://pythonhosted.org/Flask-BabelEx/
|
||||
<https://pythonhosted.org/Flask-BabelEx/>
|
||||
|
||||
## Init
|
||||
|
||||
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel init -i messages.pot -d translations -l de
|
||||
|
||||
## Neue msgid's scannen und in *.po mergen
|
||||
|
||||
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel update -i messages.pot -d translations
|
||||
|
||||
## Nach dem Übersetzen
|
||||
|
||||
pybabel compile -d translations
|
||||
|
||||
5
app.py
5
app.py
@ -24,6 +24,8 @@ app.config['SECURITY_TRACKABLE'] = True
|
||||
app.config['SECURITY_REGISTERABLE'] = True
|
||||
app.config['SECURITY_SEND_REGISTER_EMAIL'] = False
|
||||
app.config['LANGUAGES'] = ['en', 'de']
|
||||
app.config['GOOGLE_OAUTH_CLIENT_ID'] = os.environ['GOOGLE_OAUTH_CLIENT_ID']
|
||||
app.config['GOOGLE_OAUTH_CLIENT_SECRET'] = os.environ['GOOGLE_OAUTH_CLIENT_SECRET']
|
||||
|
||||
# Generate a nice key using secrets.token_urlsafe()
|
||||
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw')
|
||||
@ -48,6 +50,9 @@ db = SQLAlchemy(app)
|
||||
from models import EventCategory, Image, EventSuggestion, EventSuggestionDate, OrgOrAdminUnit, Actor, Place, Location, User, Role, AdminUnit, AdminUnitMember, AdminUnitMemberRole, OrgMember, OrgMemberRole, Organization, AdminUnitOrg, AdminUnitOrgRole, Event, EventDate
|
||||
user_datastore = SQLAlchemySessionUserDatastore(db.session, User, Role)
|
||||
security = Security(app, user_datastore)
|
||||
from oauth import blueprint
|
||||
|
||||
app.register_blueprint(blueprint, url_prefix="/login")
|
||||
|
||||
berlin_tz = pytz.timezone('Europe/Berlin')
|
||||
now = datetime.now(tz=berlin_tz)
|
||||
|
||||
@ -7,6 +7,7 @@ Create Date: ${create_date}
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
39
migrations/versions/abf0f671ba27_.py
Normal file
39
migrations/versions/abf0f671ba27_.py
Normal file
@ -0,0 +1,39 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: abf0f671ba27
|
||||
Revises: bbad7e33a780
|
||||
Create Date: 2020-06-30 21:09:35.692876
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'abf0f671ba27'
|
||||
down_revision = 'bbad7e33a780'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('flask_dance_oauth',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('provider', sa.String(length=50), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('token', sqlalchemy_utils.types.json.JSONType(), nullable=False),
|
||||
sa.Column('provider_user_id', sa.String(length=256), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('provider_user_id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('flask_dance_oauth')
|
||||
# ### end Alembic commands ###
|
||||
@ -4,6 +4,7 @@ from sqlalchemy.orm import relationship, backref
|
||||
from sqlalchemy.schema import CheckConstraint
|
||||
from sqlalchemy import UniqueConstraint, Boolean, DateTime, Column, Integer, String, ForeignKey, Unicode, UnicodeText, Numeric, LargeBinary
|
||||
from flask_security import UserMixin, RoleMixin
|
||||
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin
|
||||
import datetime
|
||||
|
||||
### Base
|
||||
@ -59,6 +60,11 @@ class User(db.Model, UserMixin):
|
||||
roles = relationship('Role', secondary='roles_users',
|
||||
backref=backref('users', lazy='dynamic'))
|
||||
|
||||
class OAuth(OAuthConsumerMixin, db.Model):
|
||||
provider_user_id = Column(String(256), unique=True, nullable=False)
|
||||
user_id = Column(Integer(), ForeignKey('user.id'), nullable=False)
|
||||
user = db.relationship('User')
|
||||
|
||||
### Organization
|
||||
|
||||
class OrgMemberRolesMembers(db.Model):
|
||||
|
||||
66
oauth.py
Normal file
66
oauth.py
Normal file
@ -0,0 +1,66 @@
|
||||
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 sqlalchemy.orm.exc import NoResultFound
|
||||
from models import User, OAuth
|
||||
from app 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):
|
||||
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):
|
||||
msg = "OAuth error from {name}! message={message} response={response}".format(
|
||||
name=blueprint.name, message=message, response=response
|
||||
)
|
||||
flash(msg, category="error")
|
||||
@ -2,7 +2,9 @@ alembic==1.4.2
|
||||
Babel==2.8.0
|
||||
bcrypt==3.1.7
|
||||
blinker==1.4
|
||||
certifi==2020.6.20
|
||||
cffi==1.14.0
|
||||
chardet==3.0.4
|
||||
click==7.1.2
|
||||
dnspython==1.16.0
|
||||
dominate==2.5.1
|
||||
@ -10,6 +12,7 @@ email-validator==1.1.1
|
||||
Flask==1.1.2
|
||||
Flask-BabelEx==0.9.4
|
||||
Flask-Bootstrap==3.3.7.1
|
||||
Flask-Dance==3.0.0
|
||||
Flask-Login==0.5.0
|
||||
Flask-Mail==0.9.1
|
||||
Flask-Migrate==2.5.3
|
||||
@ -24,6 +27,7 @@ itsdangerous==1.1.0
|
||||
Jinja2==2.11.2
|
||||
Mako==1.1.3
|
||||
MarkupSafe==1.1.1
|
||||
oauthlib==3.1.0
|
||||
passlib==1.7.2
|
||||
psycopg2-binary==2.8.5
|
||||
pycparser==2.20
|
||||
@ -31,9 +35,14 @@ python-dateutil==2.8.1
|
||||
python-dotenv==0.13.0
|
||||
python-editor==1.0.4
|
||||
pytz==2020.1
|
||||
requests==2.24.0
|
||||
requests-oauthlib==1.3.0
|
||||
six==1.15.0
|
||||
speaklater==1.3
|
||||
SQLAlchemy==1.3.17
|
||||
SQLAlchemy-Utils==0.36.6
|
||||
urllib3==1.25.9
|
||||
URLObject==2.4.3
|
||||
visitor==0.1.3
|
||||
Werkzeug==1.0.1
|
||||
WTForms==2.3.1
|
||||
|
||||
@ -77,6 +77,11 @@ tr.table-line-through td {
|
||||
z-index: 1025 !important;
|
||||
}
|
||||
|
||||
.btn-google {
|
||||
color: white;
|
||||
background-color: #ea4335;
|
||||
}
|
||||
|
||||
@media (max-width: 320px) {
|
||||
td, th {
|
||||
word-break: break-all;
|
||||
|
||||
@ -193,3 +193,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_google_sign_in_button() %}
|
||||
<a href="{{ url_for('google.login') }}" class="btn btn-google" role="button"><i class="fab fa-google mr-2"></i> {{ _('Sign in with Google') }}</a>
|
||||
{% endmacro %}
|
||||
@ -13,7 +13,7 @@
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='site.css')}}" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-beta.1/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<!-- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='jquery.recurrenceinput.css')}}" /> -->
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "_macros.html" import render_field_with_errors, render_field, render_field_errors %}
|
||||
{% from "_macros.html" import render_google_sign_in_button, render_field_with_errors, render_field, render_field_errors %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
@ -18,4 +18,8 @@
|
||||
{{ render_field(login_user_form.submit) }}
|
||||
</form>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
{{ render_google_sign_in_button() }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "_macros.html" import render_field_with_errors, render_field %}
|
||||
{% from "_macros.html" import render_google_sign_in_button, render_field_with_errors, render_field %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
@ -14,4 +14,8 @@
|
||||
{{ render_field(register_user_form.submit) }}
|
||||
</form>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
{{ render_google_sign_in_button() }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2020-06-26 12:23+0200\n"
|
||||
"POT-Creation-Date: 2020-07-01 08:43+0200\n"
|
||||
"PO-Revision-Date: 2020-06-07 18:51+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: de\n"
|
||||
@ -18,90 +18,94 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.8.0\n"
|
||||
|
||||
#: app.py:61
|
||||
#: app.py:66
|
||||
msgid "Event_"
|
||||
msgstr ""
|
||||
|
||||
#: app.py:66
|
||||
#: app.py:71
|
||||
msgid "Event_Art"
|
||||
msgstr "Kunst"
|
||||
|
||||
#: app.py:67
|
||||
#: app.py:72
|
||||
msgid "Event_Book"
|
||||
msgstr "Literatur"
|
||||
|
||||
#: app.py:68
|
||||
#: app.py:73
|
||||
msgid "Event_Movie"
|
||||
msgstr "Film"
|
||||
|
||||
#: app.py:69
|
||||
#: app.py:74
|
||||
msgid "Event_Family"
|
||||
msgstr "Familie"
|
||||
|
||||
#: app.py:70
|
||||
#: app.py:75
|
||||
msgid "Event_Festival"
|
||||
msgstr "Festival"
|
||||
|
||||
#: app.py:71
|
||||
#: app.py:76
|
||||
msgid "Event_Religious"
|
||||
msgstr "Religion"
|
||||
|
||||
#: app.py:72
|
||||
#: app.py:77
|
||||
msgid "Event_Shopping"
|
||||
msgstr "Shopping"
|
||||
|
||||
#: app.py:73
|
||||
#: app.py:78
|
||||
msgid "Event_Comedy"
|
||||
msgstr "Comedy"
|
||||
|
||||
#: app.py:74
|
||||
#: app.py:79
|
||||
msgid "Event_Music"
|
||||
msgstr "Musik"
|
||||
|
||||
#: app.py:75
|
||||
#: app.py:80
|
||||
msgid "Event_Dance"
|
||||
msgstr "Tanz"
|
||||
|
||||
#: app.py:76
|
||||
#: app.py:81
|
||||
msgid "Event_Nightlife"
|
||||
msgstr "Party"
|
||||
|
||||
#: app.py:77
|
||||
#: app.py:82
|
||||
msgid "Event_Theater"
|
||||
msgstr "Theater"
|
||||
|
||||
#: app.py:78
|
||||
#: app.py:83
|
||||
msgid "Event_Dining"
|
||||
msgstr "Essen"
|
||||
|
||||
#: app.py:79
|
||||
#: app.py:84
|
||||
msgid "Event_Conference"
|
||||
msgstr "Konferenz"
|
||||
|
||||
#: app.py:80
|
||||
#: app.py:85
|
||||
msgid "Event_Meetup"
|
||||
msgstr "Networking"
|
||||
|
||||
#: app.py:81
|
||||
#: app.py:86
|
||||
msgid "Event_Fitness"
|
||||
msgstr "Fitness"
|
||||
|
||||
#: app.py:82
|
||||
#: app.py:87
|
||||
msgid "Event_Sports"
|
||||
msgstr "Sport"
|
||||
|
||||
#: app.py:83
|
||||
#: app.py:88
|
||||
msgid "Event_Other"
|
||||
msgstr "Sonstiges"
|
||||
|
||||
#: app.py:1062
|
||||
#: app.py:1067
|
||||
msgid "Event successfully created"
|
||||
msgstr "Veranstaltung erfolgreich erstellt"
|
||||
|
||||
#: app.py:1094
|
||||
#: app.py:1099
|
||||
msgid "Event suggestion successfully created"
|
||||
msgstr "Veranstaltungsvorschlag erfolgreich erstellt"
|
||||
|
||||
#: oauth.py:41 oauth.py:54
|
||||
msgid "Successfully signed in."
|
||||
msgstr "Erfolgreich eingeloggt."
|
||||
|
||||
#: templates/_macros.html:72 templates/event/create.html:6
|
||||
msgid "Create event"
|
||||
msgstr "Veranstaltung erstellen"
|
||||
@ -164,6 +168,10 @@ msgstr "Link"
|
||||
msgid "Category"
|
||||
msgstr "Kategorie"
|
||||
|
||||
#: templates/_macros.html:198
|
||||
msgid "Sign in with Google"
|
||||
msgstr "Mit Google anmelden"
|
||||
|
||||
#: templates/admin_unit.html:14 templates/organization.html:17
|
||||
msgid "Members"
|
||||
msgstr "Mitglieder"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user