2019-08-03 21:35:08 +01:00
|
|
|
from flask import Blueprint, session, flash, request, redirect, url_for, render_template
|
2019-07-29 11:44:10 +01:00
|
|
|
from google.cloud import firestore
|
2019-08-03 21:35:08 +01:00
|
|
|
from werkzeug.security import check_password_hash, generate_password_hash
|
2019-07-29 11:44:10 +01:00
|
|
|
|
2019-07-31 12:24:10 +01:00
|
|
|
import urllib
|
|
|
|
import datetime
|
2019-08-17 18:30:13 +01:00
|
|
|
import logging
|
2019-07-31 12:24:10 +01:00
|
|
|
from base64 import b64encode
|
|
|
|
import requests
|
|
|
|
|
2019-08-26 18:31:21 +01:00
|
|
|
import spotify.db.database as database
|
2019-07-31 12:24:10 +01:00
|
|
|
|
2019-07-29 11:44:10 +01:00
|
|
|
blueprint = Blueprint('authapi', __name__)
|
|
|
|
|
|
|
|
db = firestore.Client()
|
|
|
|
|
2019-08-17 18:30:13 +01:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2019-07-29 11:44:10 +01:00
|
|
|
|
|
|
|
@blueprint.route('/login', methods=['GET', 'POST'])
|
|
|
|
def login():
|
|
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
|
|
|
session.pop('username', None)
|
|
|
|
|
2019-08-05 21:43:09 +01:00
|
|
|
username = request.form.get('username', None)
|
|
|
|
password = request.form.get('password', None)
|
|
|
|
|
|
|
|
if username is None or password is None:
|
|
|
|
flash('malformed request')
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
username = username.lower()
|
2019-07-29 11:44:10 +01:00
|
|
|
|
2019-07-31 12:24:10 +01:00
|
|
|
users = database.get_user_query_stream(username)
|
2019-07-29 11:44:10 +01:00
|
|
|
|
2019-07-30 16:25:01 +01:00
|
|
|
if len(users) == 0:
|
|
|
|
flash('user not found')
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
if len(users) > 1:
|
2019-07-29 11:44:10 +01:00
|
|
|
flash('multiple users found')
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
doc = users[0].to_dict()
|
|
|
|
if doc is None:
|
|
|
|
flash('username not found')
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
if check_password_hash(doc['password'], password):
|
2019-07-31 12:24:10 +01:00
|
|
|
|
2019-08-03 21:35:08 +01:00
|
|
|
if doc['locked']:
|
2019-08-17 18:30:13 +01:00
|
|
|
logger.warning(f'locked account attempt {username}')
|
2019-08-03 21:35:08 +01:00
|
|
|
flash('account locked')
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
2019-07-31 12:24:10 +01:00
|
|
|
user_reference = db.collection(u'spotify_users').document(u'{}'.format(users[0].id))
|
|
|
|
user_reference.update({'last_login': datetime.datetime.utcnow()})
|
|
|
|
|
2019-08-17 18:30:13 +01:00
|
|
|
logger.info(f'success {username}')
|
2019-07-29 11:44:10 +01:00
|
|
|
session['username'] = username
|
|
|
|
return redirect(url_for('app_route'))
|
|
|
|
else:
|
2019-08-17 18:30:13 +01:00
|
|
|
logger.warning(f'failed attempt {username}')
|
2019-07-29 11:44:10 +01:00
|
|
|
flash('incorrect password')
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
else:
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route('/logout', methods=['GET', 'POST'])
|
|
|
|
def logout():
|
2019-08-17 18:30:13 +01:00
|
|
|
if 'username' in session:
|
|
|
|
logger.info(f'logged out {session["username"]}')
|
2019-07-29 11:44:10 +01:00
|
|
|
session.pop('username', None)
|
|
|
|
flash('logged out')
|
|
|
|
return redirect(url_for('index'))
|
2019-07-31 12:24:10 +01:00
|
|
|
|
|
|
|
|
2019-08-03 21:35:08 +01:00
|
|
|
@blueprint.route('/register', methods=['GET', 'POST'])
|
|
|
|
def register():
|
|
|
|
|
|
|
|
if 'username' in session:
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
if request.method == 'GET':
|
|
|
|
return render_template('register.html')
|
|
|
|
else:
|
|
|
|
|
2019-08-05 21:43:09 +01:00
|
|
|
username = request.form.get('username', None)
|
|
|
|
password = request.form.get('password', None)
|
|
|
|
password_again = request.form.get('password_again', None)
|
2019-08-03 21:35:08 +01:00
|
|
|
|
2019-08-05 21:43:09 +01:00
|
|
|
if username is None or password is None or password_again is None:
|
|
|
|
flash('malformed request')
|
2019-08-03 21:35:08 +01:00
|
|
|
return redirect('authapi.register')
|
|
|
|
|
2019-08-05 21:43:09 +01:00
|
|
|
username = username.lower()
|
|
|
|
|
2019-08-03 21:35:08 +01:00
|
|
|
if password != password_again:
|
|
|
|
flash('password mismatch')
|
|
|
|
return redirect('authapi.register')
|
|
|
|
|
2019-08-05 21:43:09 +01:00
|
|
|
if username in [i.to_dict()['username'] for i in
|
|
|
|
db.collection(u'spotify_users').where(u'username', u'==', username).stream()]:
|
|
|
|
flash('username already registered')
|
|
|
|
return redirect('authapi.register')
|
|
|
|
|
2019-08-03 21:35:08 +01:00
|
|
|
db.collection(u'spotify_users').add({
|
|
|
|
'access_token': None,
|
|
|
|
'email': None,
|
|
|
|
'last_login': datetime.datetime.utcnow(),
|
|
|
|
'locked': False,
|
|
|
|
'password': generate_password_hash(password),
|
|
|
|
'refresh_token': None,
|
|
|
|
'spotify_linked': False,
|
|
|
|
'type': 'user',
|
|
|
|
'username': username,
|
|
|
|
'validated': True
|
|
|
|
})
|
|
|
|
|
2019-08-17 18:30:13 +01:00
|
|
|
logger.info(f'new user {username}')
|
2019-08-03 21:35:08 +01:00
|
|
|
session['username'] = username
|
|
|
|
return redirect(url_for('authapi.auth'))
|
|
|
|
|
|
|
|
|
2019-07-31 12:24:10 +01:00
|
|
|
@blueprint.route('/spotify')
|
|
|
|
def auth():
|
|
|
|
|
|
|
|
if 'username' in session:
|
|
|
|
|
|
|
|
client_id = db.document('key/spotify').get().to_dict()['clientid']
|
|
|
|
params = urllib.parse.urlencode(
|
|
|
|
{
|
|
|
|
'client_id': client_id,
|
|
|
|
'response_type': 'code',
|
2019-09-25 19:28:38 +01:00
|
|
|
'scope': 'playlist-modify-public playlist-modify-private playlist-read-private user-read-playback-state user-modify-playback-state user-library-read',
|
2019-07-31 12:24:10 +01:00
|
|
|
'redirect_uri': 'https://spotify.sarsoo.xyz/auth/spotify/token'
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
return redirect(urllib.parse.urlunparse(['https', 'accounts.spotify.com', 'authorize', '', params, '']))
|
|
|
|
|
2019-07-31 20:31:01 +01:00
|
|
|
return redirect(url_for('index'))
|
2019-07-31 12:24:10 +01:00
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route('/spotify/token')
|
|
|
|
def token():
|
|
|
|
|
|
|
|
if 'username' in session:
|
|
|
|
|
|
|
|
code = request.args.get('code', None)
|
|
|
|
if code is None:
|
2019-08-05 21:43:09 +01:00
|
|
|
flash('authorization failed')
|
|
|
|
return redirect('app_route')
|
2019-07-31 12:24:10 +01:00
|
|
|
else:
|
|
|
|
app_credentials = db.document('key/spotify').get().to_dict()
|
|
|
|
|
|
|
|
idsecret = b64encode(bytes(app_credentials['clientid'] + ':' + app_credentials['clientsecret'], "utf-8")).decode("ascii")
|
|
|
|
headers = {'Authorization': 'Basic %s' % idsecret}
|
|
|
|
|
|
|
|
data = {
|
|
|
|
'grant_type': 'authorization_code',
|
|
|
|
'code': code,
|
|
|
|
'redirect_uri': 'https://spotify.sarsoo.xyz/auth/spotify/token'
|
|
|
|
}
|
|
|
|
|
|
|
|
req = requests.post('https://accounts.spotify.com/api/token', data=data, headers=headers)
|
|
|
|
|
2019-08-05 21:43:09 +01:00
|
|
|
if 200 <= req.status_code < 300:
|
|
|
|
|
|
|
|
resp = req.json()
|
|
|
|
|
|
|
|
user_reference = database.get_user_doc_ref(session['username'])
|
2019-07-31 12:24:10 +01:00
|
|
|
|
2019-08-05 21:43:09 +01:00
|
|
|
user_reference.update({
|
|
|
|
'access_token': resp['access_token'],
|
|
|
|
'refresh_token': resp['refresh_token'],
|
2019-10-01 19:20:22 +01:00
|
|
|
'last_refreshed': datetime.datetime.now(datetime.timezone.utc),
|
|
|
|
'token_expiry': resp['expires_in'],
|
2019-08-05 21:43:09 +01:00
|
|
|
'spotify_linked': True
|
|
|
|
})
|
2019-07-31 12:24:10 +01:00
|
|
|
|
2019-08-05 21:43:09 +01:00
|
|
|
else:
|
|
|
|
flash('http error on token request')
|
|
|
|
return redirect('app_route')
|
2019-07-31 12:24:10 +01:00
|
|
|
|
|
|
|
return redirect('/app/settings/spotify')
|
|
|
|
|
2019-07-31 20:31:01 +01:00
|
|
|
return redirect(url_for('index'))
|
2019-07-31 12:24:10 +01:00
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route('/spotify/deauth')
|
|
|
|
def deauth():
|
|
|
|
|
|
|
|
if 'username' in session:
|
|
|
|
|
|
|
|
user_reference = database.get_user_doc_ref(session['username'])
|
|
|
|
|
|
|
|
user_reference.update({
|
|
|
|
'access_token': None,
|
|
|
|
'refresh_token': None,
|
2019-10-01 19:20:22 +01:00
|
|
|
'last_refreshed': datetime.datetime.now(datetime.timezone.utc),
|
|
|
|
'token_expiry': None,
|
2019-07-31 12:24:10 +01:00
|
|
|
'spotify_linked': False
|
|
|
|
})
|
|
|
|
|
|
|
|
return redirect('/app/settings/spotify')
|
|
|
|
|
2019-07-31 20:31:01 +01:00
|
|
|
return redirect(url_for('index'))
|