Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
Philipp Wolfer
funkwhale
Commits
88de9976
Verified
Commit
88de9976
authored
Apr 24, 2021
by
Georg Krause
Browse files
Remove JWT related code
parent
f1f9f935
Changes
13
Hide whitespace changes
Inline
Side-by-side
.env.dev
View file @
88de9976
...
...
@@ -3,7 +3,7 @@ DJANGO_SETTINGS_MODULE=config.settings.local
DJANGO_SECRET_KEY=dev
C_FORCE_ROOT=true
FUNKWHALE_HOSTNAME=localhost
FUNKWHALE_PROTOCOL=http
FUNKWHALE_PROTOCOL=http
s
PYTHONDONTWRITEBYTECODE=true
VUE_PORT=8080
MUSIC_DIRECTORY_PATH=/music
...
...
api/config/api_urls.py
View file @
88de9976
...
...
@@ -10,7 +10,6 @@ from funkwhale_api.music import views
from
funkwhale_api.playlists
import
views
as
playlists_views
from
funkwhale_api.subsonic.views
import
SubsonicViewSet
from
funkwhale_api.tags
import
views
as
tags_views
from
funkwhale_api.users
import
jwt_views
router
=
common_routers
.
OptionalSlashRouter
()
router
.
register
(
r
"activity"
,
activity_views
.
ActivityViewSet
,
"activity"
)
...
...
@@ -84,8 +83,6 @@ v1_patterns += [
r
"^oauth/"
,
include
((
"funkwhale_api.users.oauth.urls"
,
"oauth"
),
namespace
=
"oauth"
),
),
url
(
r
"^token/?$"
,
jwt_views
.
obtain_jwt_token
,
name
=
"token"
),
url
(
r
"^token/refresh/?$"
,
jwt_views
.
refresh_jwt_token
,
name
=
"token_refresh"
),
url
(
r
"^rate-limit/?$"
,
common_views
.
RateLimitView
.
as_view
(),
name
=
"rate-limit"
),
url
(
r
"^text-preview/?$"
,
common_views
.
TextPreviewView
.
as_view
(),
name
=
"text-preview"
...
...
api/config/settings/common.py
View file @
88de9976
...
...
@@ -2,7 +2,6 @@
from
__future__
import
absolute_import
,
unicode_literals
from
collections
import
OrderedDict
import
datetime
import
logging.config
import
sys
...
...
@@ -812,13 +811,6 @@ def get_user_secret_key(user):
return
settings
.
SECRET_KEY
+
str
(
user
.
secret_key
)
JWT_AUTH
=
{
"JWT_ALLOW_REFRESH"
:
True
,
"JWT_EXPIRATION_DELTA"
:
datetime
.
timedelta
(
days
=
7
),
"JWT_REFRESH_EXPIRATION_DELTA"
:
datetime
.
timedelta
(
days
=
30
),
"JWT_AUTH_HEADER_PREFIX"
:
"JWT"
,
"JWT_GET_USER_SECRET_KEY"
:
get_user_secret_key
,
}
OLD_PASSWORD_FIELD_ENABLED
=
True
AUTH_PASSWORD_VALIDATORS
=
[
{
...
...
@@ -857,9 +849,6 @@ REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES"
:
(
"funkwhale_api.common.authentication.OAuth2Authentication"
,
"funkwhale_api.common.authentication.ApplicationTokenAuthentication"
,
"funkwhale_api.common.authentication.JSONWebTokenAuthenticationQS"
,
"funkwhale_api.common.authentication.BearerTokenHeaderAuth"
,
"funkwhale_api.common.authentication.JSONWebTokenAuthentication"
,
"rest_framework.authentication.BasicAuthentication"
,
"rest_framework.authentication.SessionAuthentication"
,
),
...
...
@@ -998,14 +987,6 @@ THROTTLING_RATES = {
"rate"
:
THROTTLING_USER_RATES
.
get
(
"login"
,
"30/hour"
),
"description"
:
"Login"
,
},
"jwt-login"
:
{
"rate"
:
THROTTLING_USER_RATES
.
get
(
"jwt-login"
,
"30/hour"
),
"description"
:
"JWT token creation"
,
},
"jwt-refresh"
:
{
"rate"
:
THROTTLING_USER_RATES
.
get
(
"jwt-refresh"
,
"30/hour"
),
"description"
:
"JWT token refresh"
,
},
"signup"
:
{
"rate"
:
THROTTLING_USER_RATES
.
get
(
"signup"
,
"10/day"
),
"description"
:
"Account creation"
,
...
...
@@ -1052,7 +1033,6 @@ REST_AUTH_SERIALIZERS = {
"PASSWORD_RESET_SERIALIZER"
:
"funkwhale_api.users.serializers.PasswordResetSerializer"
# noqa
}
REST_SESSION_LOGIN
=
False
REST_USE_JWT
=
True
ATOMIC_REQUESTS
=
False
USE_X_FORWARDED_HOST
=
True
...
...
api/funkwhale_api/common/authentication.py
View file @
88de9976
from
django.conf
import
settings
from
django.utils.encoding
import
smart_text
from
django.utils.translation
import
ugettext
as
_
from
django.core.cache
import
cache
...
...
@@ -9,8 +8,6 @@ from oauth2_provider.contrib.rest_framework.authentication import (
OAuth2Authentication
as
BaseOAuth2Authentication
,
)
from
rest_framework
import
exceptions
from
rest_framework_jwt
import
authentication
from
rest_framework_jwt.settings
import
api_settings
from
funkwhale_api.users
import
models
as
users_models
...
...
@@ -76,116 +73,3 @@ class ApplicationTokenAuthentication(object):
request
.
scopes
=
application
.
scope
.
split
()
return
user
,
None
class
BaseJsonWebTokenAuth
(
object
):
def
authenticate
(
self
,
request
):
try
:
return
super
().
authenticate
(
request
)
except
UnverifiedEmail
as
e
:
msg
=
_
(
"You need to verify your email address."
)
resend_confirmation_email
(
request
,
e
.
user
)
raise
exceptions
.
AuthenticationFailed
(
msg
)
def
authenticate_credentials
(
self
,
payload
):
"""
We have to implement this method by hand to ensure we can check that the
User has a verified email, if required
"""
User
=
authentication
.
get_user_model
()
username
=
authentication
.
jwt_get_username_from_payload
(
payload
)
if
not
username
:
msg
=
_
(
"Invalid payload."
)
raise
exceptions
.
AuthenticationFailed
(
msg
)
try
:
user
=
User
.
objects
.
get_by_natural_key
(
username
)
except
User
.
DoesNotExist
:
msg
=
_
(
"Invalid signature."
)
raise
exceptions
.
AuthenticationFailed
(
msg
)
if
not
user
.
is_active
:
msg
=
_
(
"User account is disabled."
)
raise
exceptions
.
AuthenticationFailed
(
msg
)
if
should_verify_email
(
user
):
raise
UnverifiedEmail
(
user
)
return
user
class
JSONWebTokenAuthenticationQS
(
BaseJsonWebTokenAuth
,
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
)
class
BearerTokenHeaderAuth
(
BaseJsonWebTokenAuth
,
authentication
.
BaseJSONWebTokenAuthentication
):
"""
For backward compatibility purpose, we used Authorization: JWT <token>
but Authorization: Bearer <token> is probably better.
"""
www_authenticate_realm
=
"api"
def
get_jwt_value
(
self
,
request
):
auth
=
authentication
.
get_authorization_header
(
request
).
split
()
auth_header_prefix
=
"bearer"
if
not
auth
:
if
api_settings
.
JWT_AUTH_COOKIE
:
return
request
.
COOKIES
.
get
(
api_settings
.
JWT_AUTH_COOKIE
)
return
None
if
smart_text
(
auth
[
0
].
lower
())
!=
auth_header_prefix
:
return
None
if
len
(
auth
)
==
1
:
msg
=
_
(
"Invalid Authorization header. No credentials provided."
)
raise
exceptions
.
AuthenticationFailed
(
msg
)
elif
len
(
auth
)
>
2
:
msg
=
_
(
"Invalid Authorization header. Credentials string "
"should not contain spaces."
)
raise
exceptions
.
AuthenticationFailed
(
msg
)
return
auth
[
1
]
def
authenticate_header
(
self
,
request
):
return
'{0} realm="{1}"'
.
format
(
"Bearer"
,
self
.
www_authenticate_realm
)
def
authenticate
(
self
,
request
):
auth
=
super
().
authenticate
(
request
)
if
auth
:
if
not
auth
[
0
].
actor
:
auth
[
0
].
create_actor
()
return
auth
class
JSONWebTokenAuthentication
(
BaseJsonWebTokenAuth
,
authentication
.
JSONWebTokenAuthentication
):
def
authenticate
(
self
,
request
):
auth
=
super
().
authenticate
(
request
)
if
auth
:
if
not
auth
[
0
].
actor
:
auth
[
0
].
create_actor
()
return
auth
api/funkwhale_api/users/jwt_views.py
deleted
100644 → 0
View file @
f1f9f935
from
rest_framework_jwt
import
views
as
jwt_views
from
.
import
serializers
class
ObtainJSONWebToken
(
jwt_views
.
ObtainJSONWebToken
):
throttling_scopes
=
{
"*"
:
{
"anonymous"
:
"jwt-login"
,
"authenticated"
:
"jwt-login"
}}
serializer_class
=
serializers
.
JSONWebTokenSerializer
class
RefreshJSONWebToken
(
jwt_views
.
RefreshJSONWebToken
):
throttling_scopes
=
{
"*"
:
{
"anonymous"
:
"jwt-refresh"
,
"authenticated"
:
"jwt-refresh"
}
}
obtain_jwt_token
=
ObtainJSONWebToken
.
as_view
()
refresh_jwt_token
=
RefreshJSONWebToken
.
as_view
()
api/funkwhale_api/users/serializers.py
View file @
88de9976
...
...
@@ -10,10 +10,8 @@ from allauth.account import models as allauth_models
from
rest_auth.serializers
import
PasswordResetSerializer
as
PRS
from
rest_auth.registration.serializers
import
RegisterSerializer
as
RS
,
get_adapter
from
rest_framework
import
serializers
from
rest_framework_jwt
import
serializers
as
jwt_serializers
from
funkwhale_api.activity
import
serializers
as
activity_serializers
from
funkwhale_api.common
import
authentication
from
funkwhale_api.common
import
models
as
common_models
from
funkwhale_api.common
import
preferences
from
funkwhale_api.common
import
serializers
as
common_serializers
...
...
@@ -42,15 +40,6 @@ username_validators = [ASCIIUsernameValidator()]
NOOP
=
object
()
class
JSONWebTokenSerializer
(
jwt_serializers
.
JSONWebTokenSerializer
):
def
validate
(
self
,
data
):
try
:
return
super
().
validate
(
data
)
except
authentication
.
UnverifiedEmail
as
e
:
authentication
.
send_email_confirmation
(
self
.
context
[
"request"
],
e
.
user
)
raise
serializers
.
ValidationError
(
"Please verify your email address."
)
class
RegisterSerializer
(
RS
):
invitation
=
serializers
.
CharField
(
required
=
False
,
allow_null
=
True
,
allow_blank
=
True
...
...
api/requirements/base.txt
View file @
88de9976
...
...
@@ -25,7 +25,6 @@ celery~=4.4.0
django-cors-headers~=3.4.0
musicbrainzngs~=0.7.1
djangorestframework~=3.11.0
djangorestframework-jwt~=1.11.0
arrow~=0.15.5
persisting-theory~=0.2.0
django-versatileimagefield~=2.0.0
...
...
api/tests/common/test_authentication.py
View file @
88de9976
import
pytest
from
rest_framework
import
exceptions
from
rest_framework_jwt.settings
import
api_settings
as
jwt_settings
from
funkwhale_api.common
import
authentication
...
...
@@ -33,35 +30,6 @@ def test_should_verify_email(
assert
authentication
.
should_verify_email
(
user
)
is
expected
@
pytest
.
mark
.
parametrize
(
"setting_value, verified_email, expected"
,
[
(
"mandatory"
,
False
,
True
),
(
"optional"
,
False
,
False
),
(
"mandatory"
,
True
,
False
),
(
"optional"
,
True
,
False
),
],
)
def
test_json_webtoken_auth_verify_email_validity
(
setting_value
,
verified_email
,
expected
,
factories
,
settings
,
mocker
,
api_request
):
settings
.
ACCOUNT_EMAIL_VERIFICATION
=
setting_value
user
=
factories
[
"users.User"
](
verified_email
=
verified_email
)
should_verify
=
mocker
.
spy
(
authentication
,
"should_verify_email"
)
payload
=
jwt_settings
.
JWT_PAYLOAD_HANDLER
(
user
)
token
=
jwt_settings
.
JWT_ENCODE_HANDLER
(
payload
)
request
=
api_request
.
get
(
"/"
,
HTTP_AUTHORIZATION
=
"JWT {}"
.
format
(
token
))
auth
=
authentication
.
JSONWebTokenAuthentication
()
if
expected
is
False
:
assert
auth
.
authenticate
(
request
)[
0
]
==
user
else
:
with
pytest
.
raises
(
exceptions
.
AuthenticationFailed
,
match
=
r
".*verify.*"
):
auth
.
authenticate
(
request
)
should_verify
.
assert_called_once_with
(
user
)
def
test_app_token_authentication
(
factories
,
api_request
):
user
=
factories
[
"users.User"
]()
app
=
factories
[
"users.Application"
](
user
=
user
,
scope
=
"read write"
)
...
...
api/tests/common/test_routers.py
View file @
88de9976
...
...
@@ -22,8 +22,6 @@ from django import urls
"/api/v1/manage/accounts"
,
"/api/v1/oauth/apps"
,
"/api/v1/moderation/content-filters"
,
"/api/v1/token"
,
"/api/v1/token/refresh"
,
"/api/v1/instance/settings"
,
"/api/v1/instance/nodeinfo/2.0"
,
],
...
...
api/tests/test_auth.py
View file @
88de9976
...
...
@@ -5,20 +5,6 @@ jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler
=
api_settings
.
JWT_ENCODE_HANDLER
def
test_can_authenticate_using_jwt_token_param_in_url
(
factories
,
preferences
,
client
):
user
=
factories
[
"users.User"
]()
preferences
[
"common__api_authentication_required"
]
=
True
url
=
reverse
(
"api:v1:tracks-list"
)
response
=
client
.
get
(
url
)
assert
response
.
status_code
==
401
payload
=
jwt_payload_handler
(
user
)
token
=
jwt_encode_handler
(
payload
)
response
=
client
.
get
(
url
,
data
=
{
"jwt"
:
token
})
assert
response
.
status_code
==
200
def
test_can_authenticate_using_oauth_token_param_in_url
(
factories
,
preferences
,
client
,
mocker
):
...
...
api/tests/users/test_jwt.py
deleted
100644 → 0
View file @
f1f9f935
import
pytest
from
jwt.exceptions
import
DecodeError
from
rest_framework_jwt.settings
import
api_settings
def
test_can_invalidate_token_when_changing_user_secret_key
(
factories
):
user
=
factories
[
"users.User"
]()
u1
=
user
.
secret_key
jwt_payload_handler
=
api_settings
.
JWT_PAYLOAD_HANDLER
jwt_encode_handler
=
api_settings
.
JWT_ENCODE_HANDLER
payload
=
jwt_payload_handler
(
user
)
payload
=
jwt_encode_handler
(
payload
)
# this should work
api_settings
.
JWT_DECODE_HANDLER
(
payload
)
# now we update the secret key
user
.
update_secret_key
()
user
.
save
()
assert
user
.
secret_key
!=
u1
# token should be invalid
with
pytest
.
raises
(
DecodeError
):
api_settings
.
JWT_DECODE_HANDLER
(
payload
)
def
test_can_invalidate_token_when_changing_settings_secret_key
(
factories
,
settings
):
settings
.
SECRET_KEY
=
"test1"
user
=
factories
[
"users.User"
]()
jwt_payload_handler
=
api_settings
.
JWT_PAYLOAD_HANDLER
jwt_encode_handler
=
api_settings
.
JWT_ENCODE_HANDLER
payload
=
jwt_payload_handler
(
user
)
payload
=
jwt_encode_handler
(
payload
)
# this should work
api_settings
.
JWT_DECODE_HANDLER
(
payload
)
# now we update the secret key
settings
.
SECRET_KEY
=
"test2"
# token should be invalid
with
pytest
.
raises
(
DecodeError
):
api_settings
.
JWT_DECODE_HANDLER
(
payload
)
api/tests/users/test_views.py
View file @
88de9976
...
...
@@ -134,42 +134,6 @@ def test_can_fetch_data_from_api(api_client, factories):
)
def
test_can_get_token_via_api
(
api_client
,
factories
):
user
=
factories
[
"users.User"
]()
url
=
reverse
(
"api:v1:token"
)
payload
=
{
"username"
:
user
.
username
,
"password"
:
"test"
}
response
=
api_client
.
post
(
url
,
payload
)
assert
response
.
status_code
==
200
assert
"token"
in
response
.
data
def
test_can_get_token_via_api_inactive
(
api_client
,
factories
):
user
=
factories
[
"users.User"
](
is_active
=
False
)
url
=
reverse
(
"api:v1:token"
)
payload
=
{
"username"
:
user
.
username
,
"password"
:
"test"
}
response
=
api_client
.
post
(
url
,
payload
)
assert
response
.
status_code
==
400
def
test_can_refresh_token_via_api
(
api_client
,
factories
,
mocker
):
# first, we get a token
user
=
factories
[
"users.User"
]()
url
=
reverse
(
"api:v1:token"
)
payload
=
{
"username"
:
user
.
username
,
"password"
:
"test"
}
response
=
api_client
.
post
(
url
,
payload
)
assert
response
.
status_code
==
200
token
=
response
.
data
[
"token"
]
url
=
reverse
(
"api:v1:token_refresh"
)
response
=
api_client
.
post
(
url
,
{
"token"
:
token
})
assert
response
.
status_code
==
200
assert
"token"
in
response
.
data
def
test_changing_password_updates_secret_key
(
logged_in_api_client
):
user
=
logged_in_api_client
.
user
password
=
user
.
password
...
...
@@ -488,40 +452,6 @@ def test_signup_with_approval_enabled_validation_error(
assert
response
.
status_code
==
400
def
test_user_login_jwt
(
factories
,
api_client
):
user
=
factories
[
"users.User"
]()
data
=
{
"username"
:
user
.
username
,
"password"
:
"test"
,
}
url
=
reverse
(
"api:v1:token"
)
response
=
api_client
.
post
(
url
,
data
)
assert
response
.
status_code
==
200
@
pytest
.
mark
.
parametrize
(
"setting_value, verified_email, expected_status_code"
,
[
(
"mandatory"
,
False
,
400
),
(
"mandatory"
,
True
,
200
),
(
"optional"
,
False
,
200
),
(
"optional"
,
True
,
200
),
],
)
def
test_user_login_jwt_honor_email_verification
(
setting_value
,
verified_email
,
expected_status_code
,
settings
,
factories
,
api_client
):
settings
.
ACCOUNT_EMAIL_VERIFICATION
=
setting_value
user
=
factories
[
"users.User"
](
verified_email
=
verified_email
)
data
=
{
"username"
:
user
.
username
,
"password"
:
"test"
,
}
url
=
reverse
(
"api:v1:token"
)
response
=
api_client
.
post
(
url
,
data
)
assert
response
.
status_code
==
expected_status_code
def
test_login_via_api
(
api_client
,
factories
):
user
=
factories
[
"users.User"
]()
url
=
reverse
(
"api:v1:users:login"
)
...
...
changes/changelog.d/1108.enhancement
0 → 100644
View file @
88de9976
Remove deprecated JWT Authentication (#1108) (1108)
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment