#StackBounty: #authentication #rest #jwt #keychain Is it safe an stateless authorization mechanism where the clear password is stored o…

Bounty: 50

Is it safe to use the following stateless authorization mechanism between a client (iOS & Android) and server?

Sign up

  1. The client provides an email and password and saves the clear password on the Keychain of iOS and using some alternative for Android.

  2. The server checks the password strength if it’s deemed strong enough the user is created on DB.

  3. The server generates a JWT token and returns it to the client. The token has an expiration time of 15 minutes.

  4. The client stores the token (maybe on the Keychain itself) and includes it for each following request on the Authorization header.

  5. For each request, the server checks the token provided (checks the signature and the expiration time). If it’s ok the request is processed, otherwise, an HTTP 401 is returned.

Sign in

  1. When the client receives an HTTP 401 from the server it means a login is required. So the app accesses to the Keychain and gets the email & password and sends it to the server (no user intervention needed).
  2. The server validates the credentials provided and if they’re valid it will repeat the Sign Up steps from 3 to 5.

Thanks to expiration time on the token, if a token is compromised it will be valid during a short time period.

If a user is logged on multiple devices and she changes her password from one device, the other devices will keep logged only for a short time period, but the clear password stored on the Keychain will not be longer valid. So a new manual login will be required, which I think it’s fine.

Which drawbacks do you see?

I’ve been thinking on using refresh token procedure to avoid store the clear password but this adds complexity and other drawbacks (for example: how to guarantee the refresh token is only used once). And as far as I’ve seen, storing the clear password on the KeyChain is secure enough:

KeyChain Services Documentation

What is the best way to maintain the login credentials in iOS?

But I also have seen other questions that do not recommend storing passwords on the device.

So I would like to hear opinions from others on this.


Get this bounty!!!

#StackBounty: #encryption #authentication #certificates #public-key-infrastructure #iot What kind of IoT devices use certificates?

Bounty: 50

I have read that PKI certificates are the main way to provide security on IoT platforms. And the common enrollment protocols for certificates are:

(CMP is the one which is used for securing LTE eNodeBs.)


Now what is not clear is what devices get the certificates? I am assuming that sensors etc are too small to get certificates and use them? So is anything used for securing sensors in IoT? I think gateways can use certs but gateways probably don’t need these protocols – they are not really IOT devices. Also what do the gateways use the certs for – is it to communicate with the cloud servers or is it to communicate with the gateways?

So what kind of IoT devices use these protocols to enroll certs? How do they use the certs? Do they use it only for authentication? Or also for encrypting communication? Are they sophisticated enough to do this?


Get this bounty!!!

#StackBounty: #oracle #oracle-12c #authentication #remote Remote login via rman working for one instance but not another

Bounty: 50

I have Oracle 12c installed on two servers. On server UAT I have an instance RMCAT that hosts an RMAN recovery catalog. On server EXT I have two instances, extdev02 and extuat01. I can use the following command on server UAT to connect to my RMCAT catalog, and target of extdev02, but when I try to connect to extuat01 it gives an ORA-01017

UAT> rman target= sys/tiger@extdev02  catalog= rmuser/tigerman@rmcat

Recovery Manager: Release 12.1.0.2.0 - Production on Wed May 30 22:59:26 2018

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

connected to target database: EXTDEV02 (DBID=4287636681)
connected to recovery catalog database

RMAN> exit


Recovery Manager complete.

UAT> rman target= sys/tiger@extuat01  catalog= rmuser/tigerman@rmcat

Recovery Manager: Release 12.1.0.2.0 - Production on Wed May 30 22:59:42 2018

Copyright (c) 1982, 2014, Oracle and/or its affiliates.  All rights reserved.

RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-00554: initialization of internal recovery manager package failed
RMAN-04005: error from target database:
ORA-01017: invalid username/password; logon denied

I get OK results from tnsping from UAT server to both instances on EXT, and I can connect to a non-dba user from UAT server to both instances. I recreated the Oracle password file using orapw on EXT server instance EXTUAT01 and that did not help.

Instance names are extdev02 and extuat01 on server EXT. Here are my password files on that server:

/u01/app/oracle/product/12.1.0.2/db_1/dbs> ls -o orapw*
-rw-r-----. 1 oracle 18432 Jun  1 00:07 orapwextuat01
/u01/app/oracle/product/12.1.0.2/db_0/dbs> ls -o orapw*
-rw-r-----. 1 oracle  7680 Feb  6 20:22 orapwextdev02

Note they are in two different ORACLE_HOME directories. The value for remote_login_passwordfile is EXCLUSIVE for both instances


Get this bounty!!!

#StackBounty: #python #python-3.x #security #authentication #django Let's register that Django user

Bounty: 200

Short intro

