Skip to content

Commit b2d59e7

Browse files
committed
Replace Gitkit with Firebase.
1 parent 1000019 commit b2d59e7

15 files changed

+93
-428
lines changed

app/jekylledit/controllers/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from . import auth
33
from . import site
44
app.register_blueprint(auth.blueprint, url_prefix='/auth')
5+
app.register_blueprint(auth.firebase.blueprint, url_prefix='/auth')

app/jekylledit/controllers/auth.py

+49-172
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,52 @@
22
import os
33
import re
44

5-
from datetime import datetime
65
from functools import wraps
7-
from urllib.parse import urlparse, parse_qs
86

9-
from flask import Blueprint, _request_ctx_stack, abort, jsonify, make_response, \
10-
redirect, render_template, request, url_for
7+
from flask import Blueprint, jsonify, render_template, url_for
8+
from flask_firebase import FirebaseAuth
119
from flask_login import LoginManager, current_user, login_user, logout_user
1210
from flask_principal import Identity, Permission, PermissionDenied, Principal
13-
from ..ext.identitytoolkit import Gitkit
1411
from itsdangerous import URLSafeTimedSerializer
1512

16-
from ..model import Account, OobAction, Repository, Roles, Site, Sites, db
17-
from .base import app, mailgun, jsonp
13+
from ..model import Account, Repository, Roles, Site, db
14+
from .base import app, jsonp
1815

1916

2017
blueprint = Blueprint('auth', __name__)
18+
firebase = FirebaseAuth(app)
2119
login_manager = LoginManager(app)
2220
principal = Principal(app, use_sessions=False, skip_static=True)
23-
if not app.debug:
24-
gitkit = Gitkit(app, {
25-
'widget': 'auth.widget',
26-
'sign_in_success': 'auth.sign_in_success',
27-
'sign_out': 'auth.sign_out',
28-
'oob_action': 'auth.oob_action',
29-
})
30-
else:
31-
gitkit = None
32-
33-
34-
GITKIT_OPTIONS = {'accountChooserEnabled', 'displayMode', 'signInOptions'}
3521

3622

3723
token_serializer = URLSafeTimedSerializer(app.secret_key, salt='access-token')
3824
token_regex = re.compile(r'Bearer\s+([-_.0-9a-zA-Z]+)$')
3925

4026

27+
@firebase.production_loader
28+
def production_sign_in(token):
29+
account = Account.query.get(token['sub'])
30+
if account is None:
31+
account = Account.query.filter(Account.email == token['email']).one_or_none()
32+
if account is not None:
33+
# ID Changed when moving from Gitkit to Firebase.
34+
account.id = token['sub']
35+
else:
36+
account = Account(id=token['sub'])
37+
db.session.add(account)
38+
account.email = token['email']
39+
account.email_verified = token['email_verified']
40+
account.name = token['name']
41+
db.session.flush()
42+
login_user(account)
43+
db.session.commit()
44+
45+
46+
@firebase.unloader
47+
def sign_out():
48+
logout_user()
49+
50+
4151
@login_manager.user_loader
4252
def load_user(id):
4353
return Account.query.get(id)
@@ -70,8 +80,9 @@ def authorization_required(*roles):
7080
def decorator(func):
7181
@wraps(func)
7282
def wrapper(**values):
73-
if not current_user.email_verified:
74-
abort(403)
83+
# XXX
84+
# if not current_user.email_verified:
85+
# abort(403)
7586
site_id = values['site_id']
7687
synchronize(site_id)
7788
needs = [(role, site_id) for role in roles]
@@ -86,181 +97,52 @@ def permission_denied(exc):
8697
return 'Forbidden', 403
8798

8899

