Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Bryan BRANCOTTE
django-basetheme-bootstrap
Commits
d201da12
Commit
d201da12
authored
Mar 25, 2020
by
Bryan BRANCOTTE
Browse files
Ability to validate validate email when creating an account
parent
b1837f8c
Pipeline
#26564
failed with stage
in 36 seconds
Changes
10
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
d201da12
...
...
@@ -55,6 +55,7 @@ BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_LOCATION_APP = "test_app_1"
BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_NAME
=
"MyUserPreferences"
BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL
=
False
BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED
=
False
BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION
=
False
################################################################################
```
...
...
README.rst
View file @
d201da12
...
...
@@ -52,6 +52,7 @@ Quick start
BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_NAME = "MyUserPreferences"
BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL = False
BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED = False
BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION = False
################################################################################
...
...
basetheme_bootstrap/default_settings.py
0 → 100644
View file @
d201da12
from
django.conf
import
settings
def
is_username_is_email
():
try
:
return
settings
.
BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL
except
AttributeError
:
return
False
def
is_first_last_name_required
():
try
:
return
settings
.
BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED
except
AttributeError
:
return
False
def
is_validating_email
():
try
:
return
settings
.
BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION
except
AttributeError
:
return
False
basetheme_bootstrap/forms.py
View file @
d201da12
from
django.conf
import
settings
from
django.contrib.auth
import
get_user_model
,
forms
as
auth_forms
from
django.db.models
import
Q
from
django.forms
import
ModelForm
...
...
@@ -6,19 +6,7 @@ from django.urls import reverse
from
django.utils.safestring
import
mark_safe
from
django.utils.translation
import
ugettext_lazy
as
_
def
is_username_is_email
():
try
:
return
settings
.
BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL
except
AttributeError
:
return
False
def
is_first_last_name_required
():
try
:
return
settings
.
BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED
except
AttributeError
:
return
False
from
basetheme_bootstrap.default_settings
import
is_username_is_email
,
is_first_last_name_required
class
CleanUsernameAndSuggestReset
:
...
...
basetheme_bootstrap/tests.py
View file @
d201da12
import
os
import
re
from
django.conf
import
settings
from
django.contrib.auth
import
get_user_model
from
django.contrib.auth.models
import
AnonymousUser
from
django.contrib.auth.models
import
AnonymousUser
,
Group
from
django.core
import
mail
from
django.core.cache
import
cache
from
django.core.exceptions
import
ValidationError
from
django.test
import
TestCase
,
RequestFactory
,
override_settings
from
django.urls
import
reverse
from
basetheme_bootstrap
import
user_preferences
from
basetheme_bootstrap
import
user_preferences
,
tokens
from
basetheme_bootstrap.user_preferences
import
get_user_preferences_for_user
...
...
@@ -138,6 +140,99 @@ class SignUpWithFirstLastNameRequiredTests(TestCase):
self
.
assertEqual
(
response
.
status_code
,
200
)
@
override_settings
(
BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION
=
True
,
PASSWORD_RESET_TIMEOUT_DAYS
=
1
,
)
class
SignUpWithValidationTests
(
TestCase
):
def
setUp
(
self
):
cache
.
clear
()
get_user_model
().
objects
.
create
(
username
=
"root"
,
)
self
.
data
=
{
'username'
:
"userAAA"
,
'email'
:
"userAAA@mp.com"
,
'password1'
:
"user@mp.comuser@mp.comuser@mp.comuser@mp.com"
,
'password2'
:
"user@mp.comuser@mp.comuser@mp.comuser@mp.com"
,
'first_name'
:
"user"
}
def
test_sign_up_form_view
(
self
):
response
=
self
.
client
.
post
(
reverse
(
'basetheme_bootstrap:signup'
),
self
.
data
)
self
.
assertEqual
(
response
.
status_code
,
200
)
user
=
get_user_model
().
objects
.
last
()
self
.
assertFalse
(
user
.
is_active
)
self
.
assertIn
(
Group
.
objects
.
get
(
name
=
"PendingAccountUser"
),
user
.
groups
.
all
())
self
.
assertEqual
(
len
(
mail
.
outbox
),
1
)
activate_link_example
=
reverse
(
'basetheme_bootstrap:activate'
,
args
=
[
"AAA"
,
"AAA-AAA"
])
activate_link_example
=
activate_link_example
.
replace
(
"AAA"
,
"([a-zA-Z0-9]+)"
)
m
=
re
.
findall
(
activate_link_example
,
mail
.
outbox
[
0
].
body
)
self
.
assertEqual
(
len
(
m
),
1
)
self
.
client
.
post
(
reverse
(
'basetheme_bootstrap:activate'
,
args
=
[
m
[
0
][
0
],
m
[
0
][
1
]
+
"-"
+
m
[
0
][
2
]]))
user
=
get_user_model
().
objects
.
last
()
self
.
assertTrue
(
user
.
is_active
)
self
.
assertNotIn
(
Group
.
objects
.
get
(
name
=
"PendingAccountUser"
),
user
.
groups
.
all
())
## test an account cannot be re-activated with the link:
user
.
is_active
=
False
user
.
save
()
self
.
client
.
post
(
reverse
(
'basetheme_bootstrap:activate'
,
args
=
[
m
[
0
][
0
],
m
[
0
][
1
]
+
"-"
+
m
[
0
][
2
]]))
self
.
assertFalse
(
get_user_model
().
objects
.
last
().
is_active
)
def
test_sign_up_with_user_pending_resend_email
(
self
):
user_count
=
get_user_model
().
objects
.
count
()
response
=
self
.
client
.
post
(
reverse
(
'basetheme_bootstrap:signup'
),
self
.
data
)
self
.
assertEqual
(
get_user_model
().
objects
.
count
(),
user_count
+
1
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
mail
.
outbox
),
1
)
response
=
self
.
client
.
post
(
reverse
(
'basetheme_bootstrap:signup'
),
self
.
data
)
self
.
assertEqual
(
get_user_model
().
objects
.
count
(),
user_count
+
1
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
mail
.
outbox
),
2
)
activate_link_example
=
reverse
(
'basetheme_bootstrap:activate'
,
args
=
[
"AAA"
,
"AAA-AAA"
])
activate_link_example
=
activate_link_example
.
replace
(
"AAA"
,
"([a-zA-Z0-9]+)"
)
m
=
re
.
findall
(
activate_link_example
,
mail
.
outbox
[
-
1
].
body
)
self
.
assertEqual
(
len
(
m
),
1
)
def
test_activate_too_late_with_user_pending_resend_email
(
self
):
actual_account_activation_token
=
tokens
.
account_activation_token
class
MockedTokenGenerator
(
tokens
.
TokenGenerator
):
def
_today
(
self
):
from
datetime
import
date
,
timedelta
# Used for mocking in tests
return
date
.
today
()
-
timedelta
(
days
=
2
)
tokens
.
account_activation_token
=
MockedTokenGenerator
()
user_count
=
get_user_model
().
objects
.
count
()
response
=
self
.
client
.
post
(
reverse
(
'basetheme_bootstrap:signup'
),
self
.
data
)
self
.
assertEqual
(
get_user_model
().
objects
.
count
(),
user_count
+
1
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertFalse
(
get_user_model
().
objects
.
last
().
is_active
)
tokens
.
account_activation_token
=
actual_account_activation_token
self
.
assertEqual
(
len
(
mail
.
outbox
),
1
)
activate_link_example
=
reverse
(
'basetheme_bootstrap:activate'
,
args
=
[
"AAA"
,
"AAA-AAA"
])
activate_link_example
=
activate_link_example
.
replace
(
"AAA"
,
"([a-zA-Z0-9]+)"
)
m
=
re
.
findall
(
activate_link_example
,
mail
.
outbox
[
0
].
body
)
self
.
assertEqual
(
len
(
m
),
1
)
self
.
client
.
post
(
reverse
(
'basetheme_bootstrap:activate'
,
args
=
[
m
[
0
][
0
],
m
[
0
][
1
]
+
"-"
+
m
[
0
][
2
]]))
self
.
assertFalse
(
get_user_model
().
objects
.
last
().
is_active
)
self
.
assertEqual
(
len
(
mail
.
outbox
),
2
)
activate_link_example
=
reverse
(
'basetheme_bootstrap:activate'
,
args
=
[
"AAA"
,
"AAA-AAA"
])
activate_link_example
=
activate_link_example
.
replace
(
"AAA"
,
"([a-zA-Z0-9]+)"
)
m
=
re
.
findall
(
activate_link_example
,
mail
.
outbox
[
1
].
body
)
self
.
assertEqual
(
len
(
m
),
1
)
class
TestWithTemplatesInPlace
(
SignUpTests
):
def
setUp
(
self
):
...
...
basetheme_bootstrap/tokens.py
0 → 100644
View file @
d201da12
from
django.contrib.auth.tokens
import
PasswordResetTokenGenerator
from
django.utils
import
six
class
TokenGenerator
(
PasswordResetTokenGenerator
):
def
_make_hash_value
(
self
,
user
,
timestamp
):
return
(
six
.
text_type
(
user
.
pk
)
+
six
.
text_type
(
timestamp
)
+
six
.
text_type
(
user
.
is_active
)
)
account_activation_token
=
TokenGenerator
()
basetheme_bootstrap/urls.py
View file @
d201da12
...
...
@@ -19,6 +19,8 @@ urlpatterns = [
url
(
r
'^accounts/signup/$'
,
views
.
signup
,
name
=
'signup'
),
url
(
r
'^accounts/edit/$'
,
views
.
user_update
,
name
=
'user-update'
),
url
(
r
'^accounts/remove/$'
,
views
.
user_delete
,
name
=
'user-delete'
),
url
(
r
'^accounts/activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$'
,
views
.
activate
,
name
=
'activate'
),
################################################################################
# Lost password
################################################################################
...
...
basetheme_bootstrap/views.py
View file @
d201da12
...
...
@@ -6,15 +6,21 @@ from django.contrib import messages
from
django.contrib.auth
import
update_session_auth_hash
,
authenticate
,
login
,
get_user_model
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.forms
import
PasswordChangeForm
from
django.contrib.auth.models
import
Group
from
django.core.mail
import
send_mail
from
django.db.models
import
ProtectedError
from
django.forms
import
widgets
from
django.http
import
HttpResponseForbidden
from
django.shortcuts
import
render
,
redirect
from
django.template
import
TemplateDoesNotExist
from
django.urls
import
reverse
from
django.utils.encoding
import
force_bytes
,
force_text
from
django.utils.http
import
urlsafe_base64_encode
,
urlsafe_base64_decode
from
django.utils.translation
import
ugettext
from
basetheme_bootstrap
import
tokens
from
basetheme_bootstrap
import
user_preferences
from
basetheme_bootstrap.default_settings
import
is_validating_email
from
basetheme_bootstrap.forms
import
UserCreationFormWithMore
,
\
MyUserChangeForm
,
UserDeleteForm
...
...
@@ -53,35 +59,38 @@ def signup(request):
if
request
.
method
==
'POST'
:
form
=
UserCreationFormWithMore
(
request
.
POST
)
if
form
.
is_valid
():
auto_active
=
not
is_validating_email
()
user
=
form
.
save
()
if
get_user_model
().
objects
.
filter
(
pk__gt
=
1
).
count
()
==
0
:
user
.
is_superuser
=
True
user
.
is_staff
=
True
user
.
save
()
user
.
is_active
=
True
Group
.
objects
.
get_or_create
(
name
=
"PendingAccountUser"
)
else
:
if
not
auto_active
:
g
,
created
=
Group
.
objects
.
get_or_create
(
name
=
"PendingAccountUser"
)
user
.
groups
.
add
(
g
)
user
.
is_active
=
auto_active
user
.
save
()
send_account_created
(
request
,
user
,
auto_active
=
auto_active
)
if
not
auto_active
:
return
account_is_pending_view
(
request
,
email
=
request
.
POST
[
'email'
])
username
=
user
.
username
raw_password
=
form
.
cleaned_data
.
get
(
'password1'
)
user
=
authenticate
(
username
=
username
,
password
=
raw_password
)
request
.
user
=
user
try
:
send_mail
(
subject
=
ugettext
(
'Account successfully created'
),
message
=
ugettext
(
'Dear %(first_name)s %(last_name)s
\n\n
'
'Your account have successfully been created on %(joined)s.
\n\n
Best regards'
)
%
dict
(
first_name
=
request
.
user
.
first_name
,
last_name
=
request
.
user
.
last_name
,
joined
=
str
(
request
.
user
.
date_joined
),
),
from_email
=
settings
.
DEFAULT_FROM_EMAIL
,
recipient_list
=
[
request
.
user
.
email
],
fail_silently
=
False
,
)
except
Exception
as
e
:
logging
.
exception
(
"Sending email to user %i failed"
%
user
.
pk
)
login
(
request
,
user
)
return
redirect
(
'home'
)
else
:
user
=
get_user_model
().
objects
.
filter
(
groups__name
=
"PendingAccountUser"
,
email
=
request
.
POST
[
'email'
]).
first
()
if
user
is
not
None
:
send_account_created
(
request
,
user
,
auto_active
=
not
is_validating_email
())
return
account_is_pending_view
(
request
,
email
=
request
.
POST
[
'email'
])
else
:
if
not
request
.
user
.
is_anonymous
:
return
HttpResponseForbidden
()
...
...
@@ -89,6 +98,57 @@ def signup(request):
return
render
(
request
,
'registration/signup.html'
,
{
'form'
:
form
})
def
account_is_pending_view
(
request
,
email
):
return
render
(
request
,
'basetheme_bootstrap/simple_message_page.html'
,
{
'page_title'
:
ugettext
(
'Account activation pending'
),
'message'
:
ugettext
(
'An email was sent with a link to validate your account, '
'please click on the link to enable your account.'
),
'sub_message'
:
ugettext
(
'The email has been addressed to %s.'
)
%
email
,
})
def
send_account_created
(
request
,
user
,
auto_active
=
False
):
try
:
activation_link
=
request
.
scheme
+
"://"
+
request
.
get_host
()
activation_link
+=
reverse
(
'basetheme_bootstrap:activate'
,
kwargs
=
{
'uidb64'
:
urlsafe_base64_encode
(
force_bytes
(
user
.
pk
)).
decode
(),
'token'
:
tokens
.
account_activation_token
.
make_token
(
user
)
})
if
auto_active
:
message
=
ugettext
(
'Dear %(first_name)s %(last_name)s
\n\n
'
'Your account have successfully been created on %(joined)s.'
'
\n\n
'
'Best regards'
)
%
dict
(
first_name
=
user
.
first_name
,
last_name
=
user
.
last_name
,
joined
=
str
(
user
.
date_joined
),
)
else
:
message
=
ugettext
(
'Dear %(first_name)s %(last_name)s
\n\n
'
'Your account have successfully been created on %(joined)s.'
'
\n\n
'
'Please click on the link to confirm your registration
\n
'
'%(activation_link)s'
'
\n\n
'
'Best regards'
)
%
dict
(
first_name
=
user
.
first_name
,
last_name
=
user
.
last_name
,
joined
=
str
(
user
.
date_joined
),
activation_link
=
activation_link
,
)
send_mail
(
subject
=
ugettext
(
'Account successfully created'
),
message
=
message
,
from_email
=
settings
.
DEFAULT_FROM_EMAIL
,
recipient_list
=
[
user
.
email
],
fail_silently
=
False
,
)
except
Exception
as
e
:
logging
.
exception
(
"Sending email to user %i failed"
%
user
.
pk
)
def
user_update
(
request
):
if
request
.
method
==
'POST'
:
form
=
MyUserChangeForm
(
instance
=
request
.
user
,
data
=
request
.
POST
)
...
...
@@ -165,3 +225,31 @@ def account_detail(request):
'form_prefs'
:
form_prefs
,
'btn_classes'
:
'pull-right float-right'
})
def
activate
(
request
,
uidb64
,
token
):
try
:
uid
=
force_text
(
urlsafe_base64_decode
(
uidb64
))
user
=
get_user_model
().
objects
.
get
(
pk
=
uid
)
except
(
TypeError
,
ValueError
,
OverflowError
,
get_user_model
().
DoesNotExist
):
user
=
None
if
user
is
not
None
and
user
.
groups
.
filter
(
name
=
"PendingAccountUser"
).
exists
():
if
tokens
.
account_activation_token
.
check_token
(
user
,
token
):
user
.
is_active
=
True
user
.
groups
.
remove
(
user
.
groups
.
get
(
name
=
"PendingAccountUser"
))
user
.
save
()
login
(
request
,
user
)
# return redirect('home')
# return HttpResponse('Thank you for your email confirmation. Now you can login your account.')
return
render
(
request
,
'basetheme_bootstrap/simple_message_page.html'
,
{
'page_title'
:
ugettext
(
'Account activated'
),
'message'
:
ugettext
(
'Thank you for your email confirmation, you account have been activated '
'and you are now logged in.'
),
})
else
:
send_account_created
(
request
,
user
)
return
account_is_pending_view
(
request
,
email
=
user
.
email
)
else
:
return
render
(
request
,
'basetheme_bootstrap/simple_message_page.html'
,
{
'page_title'
:
ugettext
(
'Activation link is invalid!'
),
})
setup.py
View file @
d201da12
...
...
@@ -7,7 +7,7 @@ readme = open('README.rst').read()
setup
(
name
=
'django-basetheme-bootstrap'
,
version
=
'0.2.
29
'
,
version
=
'0.2.
30
'
,
description
=
'Django Basetheme Bootstrap'
,
long_description
=
readme
,
author
=
'Bryan Brancotte'
,
...
...
tests/settings.py
View file @
d201da12
...
...
@@ -138,6 +138,7 @@ BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_LOCATION_APP = "test_app_1"
BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_NAME
=
"MyUserPreferences"
# BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL = True # default is False
# BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED = True # default is False
# BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION = True # default is False
################################################################################
# Various debug stuff
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a 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