|
|
@ -16,8 +16,8 @@ from psycopg2 import sql
|
|
|
|
from common import database, PromLogCountsHandler, install_stacksampler
|
|
|
|
from common import database, PromLogCountsHandler, install_stacksampler
|
|
|
|
from common.flask_stats import request_stats, after_request
|
|
|
|
from common.flask_stats import request_stats, after_request
|
|
|
|
|
|
|
|
|
|
|
|
from google.oauth2 import id_token
|
|
|
|
import google.oauth2.id_token
|
|
|
|
from google.auth.transport import requests
|
|
|
|
import google.auth.transport.requests
|
|
|
|
|
|
|
|
|
|
|
|
psycopg2.extras.register_uuid()
|
|
|
|
psycopg2.extras.register_uuid()
|
|
|
|
app = flask.Flask('thrimshim')
|
|
|
|
app = flask.Flask('thrimshim')
|
|
|
@ -41,51 +41,48 @@ def cors(app):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def authenticate(f):
|
|
|
|
def auth_wrapper(f):
|
|
|
|
"""Authenticate a token against the database.
|
|
|
|
"""Authenticate a token against the database.
|
|
|
|
|
|
|
|
|
|
|
|
Reference: https://developers.google.com/identity/sign-in/web/backend-auth"""
|
|
|
|
Reference: https://developers.google.com/identity/sign-in/web/backend-auth"""
|
|
|
|
@wraps(f)
|
|
|
|
@wraps(f)
|
|
|
|
def decorated_function(*args, **kwargs):
|
|
|
|
def decorated_function(*args, **kwargs):
|
|
|
|
if flask.request.method == 'POST':
|
|
|
|
|
|
|
|
if app.no_authentication:
|
|
|
|
if app.no_authentication:
|
|
|
|
return f(*args, editor='NOT_AUTH', **kwargs)
|
|
|
|
return f(*args, editor='NOT_AUTH', **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
userToken = flask.request.json['token']
|
|
|
|
userToken = flask.request.json['token']
|
|
|
|
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
|
|
|
|
return 'User token required', 401
|
|
|
|
# check whether token is valid
|
|
|
|
# check whether token is valid
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
idinfo = id_token.verify_oauth2_token(userToken, requests.Request(), None)
|
|
|
|
idinfo = google.oauth2.id_token.verify_oauth2_token(userToken, google.auth.transport.requests.Request(), None)
|
|
|
|
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
|
|
|
|
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
|
|
|
|
raise ValueError('Wrong issuer.')
|
|
|
|
raise ValueError('Wrong issuer.')
|
|
|
|
except ValueError:
|
|
|
|
except ValueError:
|
|
|
|
return 'Invalid token. Access denied.', 403
|
|
|
|
return 'Invalid token. Access denied.', 403
|
|
|
|
|
|
|
|
|
|
|
|
# check whether user is in the database
|
|
|
|
# check whether user is in the database
|
|
|
|
email = idinfo['email']
|
|
|
|
email = idinfo['email'].lower()
|
|
|
|
conn = app.db_manager.get_conn()
|
|
|
|
conn = app.db_manager.get_conn()
|
|
|
|
results = database.query(conn, """
|
|
|
|
results = database.query(conn, """
|
|
|
|
SELECT email
|
|
|
|
SELECT email
|
|
|
|
FROM editors
|
|
|
|
FROM editors
|
|
|
|
WHERE email = %s""", email)
|
|
|
|
WHERE lower(email) = %s""", email)
|
|
|
|
row = results.fetchone()
|
|
|
|
row = results.fetchone()
|
|
|
|
if row is None:
|
|
|
|
if row is None:
|
|
|
|
return 'Unknown user. Access denied.', 403
|
|
|
|
return 'Unknown user. Access denied.', 403
|
|
|
|
|
|
|
|
|
|
|
|
return f(*args, editor=email, **kwargs)
|
|
|
|
return f(*args, editor=email, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
return f(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return decorated_function
|
|
|
|
return decorated_function
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/thrimshim/auth-test', methods=['GET', 'POST'])
|
|
|
|
@app.route('/thrimshim/auth-test', methods=['POST'])
|
|
|
|
@request_stats
|
|
|
|
@request_stats
|
|
|
|
@authenticate
|
|
|
|
@auth_wrapper
|
|
|
|
def test(editor=None):
|
|
|
|
def test(editor=None):
|
|
|
|
if flask.request.method == 'POST':
|
|
|
|
|
|
|
|
return json.dumps(editor)
|
|
|
|
return json.dumps(editor)
|
|
|
|
else:
|
|
|
|
|
|
|
|
return "Hello World!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/metrics')
|
|
|
|
@app.route('/metrics')
|
|
|
|
@request_stats
|
|
|
|
@request_stats
|
|
|
@ -115,17 +112,9 @@ def get_all_rows():
|
|
|
|
logging.info('All rows fetched')
|
|
|
|
logging.info('All rows fetched')
|
|
|
|
return json.dumps(rows)
|
|
|
|
return json.dumps(rows)
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/thrimshim/<uuid:ident>', methods=['GET', 'POST'])
|
|
|
|
|
|
|
|
@request_stats
|
|
|
|
|
|
|
|
@authenticate
|
|
|
|
|
|
|
|
def thrimshim(ident, editor=None):
|
|
|
|
|
|
|
|
"""Comunicate between Thrimbletrimmer and the Wubloader database."""
|
|
|
|
|
|
|
|
if flask.request.method == 'POST':
|
|
|
|
|
|
|
|
row = flask.request.json
|
|
|
|
|
|
|
|
return update_row(ident, row, editor)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
return get_row(ident)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/thrimshim/<uuid:ident>', methods=['GET'])
|
|
|
|
|
|
|
|
@request_stats
|
|
|
|
def get_row(ident):
|
|
|
|
def get_row(ident):
|
|
|
|
"""Gets the row from the database with id == ident."""
|
|
|
|
"""Gets the row from the database with id == ident."""
|
|
|
|
conn = app.db_manager.get_conn()
|
|
|
|
conn = app.db_manager.get_conn()
|
|
|
@ -149,7 +138,11 @@ def get_row(ident):
|
|
|
|
logging.info('Row {} fetched'.format(ident))
|
|
|
|
logging.info('Row {} fetched'.format(ident))
|
|
|
|
return json.dumps(response)
|
|
|
|
return json.dumps(response)
|
|
|
|
|
|
|
|
|
|
|
|
def update_row(ident, new_row, editor):
|
|
|
|
@app.route('/thrimshim/<uuid:ident>', methods=['POST'])
|
|
|
|
|
|
|
|
@request_stats
|
|
|
|
|
|
|
|
@auth_wrapper
|
|
|
|
|
|
|
|
def update_row(ident, editor=None):
|
|
|
|
|
|
|
|
new_row = flask.request.json
|
|
|
|
"""Updates row of database with id = ident with the edit columns in
|
|
|
|
"""Updates row of database with id = ident with the edit columns in
|
|
|
|
new_row."""
|
|
|
|
new_row."""
|
|
|
|
|
|
|
|
|
|
|
@ -226,11 +219,11 @@ def update_row(ident, new_row, editor):
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/thrimshim/manual-link/<uuid:ident>', methods=['POST'])
|
|
|
|
@app.route('/thrimshim/manual-link/<uuid:ident>', methods=['POST'])
|
|
|
|
@request_stats
|
|
|
|
@request_stats
|
|
|
|
@authenticate
|
|
|
|
@auth_wrapper
|
|
|
|
def manual_link(ident, editor=None):
|
|
|
|
def manual_link(ident, editor=None):
|
|
|
|
"""Manually set a video_link if the state is 'UNEDITED' or 'DONE' and the
|
|
|
|
"""Manually set a video_link if the state is 'UNEDITED' or 'DONE' and the
|
|
|
|
upload_location is 'manual'."""
|
|
|
|
upload_location is 'manual'."""
|
|
|
|
link = flask.request.json
|
|
|
|
link = flask.request.json['link']
|
|
|
|
conn = app.db_manager.get_conn()
|
|
|
|
conn = app.db_manager.get_conn()
|
|
|
|
results = database.query(conn, """
|
|
|
|
results = database.query(conn, """
|
|
|
|
SELECT id, state, upload_location
|
|
|
|
SELECT id, state, upload_location
|
|
|
@ -254,7 +247,7 @@ def manual_link(ident, editor=None):
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/thrimshim/reset/<uuid:ident>', methods=['POST'])
|
|
|
|
@app.route('/thrimshim/reset/<uuid:ident>', methods=['POST'])
|
|
|
|
@request_stats
|
|
|
|
@request_stats
|
|
|
|
@authenticate
|
|
|
|
@auth_wrapper
|
|
|
|
def reset_row(ident, editor=None):
|
|
|
|
def reset_row(ident, editor=None):
|
|
|
|
"""Clear state and video_link columns and reset state to 'UNEDITED'."""
|
|
|
|
"""Clear state and video_link columns and reset state to 'UNEDITED'."""
|
|
|
|
conn = app.db_manager.get_conn()
|
|
|
|
conn = app.db_manager.get_conn()
|
|
|
|