So, I’ve been using Django for a while now and thought it would be nice to start a simple application. In an ideal World, each app must have a way of letting its users register and that’s what I’m gonna do below.


Description

I’ve used the latest Django version for this project -> because of this, I couldn’t use django-registration module as it’s incompatible with the above. Apart from this, I’ve used Python 3.6.5.

What I want to do it’s common along other web applications:

  1. The user goes to the registration page and fills in the following fields:
    • first_name
    • last_name
    • email
  2. The user submits the form and receives a confirmation email with an URL containing a unique token.

  3. When the user clicks on the received link, he’s redirected to a page where he’ll set his password.

  4. When done, he’s logged in to the dashboard page.


Code

models.py

from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    """
    A custom user manager to deal with emails as unique identifiers for auth
    instead of usernames. The default that's used is "UserManager"
    """

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """

        if not email:
            raise ValueError('The Email must be set')

        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_user(self, email, password=None, **extra_fields):
        """
        Create and save a regular User with the given email and password.
        """

        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)

        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """

        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True, null=True, max_length=64)
    first_name = models.CharField(max_length=64)
    last_name = models.CharField(max_length=64)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    USERNAME_FIELD = 'email'
    objects = UserManager()

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

forms.py

from django.contrib.auth import get_user_model
from django.forms import ModelForm

User = get_user_model()


class RegistrationForm(ModelForm):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name')

    def save(self, commit=True):
        user = super(RegistrationForm, self).save(commit=False)
        if commit:
            user.save()
        return user

views.py

from django.contrib import messages
from django.contrib.auth import update_session_auth_hash, get_user_model, authenticate, login
from django.contrib.auth.forms import AuthenticationForm, SetPasswordForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMessage
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.views.generic import TemplateView, View

from .forms import RegistrationForm
from .tokens import account_activation_token

User = get_user_model()


class HomeView(LoginRequiredMixin, TemplateView):
    template_name = 'base/home.html'


class CustomLoginView(LoginView):
    template_name = 'base/login.html'
    form_class = AuthenticationForm


class CustomLogoutView(LogoutView):
    template_name = 'base/login.html'


class RegistrationView(View):

    def get(self, request):
        registration_form = RegistrationForm()
        return render(request, 'base/register.html', {'form': registration_form})

    def post(self, request):
        registration_form = RegistrationForm(request.POST)

        if registration_form.is_valid():
            user = registration_form.save(commit=False)
            user.is_active = False
            user.set_unusable_password()
            user.save()

            current_site = get_current_site(request)
            mail_subject = 'Activate your account.'
            message = render_to_string('base/activation-email.html', {
                'user': user,
                'domain': current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
                'token': account_activation_token.make_token(user),
            })
            to_email = registration_form.cleaned_data.get('email')
            email = EmailMessage(
                mail_subject, message, to=[to_email]
            )
            email.send()

            return render(request, 'base/confirm-email.html')


class ActivationView(View):

    def get(self, request, uidb64, token):
        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        if user is not None and account_activation_token.check_token(user, token):
            user.is_active = True
            user.save()
            login(request, user)

            form = SetPasswordForm(request.user)
            return render(request, 'base/password-set.html', {'form': form})
        else:
            return render(request, 'base/verification-failed.html')

    def post(self, request, uidb64, token):

        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        form = SetPasswordForm(user, request.POST)

        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)

            user = authenticate(username=user.email, password=form.cleaned_data['new_password1'])
            login(request, user)

            return HttpResponseRedirect('/')
        else:
            messages.error(request, form.errors)
            return render(request, 'base/password-set.html', {'form': form})

urls.py

from django.conf.urls import url
from django.views.generic import TemplateView

from .views import (
    CustomLoginView, CustomLogoutView, HomeView, 
    RegistrationView, ActivationView
)

app_name = 'apps.base'

urlpatterns = [
    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^login/$', CustomLoginView.as_view(), name='login'),
    url(r'^logout/$', CustomLogoutView.as_view(), name='logout'),

    url(r'^register/$', RegistrationView.as_view(), name='register'),
    url(r'^confirm_email/$', TemplateView.as_view(template_name='base/confirm-email.html'), name='confirm_email'),
    url(r'^activate/(?P<uidb64>[0-9A-Za-z_-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        ActivationView.as_view(), name='activate'),
]

tokens.py

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()

Things that I’d like to be reviewed:

  • Django Design Patterns: Did I use the correct views / forms / models in my app? Should some of the logic be written somewhere else etc…
  • Security: Is my register system secure? If not, what can I do to make it so?
  • General feedback: Anything that comes to your mind is welcome (including criticism, ideas, improvements, cats…aliens..)


Get this bounty!!!

#StackBounty: #python #python-3.x #security #authentication #django Let's register that Django user

Bounty: 200

Short intro

So, I’ve been using Django for a while now and thought it would be nice to start a simple application. In an ideal World, each app must have a way of letting its users register and that’s what I’m gonna do below.


Description

I’ve used the latest Django version for this project -> because of this, I couldn’t use django-registration module as it’s incompatible with the above. Apart from this, I’ve used Python 3.6.5.

What I want to do it’s common along other web applications:

  1. The user goes to the registration page and fills in the following fields:
    • first_name
    • last_name
    • email
  2. The user submits the form and receives a confirmation email with an URL containing a unique token.

  3. When the user clicks on the received link, he’s redirected to a page where he’ll set his password.

  4. When done, he’s logged in to the dashboard page.


Code

models.py

from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    """
    A custom user manager to deal with emails as unique identifiers for auth
    instead of usernames. The default that's used is "UserManager"
    """

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """

        if not email:
            raise ValueError('The Email must be set')

        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_user(self, email, password=None, **extra_fields):
        """
        Create and save a regular User with the given email and password.
        """

        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)

        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """

        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True, null=True, max_length=64)
    first_name = models.CharField(max_length=64)
    last_name = models.CharField(max_length=64)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    USERNAME_FIELD = 'email'
    objects = UserManager()

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

forms.py

from django.contrib.auth import get_user_model
from django.forms import ModelForm

User = get_user_model()


class RegistrationForm(ModelForm):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name')

    def save(self, commit=True):
        user = super(RegistrationForm, self).save(commit=False)
        if commit:
            user.save()
        return user

views.py

from django.contrib import messages
from django.contrib.auth import update_session_auth_hash, get_user_model, authenticate, login
from django.contrib.auth.forms import AuthenticationForm, SetPasswordForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMessage
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.views.generic import TemplateView, View

from .forms import RegistrationForm
from .tokens import account_activation_token

User = get_user_model()


class HomeView(LoginRequiredMixin, TemplateView):
    template_name = 'base/home.html'


class CustomLoginView(LoginView):
    template_name = 'base/login.html'
    form_class = AuthenticationForm


class CustomLogoutView(LogoutView):
    template_name = 'base/login.html'


class RegistrationView(View):

    def get(self, request):
        registration_form = RegistrationForm()
        return render(request, 'base/register.html', {'form': registration_form})

    def post(self, request):
        registration_form = RegistrationForm(request.POST)

        if registration_form.is_valid():
            user = registration_form.save(commit=False)
            user.is_active = False
            user.set_unusable_password()
            user.save()

            current_site = get_current_site(request)
            mail_subject = 'Activate your account.'
            message = render_to_string('base/activation-email.html', {
                'user': user,
                'domain': current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
                'token': account_activation_token.make_token(user),
            })
            to_email = registration_form.cleaned_data.get('email')
            email = EmailMessage(
                mail_subject, message, to=[to_email]
            )
            email.send()

            return render(request, 'base/confirm-email.html')


class ActivationView(View):

    def get(self, request, uidb64, token):
        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        if user is not None and account_activation_token.check_token(user, token):
            user.is_active = True
            user.save()
            login(request, user)

            form = SetPasswordForm(request.user)
            return render(request, 'base/password-set.html', {'form': form})
        else:
            return render(request, 'base/verification-failed.html')

    def post(self, request, uidb64, token):

        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        form = SetPasswordForm(user, request.POST)

        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)

            user = authenticate(username=user.email, password=form.cleaned_data['new_password1'])
            login(request, user)

            return HttpResponseRedirect('/')
        else:
            messages.error(request, form.errors)
            return render(request, 'base/password-set.html', {'form': form})

urls.py

from django.conf.urls import url
from django.views.generic import TemplateView

from .views import (
    CustomLoginView, CustomLogoutView, HomeView, 
    RegistrationView, ActivationView
)

app_name = 'apps.base'

