Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
jovuit
funkwhale
Commits
b07bd83f
Verified
Commit
b07bd83f
authored
Apr 01, 2020
by
Eliot Berriot
Browse files
See #1039: resend confirmation email on login if email is unverified
parent
372bd4a6
Changes
9
Hide whitespace changes
Inline
Side-by-side
api/config/settings/common.py
View file @
b07bd83f
...
...
@@ -686,7 +686,7 @@ REST_FRAMEWORK = {
"funkwhale_api.federation.parsers.ActivityParser"
,
),
"DEFAULT_AUTHENTICATION_CLASSES"
:
(
"
oauth2_provider.contrib.rest_framework
.OAuth2Authentication"
,
"
funkwhale_api.common.authentication
.OAuth2Authentication"
,
"funkwhale_api.common.authentication.JSONWebTokenAuthenticationQS"
,
"funkwhale_api.common.authentication.BearerTokenHeaderAuth"
,
"funkwhale_api.common.authentication.JSONWebTokenAuthentication"
,
...
...
api/funkwhale_api/common/authentication.py
View file @
b07bd83f
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
from
allauth.account.utils
import
send_email_confirmation
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
...
...
@@ -14,7 +21,40 @@ def should_verify_email(user):
return
has_unverified_email
and
mandatory_verification
class
UnverifiedEmail
(
Exception
):
def
__init__
(
self
,
user
):
self
.
user
=
user
def
resend_confirmation_email
(
request
,
user
):
THROTTLE_DELAY
=
500
cache_key
=
"auth:resent-email-confirmation:{}"
.
format
(
user
.
pk
)
if
cache
.
get
(
cache_key
):
return
False
done
=
send_email_confirmation
(
request
,
user
)
cache
.
set
(
cache_key
,
True
,
THROTTLE_DELAY
)
return
done
class
OAuth2Authentication
(
BaseOAuth2Authentication
):
def
authenticate
(
self
,
request
):
try
:
return
super
().
authenticate
(
request
)
except
UnverifiedEmail
as
e
:
request
.
oauth2_error
=
{
"error"
:
"unverified_email"
}
resend_confirmation_email
(
request
,
e
.
user
)
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
...
...
@@ -38,9 +78,7 @@ class BaseJsonWebTokenAuth(object):
raise
exceptions
.
AuthenticationFailed
(
msg
)
if
should_verify_email
(
user
):
msg
=
_
(
"You need to verify your email address."
)
raise
exceptions
.
AuthenticationFailed
(
msg
)
raise
UnverifiedEmail
(
user
)
return
user
...
...
api/funkwhale_api/users/adapters.py
View file @
b07bd83f
...
...
@@ -25,3 +25,7 @@ class FunkwhaleAccountAdapter(DefaultAccountAdapter):
def
get_login_redirect_url
(
self
,
request
):
return
"noop"
def
add_message
(
self
,
*
args
,
**
kwargs
):
# disable message sending
return
api/funkwhale_api/users/auth_backends.py
View file @
b07bd83f
...
...
@@ -43,9 +43,11 @@ class ModelBackend(backends.ModelBackend):
return
user
if
self
.
user_can_authenticate
(
user
)
else
None
def
user_can_authenticate
(
self
,
user
):
return
super
().
user_can_authenticate
(
user
)
and
not
authentication
.
should_verify_email
(
user
)
can_authenticate
=
super
().
user_can_authenticate
(
user
)
if
authentication
.
should_verify_email
(
user
):
raise
authentication
.
UnverifiedEmail
(
user
)
return
can_authenticate
class
AllAuthBackend
(
auth_backends
.
AuthenticationBackend
,
ModelBackend
):
...
...
api/funkwhale_api/users/jwt_views.py
View file @
b07bd83f
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
):
...
...
api/funkwhale_api/users/oauth/server.py
View file @
b07bd83f
...
...
@@ -8,8 +8,7 @@ def check(request):
user
=
request
.
user
request
.
user
=
user
.
__class__
.
objects
.
all
().
for_auth
().
get
(
pk
=
user
.
pk
)
if
authentication
.
should_verify_email
(
request
.
user
):
setattr
(
request
,
"oauth2_error"
,
{
"error"
:
"unverified_email"
})
return
False
raise
authentication
.
UnverifiedEmail
(
user
)
return
True
...
...
api/funkwhale_api/users/serializers.py
View file @
b07bd83f
...
...
@@ -7,8 +7,10 @@ from django.utils.translation import gettext_lazy as _
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
...
...
@@ -36,6 +38,15 @@ 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/tests/users/oauth/test_views.py
View file @
b07bd83f
...
...
@@ -381,9 +381,15 @@ def test_grant_delete(factories, logged_in_api_client, mocker, now):
],
)
def
test_token_auth
(
setting_value
,
verified_email
,
expected_status_code
,
api_client
,
factories
,
settings
setting_value
,
verified_email
,
expected_status_code
,
api_client
,
factories
,
settings
,
mailoutbox
,
):
sent_emails
=
len
(
mailoutbox
)
user
=
factories
[
"users.User"
](
verified_email
=
verified_email
)
token
=
factories
[
"users.AccessToken"
](
user
=
user
)
settings
.
ACCOUNT_EMAIL_VERIFICATION
=
setting_value
...
...
@@ -392,3 +398,7 @@ def test_token_auth(
HTTP_AUTHORIZATION
=
"Bearer {}"
.
format
(
token
.
token
),
)
assert
response
.
status_code
==
expected_status_code
if
expected_status_code
!=
200
:
# confirmation email should have been sent again
assert
len
(
mailoutbox
)
==
sent_emails
+
1
deploy/env.prod.sample
View file @
b07bd83f
...
...
@@ -52,6 +52,10 @@ FUNKWHALE_PROTOCOL=https
# EMAIL_CONFIG=smtp+ssl://user@:password@youremail.host:465
# EMAIL_CONFIG=smtp+tls://user@:password@youremail.host:587
# Make email verification mandatory before using the service
# Doesn't apply to admins.
# ACCOUNT_EMAIL_VERIFICATION_ENFORCE=false
# The email address to use to send system emails.
# DEFAULT_FROM_EMAIL=noreply@yourdomain
...
...
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