89-
@blueprint.route('/widget', methods={'GET', 'POST'})
90-
def widget():
91-
if app.debug:
92-
if request.method == 'GET':
93-
return render_template('auth/widget.html')
94-
email = request.form['email']
95-
account = Account.query.filter_by(email=email).one_or_none()
96-
if account is None:
97-
account = Account(
98-
id=email,
99-
email=email,
100-
email_verified=True)
101-
db.session.add(account)
102-
db.session.flush()
103-
login_user(account)
104-
db.session.commit()
105-
return render_template('auth/close-window.html', message='You have signed in.')
106-
if request.args.get('mode') != 'select':
107-
return render_template('auth/widget.html', options={})
108-
url_adapter = _request_ctx_stack.top.url_adapter
109-
next = request.args.get('next')
110-
if next:
111-
url = urlparse(next)
112-
if url.netloc != request.host:
113-
abort(400)
114-
endpoint, values = url_adapter.match(url.path, 'GET')
115-
if endpoint != 'auth.sign_in_success':
116-
abort(400)
117-
site = synchronize(values['site_id'])
118-
return render_template('auth/widget.html', options=site.gitkit_options)
119-
url = urlparse(request.referrer)
120-
if url.netloc != request.host:
121-
abort(400)
122-
endpoint, __ = url_adapter.match(url.path, 'GET')
123-
if endpoint != request.endpoint:
124-
abort(400)
125-
oob_code = parse_qs(url.query).get('oobCode')
126-
if not oob_code or len(oob_code) != 1:
127-
abort(400)
128-
action = OobAction.query.get(oob_code[0])
129-
if action is None:
130-
abort(400)
131-
base_url = Sites(action.site_id).get_base_url()
132-
if not base_url.endswith('/'):
133-
base_url += '/'
134-
return redirect('{}#sign-in'.format(base_url))
100+
@blueprint.route('/signed-in')
101+
def signed_in():
102+
return render_template('auth/close-window.html', message='You have signed in.')
135103

136104

137-
@blueprint.route('/site/<site_id>/sign-in-success')
138-
def sign_in_success(site_id):
139-
token = gitkit.verify_token()
140-
if token is None:
141-
abort(400)
142-
gitkit_account = gitkit.get_account_by_id(token['id'])
143-
account = Account.query.get(token['id'])
144-
if account is None:
145-
account = Account(id=token['id'])
146-
db.session.add(account)
147-
email = gitkit_account['email']
148-
account.email = email
149-
account.email_verified = gitkit_account['email_verified']
150-
account.name = gitkit_account['name']
151-
account.photo_url = gitkit_account['photo_url']
152-
db.session.flush()
153-
login_user(account)
154-
if account.email_verified:
155-
db.session.commit()
156-
return render_template('auth/close-window.html', message='You have signed in.')
157-
oob_link = gitkit.get_email_verification_link(email)
158-
action = OobAction(
159-
oob_code=parse_qs(urlparse(oob_link).query)['oobCode'][0],
160-
site_id=site_id,
161-
moment=datetime.utcnow())
162-
db.session.add(action)
163-
db.session.commit()
164-
text = render_template('auth/verify-email.txt', oob_link=oob_link)
165-
send(email, 'Verify email address', text)
166-
return render_template('auth/close-window.html', message='Email verification link sent.')
167-
168-
169-
@blueprint.route('/sign-out')
170-
def sign_out():
171-
logout_user()
172-
text = render_template('auth/close-window.html', message='You have signed out.')
173-
response = make_response(text)
174-
if not app.debug:
175-
gitkit.delete_token(response)
176-
return response
177-
178-
179-
@blueprint.route('/oob-action', methods={'POST'})
180-
def oob_action():
181-
url_adapter = _request_ctx_stack.top.url_adapter
182-
url = urlparse(request.referrer)
183-
if url.netloc != request.host:
184-
abort(400)
185-
endpoint, __ = url_adapter.match(url.path, 'GET')
186-
if endpoint != 'auth.widget':
187-
abort(400)
188-
next = parse_qs(url.query).get('next')
189-
if not next or len(next) != 1:
190-
abort(400)
191-
url = urlparse(next[0])
192-
if url.netloc != request.host:
193-
abort(400)
194-
endpoint, values = url_adapter.match(url.path, 'GET')
195-
if endpoint != 'auth.sign_in_success':
196-
abort(400)
197-
result = gitkit.get_oob_result()
198-
action = OobAction(
199-
oob_code=parse_qs(urlparse(result['oob_link']).query)['oobCode'][0],
200-
site_id=values['site_id'],
201-
moment=datetime.utcnow())
202-
db.session.add(action)
203-
db.session.commit()
204-
if result['action'] == 'changeEmail':
205-
text = render_template(
206-
'auth/change-email.txt',
207-
email=result['email'],
208-
new_email=result['new_email'],
209-
oob_link=result['oob_link'])
210-
send(result['new_email'], 'Change of email address', text)
211-
return result['response_body']
212-
if result['action'] == 'resetPassword':
213-
text = render_template(
214-
'auth/reset-password.txt',
215-
oob_link=result['oob_link'])
216-
send(result['email'], 'Password reset', text)
217-
return result['response_body']
218-
raise Exception('Invalid action {}'.format(result['action']))
105+
@blueprint.route('/signed-out')
106+
def signed_out():
107+
return render_template('auth/close-window.html', message='You have signed out.')
219108