urlpatterns = [
    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^login/$', CustomLoginView.as_view(), name='login'),
    url(r'^logout/$', CustomLogoutView.as_view(), name='logout'),

    url(r'^register/$', RegistrationView.as_view(), name='register'),
    url(r'^confirm_email/$', TemplateView.as_view(template_name='base/confirm-email.html'), name='confirm_email'),
    url(r'^activate/(?P<uidb64>[0-9A-Za-z_-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        ActivationView.as_view(), name='activate'),
]

tokens.py

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()

Things that I’d like to be reviewed:

  • Django Design Patterns: Did I use the correct views / forms / models in my app? Should some of the logic be written somewhere else etc…
  • Security: Is my register system secure? If not, what can I do to make it so?
  • General feedback: Anything that comes to your mind is welcome (including criticism, ideas, improvements, cats…aliens..)


Get this bounty!!!

#StackBounty: #python #python-3.x #security #authentication #django Let's register that Django user

Bounty: 200

Short intro

So, I’ve been using Django for a while now and thought it would be nice to start a simple application. In an ideal World, each app must have a way of letting its users register and that’s what I’m gonna do below.


Description

I’ve used the latest Django version for this project -> because of this, I couldn’t use django-registration module as it’s incompatible with the above. Apart from this, I’ve used Python 3.6.5.

What I want to do it’s common along other web applications:

  1. The user goes to the registration page and fills in the following fields:
    • first_name
    • last_name
    • email
  2. The user submits the form and receives a confirmation email with an URL containing a unique token.

  3. When the user clicks on the received link, he’s redirected to a page where he’ll set his password.

  4. When done, he’s logged in to the dashboard page.


Code

models.py

from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    """
    A custom user manager to deal with emails as unique identifiers for auth
    instead of usernames. The default that's used is "UserManager"
    """

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """

        if not email:
            raise ValueError('The Email must be set')

        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_user(self, email, password=None, **extra_fields):
        """
        Create and save a regular User with the given email and password.
        """

        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)

        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """

        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True, null=True, max_length=64)
    first_name = models.CharField(max_length=64)
    last_name = models.CharField(max_length=64)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    USERNAME_FIELD = 'email'
    objects = UserManager()

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

forms.py

from django.contrib.auth import get_user_model
from django.forms import ModelForm

User = get_user_model()


class RegistrationForm(ModelForm):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name')

    def save(self, commit=True):
        user = super(RegistrationForm, self).save(commit=False)
        if commit:
            user.save()
        return user

views.py

from django.contrib import messages
from django.contrib.auth import update_session_auth_hash, get_user_model, authenticate, login
from django.contrib.auth.forms import AuthenticationForm, SetPasswordForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMessage
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.views.generic import TemplateView, View

from .forms import RegistrationForm
from .tokens import account_activation_token

User = get_user_model()


class HomeView(LoginRequiredMixin, TemplateView):
    template_name = 'base/home.html'


class CustomLoginView(LoginView):
    template_name = 'base/login.html'
    form_class = AuthenticationForm


class CustomLogoutView(LogoutView):
    template_name = 'base/login.html'


class RegistrationView(View):

    def get(self, request):
        registration_form = RegistrationForm()
        return render(request, 'base/register.html', {'form': registration_form})

    def post(self, request):
        registration_form = RegistrationForm(request.POST)

        if registration_form.is_valid():
            user = registration_form.save(commit=False)
            user.is_active = False
            user.set_unusable_password()
            user.save()

            current_site = get_current_site(request)
            mail_subject = 'Activate your account.'
            message = render_to_string('base/activation-email.html', {
                'user': user,
                'domain': current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
                'token': account_activation_token.make_token(user),
            })
            to_email = registration_form.cleaned_data.get('email')
            email = EmailMessage(
                mail_subject, message, to=[to_email]
            )
            email.send()

            return render(request, 'base/confirm-email.html')


class ActivationView(View):

    def get(self, request, uidb64, token):
        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        if user is not None and account_activation_token.check_token(user, token):
            user.is_active = True
            user.save()
            login(request, user)

            form = SetPasswordForm(request.user)
            return render(request, 'base/password-set.html', {'form': form})
        else:
            return render(request, 'base/verification-failed.html')

    def post(self, request, uidb64, token):

        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        form = SetPasswordForm(user, request.POST)

        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)

            user = authenticate(username=user.email, password=form.cleaned_data['new_password1'])
            login(request, user)

            return HttpResponseRedirect('/')
        else:
            messages.error(request, form.errors)
            return render(request, 'base/password-set.html', {'form': form})

urls.py

from django.conf.urls import url
from django.views.generic import TemplateView

from .views import (
    CustomLoginView, CustomLogoutView, HomeView, 
    RegistrationView, ActivationView
)

app_name = 'apps.base'

