Commit 3ccb70d0 authored by Agate's avatar Agate 💬

Fixed #15 again, now check authorization also using query param

parent 795cd7be
BACKEND_URL=http://localhost:12081
BACKEND_URL=http://localhost:6001
YOUTUBE_API_KEY=
API_AUTHENTICATION_REQUIRED=False
API_AUTHENTICATION_REQUIRED=True
......@@ -288,6 +288,7 @@ REST_FRAMEWORK = {
'PAGE_SIZE': 25,
'DEFAULT_AUTHENTICATION_CLASSES': (
'funkwhale_api.common.authentication.JSONWebTokenAuthenticationQS',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
......
from rest_framework import exceptions
from rest_framework_jwt import authentication
from rest_framework_jwt.settings import api_settings
class JSONWebTokenAuthenticationQS(
authentication.BaseJSONWebTokenAuthentication):
www_authenticate_realm = 'api'
def get_jwt_value(self, request):
token = request.query_params.get('jwt')
if 'jwt' in request.query_params and not token:
msg = _('Invalid Authorization header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
return token
def authenticate_header(self, request):
return '{0} realm="{1}"'.format(
api_settings.JWT_AUTH_HEADER_PREFIX, self.www_authenticate_realm)
from test_plus.test import TestCase
from rest_framework_jwt.settings import api_settings
from funkwhale_api.users.models import User
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class TestJWTQueryString(TestCase):
www_authenticate_realm = 'api'
def test_can_authenticate_using_token_param_in_url(self):
user = User.objects.create_superuser(
username='test', email='test@test.com', password='test')
url = self.reverse('api:v1:tracks-list')
with self.settings(API_AUTHENTICATION_REQUIRED=True):
response = self.client.get(url)
self.assertEqual(response.status_code, 401)
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
print(payload, token)
with self.settings(API_AUTHENTICATION_REQUIRED=True):
response = self.client.get(url, data={
'jwt': token
})
self.assertEqual(response.status_code, 200)
import os
import json
import unicodedata
import urllib
from django.core.urlresolvers import reverse
from django.db import models, transaction
from django.db.models.functions import Length
......@@ -137,8 +139,10 @@ class TrackFileViewSet(viewsets.ReadOnlyModelViewSet):
return Response(status=404)
response = Response()
response["Content-Disposition"] = "attachment; filename={0}".format(
f.audio_file.name)
filename = "filename*=UTF-8''{}{}".format(
urllib.parse.quote(f.track.full_name),
os.path.splitext(f.audio_file.name)[-1])
response["Content-Disposition"] = "attachment; {}".format(filename)
response['X-Accel-Redirect'] = "{}{}".format(
settings.PROTECT_FILES_PATH,
f.audio_file.url)
......
......@@ -5,6 +5,8 @@ import Audio from '@/audio'
import backend from '@/audio/backend'
import radios from '@/radios'
import Vue from 'vue'
import url from '@/utils/url'
import auth from '@/auth'
class Queue {
constructor (options = {}) {
......@@ -181,7 +183,17 @@ class Queue {
if (!file) {
return this.next()
}
this.audio = new Audio(backend.absoluteUrl(file.path), {
let path = backend.absoluteUrl(file.path)
if (auth.user.authenticated) {
// we need to send the token directly in url
// so authentication can be checked by the backend
// because for audio files we cannot use the regular Authentication
// header
path = url.updateQueryString(path, 'jwt', auth.getAuthToken())
}
this.audio = new Audio(path, {
preload: true,
autoplay: true,
rate: 1,
......
......@@ -50,7 +50,7 @@ export default {
checkAuth () {
logger.default.info('Checking authentication...')
var jwt = cache.get('token')
var jwt = this.getAuthToken()
var username = cache.get('username')
if (jwt) {
this.user.authenticated = true
......@@ -63,9 +63,13 @@ export default {
}
},
getAuthToken () {
return cache.get('token')
},
// The object to be passed as a header for authenticated requests
getAuthHeader () {
return 'JWT ' + cache.get('token')
return 'JWT ' + this.getAuthToken()
},
fetchProfile () {
......
......@@ -61,6 +61,8 @@
<script>
import auth from '@/auth'
import url from '@/utils/url'
import logger from '@/logging'
import backend from '@/audio/backend'
import PlayButton from '@/components/audio/PlayButton'
......@@ -121,7 +123,11 @@ export default {
},
downloadUrl () {
if (this.track.files.length > 0) {
return backend.absoluteUrl(this.track.files[0].path)
let u = backend.absoluteUrl(this.track.files[0].path)
if (auth.user.authenticated) {
u = url.updateQueryString(u, 'jwt', auth.getAuthToken())
}
return u
}
},
lyricsSearchUrl () {
......
export default {
updateQueryString (uri, key, value) {
var re = new RegExp('([?&])' + key + '=.*?(&|$)', 'i')
var separator = uri.indexOf('?') !== -1 ? '&' : '?'
if (uri.match(re)) {
return uri.replace(re, '$1' + key + '=' + value + '$2')
} else {
return uri + separator + key + '=' + value
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment