Skip to content

Commit

Permalink
165305756-ft(Password Reset):User should be able to reset password vi…
Browse files Browse the repository at this point in the history
…a email

- Add password reset endpoint
- Add password reset confirm endpoint
- Update Readme
- Add email template
[Delivers #165305756]
  • Loading branch information
Ogutu-Brian committed May 2, 2019
1 parent db17f92 commit c56c03f
Show file tree
Hide file tree
Showing 11 changed files with 870 additions and 5 deletions.
9 changes: 9 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,12 @@ export SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET='your-google-secret'

export SOCIAL_AUTH_TWITTER_KEY='your-twitter-key'
export SOCIAL_AUTH_TWITTER_SECRET='your-twitter-secret'

export SECRET_KEY=your secret key
export EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
export EMAIL_HOST='your email host'
export EMAIL_HOST_PASSWORD='your email host password'
export EMAIL_PORT='your email port'
export EMAIL_HOST_USER='your email host user'
export EMAIL_USE_TLS=True
export DOMAIN_NAME='your domain name'
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,38 @@ Authentication required, returns the User

Accepted fields: `email``username``password``image``bio`

### Password Reset
`POST /api/users/password/reset/`

Example request body:

```source-json
{
"user":{
"email":"codingbrian58@gmail.com"
}
}
```

No authentication required, returns a token

Required fields: `email`

### Password Reset Confirm
`POST /api/users/password/reset/confirm/`

Example request body:

```source-json
{
"token":"bb57bb7c779d2c7872c8621d5735e3b8170d6105",
"password":"passroneggne2424",
"password_confirm":"passroneggne2424"
}
```

No authentication required, returns a success message

### Get Profile

`GET /api/profiles/:username`
Expand Down
64 changes: 64 additions & 0 deletions authors/apps/authentication/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
from rest_framework.validators import UniqueValidator

from .models import User
from django.core.validators import EmailValidator
from django.core.exceptions import ValidationError
from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone
from rest_framework.authtoken.models import Token
from django.contrib.auth.password_validation import validate_password


class RegistrationSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -205,3 +211,61 @@ class SocialAuthSerializer(serializers.Serializer):
allow_blank=True,
default=""
)
class PasswordResetSerializer(serializers.Serializer):
"""
Serializer for password reset
"""
email = serializers.EmailField(required=True)

def validate_email(self, value):
try:
EmailValidator(value)
except ValidationError as e:
raise serializers.ValidationError(e)
try:
User.objects.get(email=value)
except ObjectDoesNotExist:
raise serializers.ValidationError(
"no account with that email address"
)


class PasswordResetConfirmSerializer(serializers.Serializer):
"""
Serializer class for password reset confirm
"""
token = serializers.CharField(max_length=250, required=True)
password = serializers.CharField(max_length=250, required=True)
password_confirm = serializers.CharField(max_length=250, required=True)

def validate(self, data):
try:
token_object = Token.objects.get(key=data.get("token"))
except ObjectDoesNotExist:
raise serializers.ValidationError({
"token": "invalid token"
})
if self.expired_token(token_object):
raise serializers.ValidationError({
"token": "invalid token"
})
try:
validate_password(data.get("password"))
except ValidationError as error:
raise serializers.ValidationError({
"password": list(error)
})
if data["password"] != data["password_confirm"]:
raise serializers.ValidationError({
"password": "passwords did not match"
})
return data

def expired_token(self, auth_token):
"""
Checks expiry of token
"""
utc_now = timezone.now()
expired = auth_token.created < utc_now - \
timezone.timedelta(hours=24)
return expired
54 changes: 53 additions & 1 deletion authors/apps/authentication/tests/basetests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.contrib.auth import get_user_model
from rest_framework.test import APITestCase, APIClient
from rest_framework.reverse import reverse
from rest_framework.authtoken.models import Token

User = get_user_model()

Expand Down Expand Up @@ -63,7 +64,7 @@ def generate_jwt_token(self, email, username):
}, settings.SECRET_KEY, algorithm='HS256'
)
return token.decode('utf-8')

def generate_expired_token(self):
"""
Generates a token that expires after one second for testing purposes
Expand Down Expand Up @@ -163,3 +164,54 @@ def social_login(self, social_url, social_user):
Method to login user using social authentication
"""
return self.client.post(social_url, social_user)

class PasswordResetBaseTest(BaseTest):
"""
Base test for testing passeord reset
"""

def setUp(self):
super().setUp()
self.client = APIClient()
self.email = self.user.email
self.password_reset_url = reverse("authentication:password_reset")
self.token = None
self.password_reset_confirm_url = reverse(
"authentication:password_reset_confirm")
self.reset_data = {
"user": {
"email": self.email
}
}
self.password_data = {
"token": self.token,
"password": "HenkDTestPAss!#",
"password_confirm": "HenkDTestPAss!#"
}
self.contains_error = lambda container, error: error in container

def password_reset(self):
"""
Verifies user account and generates reset password
token
"""
response = self.client.post(
path=self.password_reset_url,
data=self.reset_data,
format="json"
)
token, created = Token.objects.get_or_create(user=self.user)
self.token = token.key
self.password_data["token"] = self.token
return response

def password_reset_confirm(self):
"""
Confirms password reset by posting new password
"""
response = self.client.post(
path=self.password_reset_confirm_url,
data=self.password_data,
format="json"
)
return response
Loading

0 comments on commit c56c03f

Please sign in to comment.