urlpatterns = [
    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^login/$', CustomLoginView.as_view(), name='login'),
    url(r'^logout/$', CustomLogoutView.as_view(), name='logout'),

    url(r'^register/$', RegistrationView.as_view(), name='register'),
    url(r'^confirm_email/$', TemplateView.as_view(template_name='base/confirm-email.html'), name='confirm_email'),
    url(r'^activate/(?P<uidb64>[0-9A-Za-z_-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        ActivationView.as_view(), name='activate'),
]

tokens.py

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()

Things that I’d like to be reviewed:

  • Django Design Patterns: Did I use the correct views / forms / models in my app? Should some of the logic be written somewhere else etc…
  • Security: Is my register system secure? If not, what can I do to make it so?
  • General feedback: Anything that comes to your mind is welcome (including criticism, ideas, improvements, cats…aliens..)


Get this bounty!!!

#StackBounty: #python #python-3.x #security #authentication #django Let's register that Django user

Bounty: 200

Short intro

So, I’ve been using Django for a while now and thought it would be nice to start a simple application. In an ideal World, each app must have a way of letting its users register and that’s what I’m gonna do below.


Description

I’ve used the latest Django version for this project -> because of this, I couldn’t use django-registration module as it’s incompatible with the above. Apart from this, I’ve used Python 3.6.5.

What I want to do it’s common along other web applications:

  1. The user goes to the registration page and fills in the following fields:
    • first_name
    • last_name
    • email
  2. The user submits the form and receives a confirmation email with an URL containing a unique token.

  3. When the user clicks on the received link, he’s redirected to a page where he’ll set his password.

  4. When done, he’s logged in to the dashboard page.


Code

models.py

from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    """
    A custom user manager to deal with emails as unique identifiers for auth
    instead of usernames. The default that's used is "UserManager"
    """

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """

        if not email:
            raise ValueError('The Email must be set')

        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_user(self, email, password=None, **extra_fields):
        """
        Create and save a regular User with the given email and password.
        """

        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)

        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """

        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True, null=True, max_length=64)
    first_name = models.CharField(max_length=64)
    last_name = models.CharField(max_length=64)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    USERNAME_FIELD = 'email'
    objects = UserManager()

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

forms.py

from django.contrib.auth import get_user_model
from django.forms import ModelForm

User = get_user_model()


class RegistrationForm(ModelForm):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name')

    def save(self, commit=True):
        user = super(RegistrationForm, self).save(commit=False)
        if commit:
            user.save()
        return user

views.py

from django.contrib import messages
from django.contrib.auth import update_session_auth_hash, get_user_model, authenticate, login
from django.contrib.auth.forms import AuthenticationForm, SetPasswordForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMessage
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.views.generic import TemplateView, View

from .forms import RegistrationForm
from .tokens import account_activation_token

User = get_user_model()


class HomeView(LoginRequiredMixin, TemplateView):
    template_name = 'base/home.html'


class CustomLoginView(LoginView):
    template_name = 'base/login.html'
    form_class = AuthenticationForm


class CustomLogoutView(LogoutView):
    template_name = 'base/login.html'


class RegistrationView(View):

    def get(self, request):
        registration_form = RegistrationForm()
        return render(request, 'base/register.html', {'form': registration_form})

    def post(self, request):
        registration_form = RegistrationForm(request.POST)

        if registration_form.is_valid():
            user = registration_form.save(commit=False)
            user.is_active = False
            user.set_unusable_password()
            user.save()

            current_site = get_current_site(request)
            mail_subject = 'Activate your account.'
            message = render_to_string('base/activation-email.html', {
                'user': user,
                'domain': current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
                'token': account_activation_token.make_token(user),
            })
            to_email = registration_form.cleaned_data.get('email')
            email = EmailMessage(
                mail_subject, message, to=[to_email]
            )
            email.send()

            return render(request, 'base/confirm-email.html')


class ActivationView(View):

    def get(self, request, uidb64, token):
        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        if user is not None and account_activation_token.check_token(user, token):
            user.is_active = True
            user.save()
            login(request, user)

            form = SetPasswordForm(request.user)
            return render(request, 'base/password-set.html', {'form': form})
        else:
            return render(request, 'base/verification-failed.html')

    def post(self, request, uidb64, token):

        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        form = SetPasswordForm(user, request.POST)

        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)

            user = authenticate(username=user.email, password=form.cleaned_data['new_password1'])
            login(request, user)

            return HttpResponseRedirect('/')
        else:
            messages.error(request, form.errors)
            return render(request, 'base/password-set.html', {'form': form})

urls.py

from django.conf.urls import url
from django.views.generic import TemplateView

from .views import (
    CustomLoginView, CustomLogoutView, HomeView, 
    RegistrationView, ActivationView
)

app_name = 'apps.base'

urlpatterns = [
    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^login/$', CustomLoginView.as_view(), name='login'),
    url(r'^logout/$', CustomLogoutView.as_view(), name='logout'),

    url(r'^register/$', RegistrationView.as_view(), name='register'),
    url(r'^confirm_email/$', TemplateView.as_view(template_name='base/confirm-email.html'), name='confirm_email'),
    url(r'^activate/(?P<uidb64>[0-9A-Za-z_-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        ActivationView.as_view(), name='activate'),
]

tokens.py

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()

Things that I’d like to be reviewed:

  • Django Design Patterns: Did I use the correct views / forms / models in my app? Should some of the logic be written somewhere else etc…
  • Security: Is my register system secure? If not, what can I do to make it so?
  • General feedback: Anything that comes to your mind is welcome (including criticism, ideas, improvements, cats…aliens..)


Get this bounty!!!

#StackBounty: #python #python-3.x #security #authentication #django Let's register that Django user

Bounty: 200

Short intro

So, I’ve been using Django for a while now and thought it would be nice to start a simple application. In an ideal World, each app must have a way of letting its users register and that’s what I’m gonna do below.


Description

I’ve used the latest Django version for this project -> because of this, I couldn’t use django-registration module as it’s incompatible with the above. Apart from this, I’ve used Python 3.6.5.

What I want to do it’s common along other web applications:

  1. The user goes to the registration page and fills in the following fields:
    • first_name
    • last_name
    • email
  2. The user submits the form and receives a confirmation email with an URL containing a unique token.

  3. When the user clicks on the received link, he’s redirected to a page where he’ll set his password.

  4. When done, he’s logged in to the dashboard page.


Code

models.py

from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    """
    A custom user manager to deal with emails as unique identifiers for auth
    instead of usernames. The default that's used is "UserManager"
    """

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """

        if not email:
            raise ValueError('The Email must be set')

        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_user(self, email, password=None, **extra_fields):
        """
        Create and save a regular User with the given email and password.
        """

        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)

        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """

        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True, null=True, max_length=64)
    first_name = models.CharField(max_length=64)
    last_name = models.CharField(max_length=64)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    USERNAME_FIELD = 'email'
    objects = UserManager()

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

forms.py

from django.contrib.auth import get_user_model
from django.forms import ModelForm

User = get_user_model()


class RegistrationForm(ModelForm):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name')

    def save(self, commit=True):
        user = super(RegistrationForm, self).save(commit=False)
        if commit:
            user.save()
        return user

views.py

from django.contrib import messages
from django.contrib.auth import update_session_auth_hash, get_user_model, authenticate, login
from django.contrib.auth.forms import AuthenticationForm, SetPasswordForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMessage
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.views.generic import TemplateView, View

from .forms import RegistrationForm
from .tokens import account_activation_token

User = get_user_model()


class HomeView(LoginRequiredMixin, TemplateView):
    template_name = 'base/home.html'


class CustomLoginView(LoginView):
    template_name = 'base/login.html'
    form_class = AuthenticationForm


class CustomLogoutView(LogoutView):
    template_name = 'base/login.html'


class RegistrationView(View):

    def get(self, request):
        registration_form = RegistrationForm()
        return render(request, 'base/register.html', {'form': registration_form})

    def post(self, request):
        registration_form = RegistrationForm(request.POST)

        if registration_form.is_valid():
            user = registration_form.save(commit=False)
            user.is_active = False
            user.set_unusable_password()
            user.save()

            current_site = get_current_site(request)
            mail_subject = 'Activate your account.'
            message = render_to_string('base/activation-email.html', {
                'user': user,
                'domain': current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
                'token': account_activation_token.make_token(user),
            })
            to_email = registration_form.cleaned_data.get('email')
            email = EmailMessage(
                mail_subject, message, to=[to_email]
            )
            email.send()

            return render(request, 'base/confirm-email.html')


class ActivationView(View):

    def get(self, request, uidb64, token):
        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        if user is not None and account_activation_token.check_token(user, token):
            user.is_active = True
            user.save()
            login(request, user)

            form = SetPasswordForm(request.user)
            return render(request, 'base/password-set.html', {'form': form})
        else:
            return render(request, 'base/verification-failed.html')

    def post(self, request, uidb64, token):

        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        form = SetPasswordForm(user, request.POST)

        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)

            user = authenticate(username=user.email, password=form.cleaned_data['new_password1'])
            login(request, user)

            return HttpResponseRedirect('/')
        else:
            messages.error(request, form.errors)
            return render(request, 'base/password-set.html', {'form': form})

urls.py

from django.conf.urls import url
from django.views.generic import TemplateView

from .views import (
    CustomLoginView, CustomLogoutView, HomeView, 
    RegistrationView, ActivationView
)

app_name = 'apps.base'