220109

221110
@blueprint.route('/site/<site_id>/token')
222111
@jsonp
223112
def site_token(site_id):
224113
synchronize(site_id)
225-
next = url_for('.sign_in_success', site_id=site_id, _external=True)
226114
response = {
227-
'sign_in': url_for('.widget', mode='select', next=next, _external=True),
228-
'sign_out': url_for('.sign_out', _external=True),
115+
'sign_in': firebase.url_for(
116+
'widget',
117+
mode='select',
118+
next=url_for('auth.signed_in', _scheme='https', _external=True),
119+
),
120+
'sign_out': firebase.url_for(
121+
'sign_out',
122+
next=url_for('auth.signed_out', _scheme='https', _external=True),
123+
),
229124
}
230125
user = current_user._get_current_object()
231126
if not user.is_authenticated:
232127
response['status_code'] = 401
233128
return jsonify(response)
234-
if not user.email_verified:
235-
gitkit_account = gitkit.get_account_by_id(user.id)
236-
user.email_verified = gitkit_account['email_verified']
237-
db.session.commit()
238129
response['account'] = {
239130
'email': user.email,
240-
'email_verified': user.email_verified,
131+
'email_verified': True, # XXX user.email_verified,
241132
'name': user.name,
242133
}
243134
for roles in user.roles:
244135
if roles.site_id == site_id:
245136
response['account']['roles'] = roles.roles
246137
break
247-
if not user.email_verified or 'roles' not in response['account']:
138+
if 'roles' not in response['account']: # XXX or not user.email_verified
248139
response['status_code'] = 403
249140
return jsonify(response)
250141
response['status_code'] = 200
251142
response['access_token'] = token_serializer.dumps(user.id)
252143
return jsonify(response)
253144

254145

255-
def send(recipient, subject, text):
256-
mailgun.send({
257-
'from': 'no-reply@{}'.format(mailgun.domain),
258-
'to': recipient,
259-
'subject': subject,
260-
'text': text,
261-
})
262-
263-
264146
def synchronize(site_id):
265147
repository = Repository(site_id)
266148
site = Site.query.get(site_id)
@@ -280,11 +162,6 @@ def synchronize(site_id):
280162
site = Site(id=site_id)
281163
db.session.add(site)
282164
site.mtime = mtime
283-
gitkit_options = data.get('gitkit_options') or None
284-
if gitkit_options is not None:
285-
if not set(gitkit_options).issubset(GITKIT_OPTIONS):
286-
raise Exception
287-
site.gitkit_options = gitkit_options
288165
roles = []
289166
default_roles = data.get('default_roles', ['visitor'])
290167
for account in data.get('accounts', []):

app/jekylledit/controllers/base.py

-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@
77
from flask import Flask, request, url_for
88
from flask_babel import Babel
99
from ..ext.mailgun import Mailgun
10-
from ..ext.normalizeUnicode import normalizeUnicode
1110

1211

1312
app = Flask('jekylledit')
1413
app.config.from_object('{}.settings'.format(app.import_name))
1514

16-
1715
babel = Babel(app)
1816
if not app.debug:
1917
mailgun = Mailgun(app)

app/jekylledit/controllers/site.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
from flask_principal import Permission
1313
from pid import PidFile, PidFileAlreadyLockedError
1414

15+
from ..ext import normalizeUnicode
1516
from ..model import Repository, Roles, Sites
16-
from .base import app, mailgun, normalizeUnicode
17+
from .base import app, mailgun
1718
from .auth import authorization_required
1819

1920

0 commit comments

Comments
 (0)