urlpatterns = [
    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^login/$', CustomLoginView.as_view(), name='login'),
    url(r'^logout/$', CustomLogoutView.as_view(), name='logout'),

    url(r'^register/$', RegistrationView.as_view(), name='register'),
    url(r'^confirm_email/$', TemplateView.as_view(template_name='base/confirm-email.html'), name='confirm_email'),
    url(r'^activate/(?P<uidb64>[0-9A-Za-z_-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        ActivationView.as_view(), name='activate'),
]

tokens.py

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()

Things that I’d like to be reviewed:

  • Django Design Patterns: Did I use the correct views / forms / models in my app? Should some of the logic be written somewhere else etc…
  • Security: Is my register system secure? If not, what can I do to make it so?
  • General feedback: Anything that comes to your mind is welcome (including criticism, ideas, improvements, cats…aliens..)


Get this bounty!!!

#StackBounty: #python #python-3.x #security #authentication #django Let's register that Django user

Bounty: 200

Short intro

So, I’ve been using Django for a while now and thought it would be nice to start a simple application. In an ideal World, each app must have a way of letting its users register and that’s what I’m gonna do below.


Description

I’ve used the latest Django version for this project -> because of this, I couldn’t use django-registration module as it’s incompatible with the above. Apart from this, I’ve used Python 3.6.5.

What I want to do it’s common along other web applications:

  1. The user goes to the registration page and fills in the following fields:
    • first_name
    • last_name
    • email
  2. The user submits the form and receives a confirmation email with an URL containing a unique token.

  3. When the user clicks on the received link, he’s redirected to a page where he’ll set his password.

  4. When done, he’s logged in to the dashboard page.


Code

models.py

from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    """
    A custom user manager to deal with emails as unique identifiers for auth
    instead of usernames. The default that's used is "UserManager"
    """

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """

        if not email:
            raise ValueError('The Email must be set')

        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_user(self, email, password=None, **extra_fields):
        """
        Create and save a regular User with the given email and password.
        """

        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)

        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """

        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True, null=True, max_length=64)
    first_name = models.CharField(max_length=64)
    last_name = models.CharField(max_length=64)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    USERNAME_FIELD = 'email'
    objects = UserManager()

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

forms.py

from django.contrib.auth import get_user_model
from django.forms import ModelForm

User = get_user_model()


class RegistrationForm(ModelForm):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name')

    def save(self, commit=True):
        user = super(RegistrationForm, self).save(commit=False)
        if commit:
            user.save()
        return user

views.py

from django.contrib import messages
from django.contrib.auth import update_session_auth_hash, get_user_model, authenticate, login
from django.contrib.auth.forms import AuthenticationForm, SetPasswordForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMessage
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.views.generic import TemplateView, View

from .forms import RegistrationForm
from .tokens import account_activation_token

User = get_user_model()


class HomeView(LoginRequiredMixin, TemplateView):
    template_name = 'base/home.html'


class CustomLoginView(LoginView):
    template_name = 'base/login.html'
    form_class = AuthenticationForm


class CustomLogoutView(LogoutView):
    template_name = 'base/login.html'


class RegistrationView(View):

    def get(self, request):
        registration_form = RegistrationForm()
        return render(request, 'base/register.html', {'form': registration_form})

    def post(self, request):
        registration_form = RegistrationForm(request.POST)

        if registration_form.is_valid():
            user = registration_form.save(commit=False)
            user.is_active = False
            user.set_unusable_password()
            user.save()

            current_site = get_current_site(request)
            mail_subject = 'Activate your account.'
            message = render_to_string('base/activation-email.html', {
                'user': user,
                'domain': current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
                'token': account_activation_token.make_token(user),
            })
            to_email = registration_form.cleaned_data.get('email')
            email = EmailMessage(
                mail_subject, message, to=[to_email]
            )
            email.send()

            return render(request, 'base/confirm-email.html')


class ActivationView(View):

    def get(self, request, uidb64, token):
        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        if user is not None and account_activation_token.check_token(user, token):
            user.is_active = True
            user.save()
            login(request, user)

            form = SetPasswordForm(request.user)
            return render(request, 'base/password-set.html', {'form': form})
        else:
            return render(request, 'base/verification-failed.html')

    def post(self, request, uidb64, token):

        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        form = SetPasswordForm(user, request.POST)

        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)

            user = authenticate(username=user.email, password=form.cleaned_data['new_password1'])
            login(request, user)

            return HttpResponseRedirect('/')
        else:
            messages.error(request, form.errors)
            return render(request, 'base/password-set.html', {'form': form})

urls.py

from django.conf.urls import url
from django.views.generic import TemplateView

from .views import (
    CustomLoginView, CustomLogoutView, HomeView, 
    RegistrationView, ActivationView
)

app_name = 'apps.base'

urlpatterns = [
    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^login/$', CustomLoginView.as_view(), name='login'),
    url(r'^logout/$', CustomLogoutView.as_view(), name='logout'),

    url(r'^register/$', RegistrationView.as_view(), name='register'),
    url(r'^confirm_email/$', TemplateView.as_view(template_name='base/confirm-email.html'), name='confirm_email'),
    url(r'^activate/(?P<uidb64>[0-9A-Za-z_-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        ActivationView.as_view(), name='activate'),
]

tokens.py

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()

Things that I’d like to be reviewed:

  • Django Design Patterns: Did I use the correct views / forms / models in my app? Should some of the logic be written somewhere else etc…
  • Security: Is my register system secure? If not, what can I do to make it so?
  • General feedback: Anything that comes to your mind is welcome (including criticism, ideas, improvements, cats…aliens..)


Get this bounty!!!

#StackBounty: #python #python-3.x #security #authentication #django Let's register that Django user

Bounty: 200

Short intro

So, I’ve been using Django for a while now and thought it would be nice to start a simple application. In an ideal World, each app must have a way of letting its users register and that’s what I’m gonna do below.


Description

I’ve used the latest Django version for this project -> because of this, I couldn’t use django-registration module as it’s incompatible with the above. Apart from this, I’ve used Python 3.6.5.

What I want to do it’s common along other web applications:

  1. The user goes to the registration page and fills in the following fields:
    • first_name
    • last_name
    • email
  2. The user submits the form and receives a confirmation email with an URL containing a unique token.

  3. When the user clicks on the received link, he’s redirected to a page where he’ll set his password.

  4. When done, he’s logged in to the dashboard page.


Code

models.py

from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils.translation import ugettext_lazy as _


class UserManager(BaseUserManager):
    """
    A custom user manager to deal with emails as unique identifiers for auth
    instead of usernames. The default that's used is "UserManager"
    """

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """

        if not email:
            raise ValueError('The Email must be set')

        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_user(self, email, password=None, **extra_fields):
        """
        Create and save a regular User with the given email and password.
        """

        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)

        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """

        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True, null=True, max_length=64)
    first_name = models.CharField(max_length=64)
    last_name = models.CharField(max_length=64)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    USERNAME_FIELD = 'email'
    objects = UserManager()

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

forms.py

from django.contrib.auth import get_user_model
from django.forms import ModelForm

User = get_user_model()


class RegistrationForm(ModelForm):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name')

    def save(self, commit=True):
        user = super(RegistrationForm, self).save(commit=False)
        if commit:
            user.save()
        return user

views.py

from django.contrib import messages
from django.contrib.auth import update_session_auth_hash, get_user_model, authenticate, login
from django.contrib.auth.forms import AuthenticationForm, SetPasswordForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMessage
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.views.generic import TemplateView, View

from .forms import RegistrationForm
from .tokens import account_activation_token

User = get_user_model()


class HomeView(LoginRequiredMixin, TemplateView):
    template_name = 'base/home.html'


class CustomLoginView(LoginView):
    template_name = 'base/login.html'
    form_class = AuthenticationForm


class CustomLogoutView(LogoutView):
    template_name = 'base/login.html'


class RegistrationView(View):

    def get(self, request):
        registration_form = RegistrationForm()
        return render(request, 'base/register.html', {'form': registration_form})

    def post(self, request):
        registration_form = RegistrationForm(request.POST)

        if registration_form.is_valid():
            user = registration_form.save(commit=False)
            user.is_active = False
            user.set_unusable_password()
            user.save()

            current_site = get_current_site(request)
            mail_subject = 'Activate your account.'
            message = render_to_string('base/activation-email.html', {
                'user': user,
                'domain': current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
                'token': account_activation_token.make_token(user),
            })
            to_email = registration_form.cleaned_data.get('email')
            email = EmailMessage(
                mail_subject, message, to=[to_email]
            )
            email.send()

            return render(request, 'base/confirm-email.html')


class ActivationView(View):

    def get(self, request, uidb64, token):
        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        if user is not None and account_activation_token.check_token(user, token):
            user.is_active = True
            user.save()
            login(request, user)

            form = SetPasswordForm(request.user)
            return render(request, 'base/password-set.html', {'form': form})
        else:
            return render(request, 'base/verification-failed.html')

    def post(self, request, uidb64, token):

        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None

        form = SetPasswordForm(user, request.POST)

        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)

            user = authenticate(username=user.email, password=form.cleaned_data['new_password1'])
            login(request, user)

            return HttpResponseRedirect('/')
        else:
            messages.error(request, form.errors)
            return render(request, 'base/password-set.html', {'form': form})

urls.py

from django.conf.urls import url
from django.views.generic import TemplateView

from .views import (
    CustomLoginView, CustomLogoutView, HomeView, 
    RegistrationView, ActivationView
)

app_name = 'apps.base'

urlpatterns = [
    url(r'^$', HomeView.as_view(), name='home'),

    url(r'^login/$', CustomLoginView.as_view(), name='login'),
    url(r'^logout/$', CustomLogoutView.as_view(), name='logout'),

    url(r'^register/$', RegistrationView.as_view(), name='register'),
    url(r'^confirm_email/$', TemplateView.as_view(template_name='base/confirm-email.html'), name='confirm_email'),
    url(r'^activate/(?P<uidb64>[0-9A-Za-z_-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        ActivationView.as_view(), name='activate'),
]

tokens.py

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()

Things that I’d like to be reviewed:

  • Django Design Patterns: Did I use the correct views / forms / models in my app? Should some of the logic be written somewhere else etc…
  • Security: Is my register system secure? If not, what can I do to make it so?
  • General feedback: Anything that comes to your mind is welcome (including criticism, ideas, improvements, cats…aliens..)


Get this bounty!!!