diff --git a/beat/web/accounts/api.py b/beat/web/accounts/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..a56b2e4d956a76dbefe294713508460fbe12ad5d
--- /dev/null
+++ b/beat/web/accounts/api.py
@@ -0,0 +1,523 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.shortcuts import get_object_or_404
+from django.http import Http404
+from django.db import models
+from django.db.models import Q
+from django.core.urlresolvers import reverse
+from django.template import loader
+from django.template import Context
+
+from rest_framework import generics
+from rest_framework import views
+from rest_framework import permissions
+from rest_framework.response import Response
+from rest_framework import status
+
+from .serializers import FullSupervisionTrackSerializer
+from .serializers import SupervisionTrackUpdateSerializer
+
+from ..common.utils import validate_restructuredtext
+from ..ui.templatetags.markup import restructuredtext
+
+
+from .models import SupervisionTrack, Profile
+from ..common.models import Shareable
+from ..common.exceptions import ShareError
+from ..common.mixins import CommonContextMixin
+from ..ui.registration.models import RegistrationProfile
+
+from itertools import chain
+from datetime import datetime, timedelta
+
+
+from .permissions import IsGodfatherAndAuthor, IsAuthorAndNotGodfather
+
+from ..common.responses import BadRequestResponse, ForbiddenResponse
+
+import datetime
+import re
+from urlparse import urlparse
+
+import simplejson as json
+
+
+#----------------------------------------------------------
+
+
+class GodfatherListView(generics.ListAPIView):
+    model = SupervisionTrack
+    serializer_class = FullSupervisionTrackSerializer
+
+    def get_permissions(self):
+        permission_classes = [permissions.IsAuthenticated, IsGodfatherAndAuthor]
+
+        self.permission_classes = permission_classes
+
+        return super(GodfatherListView, self).get_permissions()
+
+
+    def get_serializer(self, *args, **kwargs):
+
+        return super(GodfatherListView, self).get_serializer(*args, **kwargs)
+
+
+    def list(self, request):
+        #A godfather can validate an account of:
+        #1) a new user requesting validation
+        #2) an existing validated user rejected by a previous supervisor
+        #3) an existing validated user requesting a change of supervisor
+        #4) a blocked user requesting a supervision
+        #On all cases check the current key in supervisee profile match the supervisiontrack key as this is the current supervision request/track from the supervisee
+        queryset    = SupervisionTrack.objects.filter(godfather=request.user).filter(Q(supervisee__profile__status=Profile.WAITINGVALIDATION)|Q(supervisee__profile__status=Profile.REJECTED)|Q(supervisee__profile__status=Profile.YEARREVALIDATION)|Q(supervisee__profile__status=Profile.ACCEPTED)|Q(supervisee__profile__status=Profile.BLOCKED)).filter(Q(supervisee__profile__supervision_key=models.F('supervision_key')))
+        serializer  = FullSupervisionTrackSerializer(queryset, many=True, context ={'request': request})
+        
+        return Response(serializer.data)
+
+
+#----------------------------------------------------------
+
+
+class BaseUpdateSupervisionTrackView(generics.UpdateAPIView):
+    model = SupervisionTrack
+    serializer_class = SupervisionTrackUpdateSerializer
+
+    def get_permissions(self):
+        permission_classes = [permissions.IsAuthenticated, IsGodfatherAndAuthor]
+
+        self.permission_classes = permission_classes
+
+        return super(BaseUpdateSupervisionTrackView, self).get_permissions()
+
+
+#----------------------------------------------------------
+
+
+class GodfatherAddSuperviseeView(BaseUpdateSupervisionTrackView):
+    permission_classes = BaseUpdateSupervisionTrackView.permission_classes
+
+    def put(self, request, supervisee_name):
+        supervisee = User.objects.get(username=supervisee_name)
+        profile_supervisee = Profile.objects.get(user=supervisee)
+        if supervisee.profile.status != Profile.ACCEPTED:
+            supervisiontrack = SupervisionTrack.objects.get(godfather=request.user, supervisee=supervisee, supervisee__profile__supervision_key=models.F('supervision_key'))
+
+            supervisiontrack.is_valid = True
+
+            now = datetime.datetime.now()
+            expiration_date_delta = datetime.timedelta(days=settings.ACCOUNT_EXPIRATION_DAYS)
+            supervisiontrack.expiration_date = now + expiration_date_delta
+
+            supervisiontrack.start_date = now
+            supervisiontrack.last_validation_date = now
+            supervisee.profile.status = Profile.ACCEPTED
+            supervisee.profile.rejection_date = None
+
+            supervisiontrack.save()
+            supervisee.profile.save()
+            supervisee.is_active = True
+            supervisee.save()
+
+            from django.core.mail import send_mail
+
+            parsed_url = urlparse(settings.URL_PREFIX)
+            server_address = '%s://%s' % (parsed_url.scheme, parsed_url.hostname)
+
+            c = Context({ 'supervisor': supervisiontrack.godfather,
+                          'supervisee': supervisee,
+                          'prefix': server_address,
+                        })
+
+            try:
+                t = loader.get_template('registration/mail.godfather_validated.subject.txt')
+                subject = t.render(c)
+
+                # Note: e-mail subject *must not* contain newlines
+                subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+                t = loader.get_template('registration/mail.godfather_validated.message.txt')
+                message = t.render(c)
+
+                send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [supervisee.email])
+            except:
+                pass
+
+            return Response(status=status.HTTP_204_NO_CONTENT)
+        else:
+            #Track already valid
+            reason = "You can't validate this supervisee as his supervision is still valid"
+            result = {
+                'error': reason,
+            }
+            return BadRequestResponse(result)
+
+
+#----------------------------------------------------------
+
+
+class GodfatherRemoveSuperviseeView(BaseUpdateSupervisionTrackView):
+    permission_classes = BaseUpdateSupervisionTrackView.permission_classes
+
+    def put(self, request, supervisee_name):
+        supervisee = User.objects.get(username=supervisee_name)
+        supervisiontrack = SupervisionTrack.objects.get(godfather=request.user, supervisee=supervisee, supervisee__profile__supervision_key=models.F('supervision_key'))
+
+        from django.core.mail import send_mail
+
+        parsed_url = urlparse(settings.URL_PREFIX)
+        server_address = '%s://%s' % (parsed_url.scheme, parsed_url.hostname)
+
+        c = Context({ 'supervisor': supervisiontrack.godfather,
+                      'supervisee': supervisee,
+                      'prefix': server_address,
+                    })
+
+        if supervisee.profile.status == Profile.WAITINGVALIDATION:
+            #New user account waiting validation, so delete this account and inform by email the user
+            try:
+                t = loader.get_template('registration/mail.godfather_rejected.subject.txt')
+                subject = t.render(c)
+
+                # Note: e-mail subject *must not* contain newlines
+                subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+                t = loader.get_template('registration/mail.godfather_rejected_delete_account.message.txt')
+                message = t.render(c)
+
+                send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [supervisee.email])
+            except:
+                pass
+
+            registration_profile = RegistrationProfile.objects.get(user=supervisee)
+            supervisee.profile.delete()
+            supervisee.delete()
+            supervisiontrack.delete()
+            registration_profile.delete()
+        else:
+            #Reject this account and inform by email the user
+            now = datetime.datetime.now()
+            expiration_date_delta = datetime.timedelta(days=settings.ACCOUNT_BLOCKAGE_AFTER_FIRST_REJECTION_DAYS)
+
+            supervisiontrack.expiration_date = now
+            supervisiontrack.is_valid = False
+
+            if supervisee.profile.status != Profile.BLOCKED:
+                supervisee.profile.status = Profile.REJECTED
+                if supervisee.profile.rejection_date == None:
+                    supervisee.profile.rejection_date = now + expiration_date_delta
+            else:
+                    supervisee.profile.rejection_date = None
+
+            supervisee.profile.supervision_key = None
+
+            supervisiontrack.save()
+            supervisee.profile.save()
+            supervisee.save()
+
+            try:
+                t = loader.get_template('registration/mail.godfather_rejected.subject.txt')
+                subject = t.render(c)
+
+                # Note: e-mail subject *must not* contain newlines
+                subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+                t = loader.get_template('registration/mail.godfather_rejected.message.txt')
+                message = t.render(c)
+
+                send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [supervisee.email])
+            except:
+                pass
+
+            #New supervision request refused
+            if supervisiontrack.start_date is None:
+                supervisiontrack.delete()
+
+        return Response(status=status.HTTP_204_NO_CONTENT)
+
+
+#----------------------------------------------------------
+
+
+class BaseCreateSupervisionTrackViewSupervisee(generics.CreateAPIView):
+    model = SupervisionTrack
+    serializer_class = SupervisionTrackUpdateSerializer
+
+    def get_permissions(self):
+        permission_classes = [permissions.IsAuthenticated, IsAuthorAndNotGodfather]
+
+        self.permission_classes = permission_classes
+
+        return super(BaseCreateSupervisionTrackViewSupervisee, self).get_permissions()
+
+
+#----------------------------------------------------------
+
+
+class SuperviseeAddGodfatherView(BaseCreateSupervisionTrackViewSupervisee):
+    permission_classes = BaseCreateSupervisionTrackViewSupervisee.permission_classes
+
+    def post(self, request, supervisor_name):
+        #check if user exists and its validity and if it's a godfather account
+        try:
+            godfather = User.objects.get(username=supervisor_name)
+            if not godfather.profile.is_godfather:
+                #Not a valid godfather
+                reason = "Not a valid supervisor request"
+                result = {
+                    'error': reason,
+                }
+                return BadRequestResponse(result)
+            else:
+                if godfather.profile.status == Profile.BLOCKED:
+                    #Not a valid godfather
+                    reason = "Not a valid supervisor request"
+                    result = {
+                        'error': reason,
+                    }
+                    return BadRequestResponse(result)
+        except:
+            #This username does not exist but don't give too much information
+            reason = "Not a valid supervisor request"
+            result = {
+                'error': reason,
+            }
+            return BadRequestResponse(result)
+
+        supervisee = request.user
+        if supervisee.profile.supervision_key is not None:
+            #There's a key check if there's a valid track
+            supervisiontrack = SupervisionTrack.objects.get(supervisee=supervisee, supervisee__profile__supervision_key=models.F('supervision_key'))
+            if supervisiontrack.is_valid:
+                if supervisee.profile.status != Profile.WAITINGVALIDATION and supervisee.profile.status != Profile.NEWUSER and supervisee.profile.status != Profile.BLOCKED:
+                    #Stop the current supervision
+                    now = datetime.datetime.now()
+                    expiration_date_delta = datetime.timedelta(days=settings.ACCOUNT_BLOCKAGE_AFTER_FIRST_REJECTION_DAYS)
+
+
+                    supervisiontrack.expiration_date = now
+                    supervisiontrack.is_valid = False
+                    supervisiontrack.save()
+
+                    #Inform by email the revoked supervisor
+                    from django.core.mail import send_mail
+
+                    parsed_url = urlparse(settings.URL_PREFIX)
+                    server_address = '%s://%s' % (parsed_url.scheme, parsed_url.hostname)
+
+                    c = Context({ 'supervisor': supervisiontrack.godfather,
+                                  'supervisee': supervisiontrack.supervisee,
+                                  'prefix': server_address,
+                                })
+
+                    try:
+                        t = loader.get_template('registration/mail.godfather_rejection.subject.txt')
+                        subject = t.render(c)
+
+                        # Note: e-mail subject *must not* contain newlines
+                        subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+                        t = loader.get_template('registration/mail.godfather_rejection.message.txt')
+                        message = t.render(c)
+
+                        send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [supervisiontrack.godfather.email])
+                    except:
+                        pass
+
+                    #Reject this account and inform by email the supervisor and the user
+
+                    supervisee.profile.status = Profile.REJECTED
+                    supervisee.profile.supervision_key = None
+                    if supervisee.profile.rejection_date == None:
+                        supervisee.profile.rejection_date = now + expiration_date_delta
+
+                    supervisee.profile.save()
+                    supervisee.save()
+                else:
+                    #Not allowed to do this (unproper profile.status)
+                    reason = "You are not able to perform this action, your profile is %s"%(supervisee.profile.status)
+                    result = {
+                        'error': reason,
+                    }
+                    return BadRequestResponse(result)
+
+            else:
+                #A pending request already exist
+                reason = "You are not able to perform this action as you already have a pending request"
+                result = {
+                    'error': reason,
+                }
+                return BadRequestResponse(result)
+        else:
+            #No key is present in supervisee
+
+            #Make sure all tracks are invalid
+            supervisiontracks = SupervisionTrack.objects.filter(supervisee=supervisee, is_valid=True)
+            # This should never be the case but if it happens invalidate all tracks
+            if supervisiontracks.count() > 0:
+                now = datetime.datetime.now()
+                for track in supervisiontracks:
+                    track.is_valid = False
+                    track.expiration_date = now
+                    track.save()
+
+        #Create and assign key
+        supervisee.profile.supervision_key = supervisee.profile._generate_current_supervision_key()
+        supervisiontrack = SupervisionTrack.objects.create(
+            supervisee = supervisee,
+            godfather = godfather,
+            is_valid = False,
+            )
+
+        #Assign key to supervision track
+        supervisiontrack.supervision_key = supervisee.profile.supervision_key
+        supervisiontrack.save()
+        supervisee.profile.supervision.add(supervisiontrack)
+        supervisee.save()
+
+        #Inform by email the supervisor that he has a new supervisee request
+        from django.core.mail import send_mail
+
+        parsed_url = urlparse(settings.URL_PREFIX)
+        server_address = '%s://%s' % (parsed_url.scheme, parsed_url.hostname)
+
+        c = Context({ 'supervisor': godfather,
+                      'supervisee': supervisee,
+                      'prefix': server_address,
+                    })
+
+        try:
+            t = loader.get_template('registration/mail.godfather_validation.subject.txt')
+            subject = t.render(c)
+
+            # Note: e-mail subject *must not* contain newlines
+            subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+            t = loader.get_template('registration/mail.godfather_validation_supervisee_add_request.message.txt')
+            message = t.render(c)
+
+            send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [godfather.email])
+        except:
+            pass
+
+        return Response(status=status.HTTP_204_NO_CONTENT)
+
+
+#----------------------------------------------------------
+
+
+class BaseUpdateSupervisionTrackFromSuperviseeView(generics.UpdateAPIView):
+    model = SupervisionTrack
+    serializer_class = SupervisionTrackUpdateSerializer
+
+    def get_permissions(self):
+        permission_classes = [permissions.IsAuthenticated, IsAuthorAndNotGodfather]
+
+        self.permission_classes = permission_classes
+
+        return super(BaseUpdateSupervisionTrackFromSuperviseeView, self).get_permissions()
+
+
+#----------------------------------------------------------
+
+
+class SuperviseeReValidationView(BaseUpdateSupervisionTrackFromSuperviseeView):
+    permission_classes = BaseUpdateSupervisionTrackFromSuperviseeView.permission_classes
+
+    def put(self, request):
+        supervisee = request.user
+        now = datetime.datetime.now()
+
+        if supervisee.profile.supervision_key is not None:
+            #There's a key check if there's a valid track
+            supervisiontrack = SupervisionTrack.objects.get(supervisee=supervisee, supervisee__profile__supervision_key=models.F('supervision_key'))
+            if supervisiontrack.is_valid:
+                if supervisee.profile.status == Profile.YEARREVALIDATION:
+                    #Check Godfather validity
+                    godfather = supervisiontrack.godfather
+                    #If Godfather account is not valid. Reject the account (though this should already be done during godfather rejection)
+                    if godfather.profile.status != Profile.BLOCKED:
+                        #Change status
+                        supervisee.profile.status = Profile.ACCEPTED
+                        #Extend supervisiontrack validity for another 12 months
+                        expiration_date_delta = datetime.timedelta(days=settings.ACCOUNT_EXPIRATION_DAYS)
+                        new_expiration_date = supervisiontrack.expiration_date + expiration_date_delta
+                        supervisiontrack.expiration_date = new_expiration_date
+                        supervisiontrack.last_validation_date = now
+                    else:
+                        #Change status
+                        expiration_date_delta = datetime.timedelta(days=settings.ACCOUNT_BLOCKAGE_AFTER_FIRST_REJECTION_DAYS)
+
+                        supervisiontrack.expiration_date = now
+                        supervisiontrack.is_valid = False
+
+                        supervisee.profile.status = Profile.REJECTED
+                        supervisee.profile.rejection_date = now + expiration_date_delta
+                        supervisee.profile.supervision_key = None
+                    #save
+                    supervisiontrack.save()
+                    supervisee.profile.save()
+                    supervisee.save()
+                else:
+                    #Track already valid
+                    reason = "You don't need to revalidate at the moment, your supervision is still valid"
+                    result = {
+                        'error': reason,
+                    }
+                    return BadRequestResponse(result)
+            else:
+                #A pending request already exist
+                reason = "You are not able to perform this action as you already have a pending supervision request"
+                result = {
+                    'error': reason,
+                }
+                return BadRequestResponse(result)
+        else:
+            #No key is present in supervisee
+            #Make sure all tracks are invalid
+            supervisiontracks = SupervisionTrack.objects.filter(supervisee=supervisee, is_valid=True)
+            # This should never be the case but if it happens invalidate all tracks
+            if supervisiontracks.count() > 0:
+                now = datetime.datetime.now()
+                for track in supervisiontracks:
+                    track.is_valid = False
+                    track.expiration_date = now
+                    track.save()
+
+            #Not allowed to do this (unproper profile.status)
+            reason = "You are not allowed to perform this action, you first need to get a valid supervision"
+            result = {
+                'error': reason,
+            }
+            return BadRequestResponse(result)
+
+        return Response(status=status.HTTP_204_NO_CONTENT)
+
+
+#----------------------------------------------------------
diff --git a/beat/web/accounts/api_urls.py b/beat/web/accounts/api_urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6b3d1659cb1a0fd026afb47ee6e17d907e7a1a6
--- /dev/null
+++ b/beat/web/accounts/api_urls.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+from django.conf.urls import *
+from . import api
+
+urlpatterns = [
+    url(
+        r'^$',
+        api.GodfatherListView.as_view(),
+        name='list_supervisee'
+    ),
+
+    url(
+        r'^(?P<supervisee_name>[\w\W]+)/validate/$',
+        api.GodfatherAddSuperviseeView.as_view(),
+        name='validate_supervisee'
+    ),
+
+    url(
+        r'^(?P<supervisee_name>[\w\W]+)/remove/$',
+        api.GodfatherRemoveSuperviseeView.as_view(),
+        name='remove_supervisee'
+    ),
+
+    url(
+        r'^(?P<supervisor_name>[\w\W]+)/add/$',
+        api.SuperviseeAddGodfatherView.as_view(),
+        name='add_supervisor'
+    ),
+
+    url(
+        r'^revalidate/$',
+        api.SuperviseeReValidationView.as_view(),
+        name='revalidate_account'
+    ),
+
+]
diff --git a/beat/web/accounts/management/__init__.py b/beat/web/accounts/management/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/beat/web/accounts/management/commands/__init__.py b/beat/web/accounts/management/commands/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/beat/web/accounts/management/commands/block_rejected_users.py b/beat/web/accounts/management/commands/block_rejected_users.py
new file mode 100644
index 0000000000000000000000000000000000000000..a00ffddf793a0974328efa71a4216e627018df0b
--- /dev/null
+++ b/beat/web/accounts/management/commands/block_rejected_users.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# encoding: utf-8
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+
+from django.core.management.base import BaseCommand, CommandError
+
+from datetime import date
+import datetime
+
+from django.contrib.auth.models import User
+from django.conf import settings
+
+from ...models import SupervisionTrack
+from ...models import Profile
+from ....ui.registration.models import RegistrationProfile
+
+import sys
+import random
+
+class Command(BaseCommand):
+
+    help = 'Block rejected users after rejection date with no valid supervisor'
+
+    def add_arguments(self, parser):
+        parser.add_argument('--noinput', action='store_false', dest='interactive', default=False,
+            help=('Tells Django to NOT prompt the user for input of any kind.'))
+
+
+    def handle(self, *args, **options):
+        block = True
+
+        if options['interactive']:
+            try:
+                answer = self.get_input_data('Block rejected user(s) that have not been validated by a supervisor?  (y/n)? ', 'y').lower()
+            except KeyboardInterrupt:
+                self.stderr.write("\nOperation canceled.")
+                sys.exit(1)
+
+            if answer != 'y':
+                self.stdout.write('Block users operation canceled')
+                sys.exit(1)
+
+        if block:
+            rejected_profiles = Profile.objects.filter(status=Profile.REJECTED)
+            count = 0
+            for rejected_profile in rejected_profiles:
+                user = rejected_profile.user
+                if user.profile.rejection_date < datetime.datetime.now():
+                    count +=1
+                    user.profile.status = Profile.BLOCKED
+                    user.profile.rejection_date = None
+                    user.is_active = False
+
+                    if user.profile.supervision_key != None:
+                        supervisiontrack = SupervisionTrack.objects.get(supervision_key=rejected_profile.supervision_key)
+                        if supervisiontrack.is_valid == False and supervisiontrack.start_date is None:
+                            user.profile.supervision_key = None
+                            supervisiontrack.delete()
+
+                    user.profile.save()
+                    user.save()
+
+            self.stdout.write('{} Rejected user(s) successfully blocked/'.format(count) + '{} Total user(s) checked'.format(rejected_profiles.count()))
+
+
+    def get_input_data(self, message, default=None):
+        """
+        Override this method if you want to customize data inputs or
+        validation exceptions.
+        """
+        raw_value = raw_input(message)
+
+        if default and raw_value == '':
+            raw_value = default
+
+        return raw_value
diff --git a/beat/web/accounts/management/commands/clean_blocked_users_expired_requests.py b/beat/web/accounts/management/commands/clean_blocked_users_expired_requests.py
new file mode 100644
index 0000000000000000000000000000000000000000..98cdeb8fcaa255bbf3fe51803bfbef25748ce459
--- /dev/null
+++ b/beat/web/accounts/management/commands/clean_blocked_users_expired_requests.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# encoding: utf-8
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+
+from django.core.management.base import BaseCommand, CommandError
+
+from datetime import date
+import datetime
+
+from django.contrib.auth.models import User
+from django.conf import settings
+
+from ...models import SupervisionTrack
+from ...models import Profile
+from ....ui.registration.models import RegistrationProfile
+
+import sys
+import random
+
+class Command(BaseCommand):
+
+    help = 'Clean blocked users expired requests for supervisor validation'
+
+    def add_arguments(self, parser):
+        parser.add_argument('--noinput', action='store_false', dest='interactive', default=False,
+            help=('Tells Django to NOT prompt the user for input of any kind.'))
+
+
+    def handle(self, *args, **options):
+        block = True
+
+        if options['interactive']:
+            try:
+                answer = self.get_input_data('Clean blocked user(s) that have not been validated by a supervisor?  (y/n)? ', 'y').lower()
+            except KeyboardInterrupt:
+                self.stderr.write("\nOperation canceled.")
+                sys.exit(1)
+
+            if answer != 'y':
+                self.stdout.write('Clean blocked users operation canceled')
+                sys.exit(1)
+
+        if block:
+            blocked_profiles = Profile.objects.filter(status=Profile.BLOCKED)
+            count = 0
+            for blocked_profile in blocked_profiles:
+                user = blocked_profile.user
+                if user.profile.rejection_date is not None:
+                    if user.profile.rejection_date < datetime.datetime.now():
+                        count +=1
+                        user.profile.status = Profile.BLOCKED
+                        user.profile.rejection_date = None
+                        user.is_active = False
+
+                        if user.profile.supervision_key != None:
+                            supervisiontrack = SupervisionTrack.objects.get(supervision_key=blocked_profile.supervision_key)
+                            if supervisiontrack.is_valid == False and supervisiontrack.start_date is None:
+                                user.profile.supervision_key = None
+                                supervisiontrack.delete()
+
+                        user.profile.save()
+                        user.save()
+
+            self.stdout.write('{} Blocked user(s) successfully cleaned from expired supervision/'.format(count) + '{} Total user(s) checked'.format(blocked_profiles.count()))
+
+
+    def get_input_data(self, message, default=None):
+        """
+        Override this method if you want to customize data inputs or
+        validation exceptions.
+        """
+        raw_value = raw_input(message)
+
+        if default and raw_value == '':
+            raw_value = default
+
+        return raw_value
diff --git a/beat/web/accounts/management/commands/clean_invalid_users.py b/beat/web/accounts/management/commands/clean_invalid_users.py
new file mode 100644
index 0000000000000000000000000000000000000000..36c3059e240a1321b09708e4f87a335003fd834f
--- /dev/null
+++ b/beat/web/accounts/management/commands/clean_invalid_users.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# encoding: utf-8
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+
+from django.core.management.base import BaseCommand, CommandError
+
+from datetime import date
+import datetime
+
+from django.contrib.auth.models import User
+from django.conf import settings
+
+from ...models import SupervisionTrack
+from ...models import Profile
+from ....ui.registration.models import RegistrationProfile
+
+import sys
+import random
+
+class Command(BaseCommand):
+
+    help = 'Cleanup outdated invalid users'
+
+    def add_arguments(self, parser):
+        parser.add_argument('--noinput', action='store_false', dest='interactive', default=False,
+            help=('Tells Django to NOT prompt the user for input of any kind.'))
+
+
+    def handle(self, *args, **options):
+        clean = True
+
+        if options['interactive']:
+            try:
+                answer = self.get_input_data('Delete user(s) that have not been validated by a supervisor?  (y/n)? ', 'y').lower()
+            except KeyboardInterrupt:
+                self.stderr.write("\nOperation canceled.")
+                sys.exit(1)
+
+            if answer != 'y':
+                self.stdout.write('Cleanup canceled')
+                sys.exit(1)
+
+        if clean:
+            invalid_userprofiles_new_users = Profile.objects.filter(status=Profile.NEWUSER)
+            invalid_userprofiles_waiting_validation = Profile.objects.filter(status=Profile.WAITINGVALIDATION)
+            count = 0
+            for invalid_profile in invalid_userprofiles_new_users:
+                user = invalid_profile.user
+                supervisiontrack = SupervisionTrack.objects.get(supervision_key=invalid_profile.supervision_key)
+                registration_profile = RegistrationProfile.objects.get(user=invalid_profile.user)
+
+                expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
+                if user.profile.registration_date + expiration_date <= datetime.datetime.now():
+                    count += 1
+                    user.delete()
+                    invalid_profile.delete()
+                    supervisiontrack.delete()
+                    registration_profile.delete()
+
+            for invalid_profile in invalid_userprofiles_waiting_validation:
+                user = invalid_profile.user
+                supervisiontrack = SupervisionTrack.objects.get(supervision_key=invalid_profile.supervision_key)
+                registration_profile = RegistrationProfile.objects.get(user=invalid_profile.user)
+
+                expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS_FROM_GODFATHER)
+                if user.profile.registration_date + expiration_date <= datetime.datetime.now():
+                    count += 1
+                    user.delete()
+                    invalid_profile.delete()
+                    supervisiontrack.delete()
+                    registration_profile.delete()
+
+            self.stdout.write('{} Invalid user(s) successfully cleaned/'.format(count) + '{} Total user(s) checked'.format(invalid_userprofiles_new_users.count()+invalid_userprofiles_waiting_validation.count()))
+
+
+    def get_input_data(self, message, default=None):
+        """
+        Override this method if you want to customize data inputs or
+        validation exceptions.
+        """
+        raw_value = raw_input(message)
+
+        if default and raw_value == '':
+            raw_value = default
+
+        return raw_value
diff --git a/beat/web/accounts/management/commands/year_revalidation_users.py b/beat/web/accounts/management/commands/year_revalidation_users.py
new file mode 100644
index 0000000000000000000000000000000000000000..b00a3669cea53b8cc3f62f814925bbdec04a8848
--- /dev/null
+++ b/beat/web/accounts/management/commands/year_revalidation_users.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# encoding: utf-8
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+
+from django.core.management.base import BaseCommand, CommandError
+
+from datetime import date
+import datetime
+
+from django.db.models import Q
+from django.contrib.auth.models import User
+from django.conf import settings
+from django.core.mail import send_mail
+from django.template import loader
+from django.template import Context
+
+from ...models import SupervisionTrack
+from ...models import Profile
+from ....ui.registration.models import RegistrationProfile
+
+import sys
+import random
+from urlparse import urlparse
+
+class Command(BaseCommand):
+
+    help = 'Check users for yearly revalidation'
+
+    def add_arguments(self, parser):
+        parser.add_argument('--noinput', action='store_false', dest='interactive', default=False,
+            help=('Tells Django to NOT prompt the user for input of any kind.'))
+
+
+    def handle(self, *args, **options):
+        yearrevalidate = True
+
+        if options['interactive']:
+            try:
+                answer = self.get_input_data('Check user(s) for yearly revalidation by a supervisor?  (y/n)? ', 'y').lower()
+            except KeyboardInterrupt:
+                self.stderr.write("\nOperation canceled.")
+                sys.exit(1)
+
+            if answer != 'y':
+                self.stdout.write('Check users yearly revalidation operation canceled')
+                sys.exit(1)
+
+        if yearrevalidate:
+            torevalidate_profiles = Profile.objects.filter(Q(status=Profile.ACCEPTED)|Q(status=Profile.YEARREVALIDATION))
+
+            now = datetime.datetime.now()
+            blocked_count = 0
+            warned_count = 0
+
+            for torevalidate_profile in torevalidate_profiles:
+                user = torevalidate_profile.user
+                if user.profile.is_godfather == False:
+                    #Not godfather
+                    if user.profile.supervision_key != None:
+                        supervisiontrack = SupervisionTrack.objects.get(supervision_key=torevalidate_profile.supervision_key)
+                        if supervisiontrack.is_valid:
+                            #Check expiration date
+                            expiration_date_delta = datetime.timedelta(days=settings.ACCOUNT_BLOCKAGE_AFTER_FIRST_REJECTION_DAYS)
+                            if supervisiontrack.expiration_date < now + expiration_date_delta:
+                                #check if need to block account
+                                if now > supervisiontrack.expiration_date:
+                                    blocked_count += 1
+                                    #terminate supervision
+                                    supervisiontrack.expiration_date = now
+                                    supervisiontrack.is_valid = False
+
+                                    #block user
+                                    user.profile.status = Profile.BLOCKED
+                                    user.profile.rejection_date = None
+                                    user.profile.supervision_key = None
+                                    user.is_active = False
+
+                                    #save all changes
+                                    supervisiontrack.save()
+                                    user.profile.save()
+                                    user.save()
+                                else:
+                                    #send the warning information
+                                    #set user to YEARREVALIDATION
+                                    if user.profile.status == Profile.ACCEPTED:
+                                        user.profile.status = Profile.YEARREVALIDATION
+                                        user.profile.save()
+                                    #send email to user
+                                    for expiration_reminder in settings.EXPIRATION_REMINDERS_REVALIDATION:
+                                        if supervisiontrack.expiration_date.date() - now.date() == datetime.timedelta(days=expiration_reminder):
+                                            warned_count += 1
+
+                                            parsed_url = urlparse(settings.URL_PREFIX)
+                                            server_address = '%s://%s' % (parsed_url.scheme, parsed_url.hostname)
+
+                                            c = Context({ 'user': user,
+                                                          'expiration_date': supervisiontrack.expiration_date.date(),
+                                                        })
+
+                                            try:
+                                                t = loader.get_template('registration/mail.account_revalidation.subject.txt')
+                                                subject = t.render(c)
+
+                                                # Note: e-mail subject *must not* contain newlines
+                                                subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+                                                t = loader.get_template('registration/mail.account_revalidation.message.txt')
+                                                message = t.render(c)
+
+                                                send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [supervisiontrack.supervisee.email])
+                                            except:
+                                                pass
+
+
+                else:
+                    #Is godfather
+                    #TODO implement this procedure
+                    pass
+
+            self.stdout.write('{} blocked user(s) and '.format(blocked_count)+'{} warned user(s)/'.format(warned_count) + '{} Total user(s) that need revalidation'.format(torevalidate_profiles.count()))
+
+
+    def get_input_data(self, message, default=None):
+        """
+        Override this method if you want to customize data inputs or
+        validation exceptions.
+        """
+        raw_value = raw_input(message)
+
+        if default and raw_value == '':
+            raw_value = default
+
+        return raw_value
diff --git a/beat/web/accounts/migrations/0002_profile.py b/beat/web/accounts/migrations/0002_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..d20bbcfb9c4b69511ca77f4e43166d827bf1c1c9
--- /dev/null
+++ b/beat/web/accounts/migrations/0002_profile.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-05-08 17:00
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('accounts', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Profile',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('status', models.CharField(choices=[(b'A', b'Accepted'), (b'R', b'Rejected'), (b'W', b'Waiting Validation'), (b'B', b'Blocked')], default=b'B', max_length=1)),
+                ('is_godfather', models.BooleanField(default=False)),
+                ('supervisees', models.ManyToManyField(blank=True, related_name='users', to=settings.AUTH_USER_MODEL)),
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+    ]
diff --git a/beat/web/accounts/migrations/0003_auto_20170518_1232.py b/beat/web/accounts/migrations/0003_auto_20170518_1232.py
new file mode 100644
index 0000000000000000000000000000000000000000..f14663979235a4738808109856b055b994e611a1
--- /dev/null
+++ b/beat/web/accounts/migrations/0003_auto_20170518_1232.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-05-18 12:32
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('accounts', '0002_profile'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='SupervisionTrack',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('is_valid', models.BooleanField(default=False)),
+                ('start_date', models.DateTimeField(blank=True, null=True)),
+                ('expiration_date', models.DateTimeField(blank=True, null=True)),
+                ('last_validation_date', models.DateTimeField(blank=True, null=True)),
+                ('godfather', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='supervisees', to=settings.AUTH_USER_MODEL)),
+                ('supervisee', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='supervisors', to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='supervisees',
+        ),
+        migrations.AddField(
+            model_name='profile',
+            name='supervision',
+            field=models.ManyToManyField(blank=True, related_name='profiles', to='accounts.SupervisionTrack'),
+        ),
+    ]
diff --git a/beat/web/accounts/migrations/0004_auto_20170523_1736.py b/beat/web/accounts/migrations/0004_auto_20170523_1736.py
new file mode 100644
index 0000000000000000000000000000000000000000..bfe5e0b238a1bd465a7d22391db1a93ca0c552d7
--- /dev/null
+++ b/beat/web/accounts/migrations/0004_auto_20170523_1736.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-05-23 17:36
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('accounts', '0003_auto_20170518_1232'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='profile',
+            name='supervision_key',
+            field=models.CharField(blank=True, max_length=40, null=True),
+        ),
+        migrations.AddField(
+            model_name='supervisiontrack',
+            name='supervision_key',
+            field=models.CharField(blank=True, max_length=40, null=True),
+        ),
+    ]
diff --git a/beat/web/accounts/migrations/0005_auto_20170608_0935.py b/beat/web/accounts/migrations/0005_auto_20170608_0935.py
new file mode 100644
index 0000000000000000000000000000000000000000..60270a7b442a6e638d721252c11c18aeb325c3f6
--- /dev/null
+++ b/beat/web/accounts/migrations/0005_auto_20170608_0935.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-06-08 09:35
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('accounts', '0004_auto_20170523_1736'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='profile',
+            name='registration_date',
+            field=models.DateTimeField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='profile',
+            name='status',
+            field=models.CharField(choices=[(b'N', b'New User'), (b'W', b'Waiting Validation'), (b'A', b'Accepted'), (b'R', b'Rejected'), (b'Y', b'Yearly revalidation'), (b'B', b'Blocked no supervisor')], default=b'B', max_length=1),
+        ),
+    ]
diff --git a/beat/web/accounts/migrations/0006_auto_20170608_1451.py b/beat/web/accounts/migrations/0006_auto_20170608_1451.py
new file mode 100644
index 0000000000000000000000000000000000000000..a0e80203a504f995905542446ee1186823e12505
--- /dev/null
+++ b/beat/web/accounts/migrations/0006_auto_20170608_1451.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-06-08 14:51
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('accounts', '0005_auto_20170608_0935'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='supervisiontrack',
+            name='godfather',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supervisees', to=settings.AUTH_USER_MODEL),
+        ),
+    ]
diff --git a/beat/web/accounts/migrations/0007_profile_first_rejection_date.py b/beat/web/accounts/migrations/0007_profile_first_rejection_date.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff5cac2066bca90f4a195a5f5936c1e0c86780d7
--- /dev/null
+++ b/beat/web/accounts/migrations/0007_profile_first_rejection_date.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-06-26 14:06
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('accounts', '0006_auto_20170608_1451'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='profile',
+            name='first_rejection_date',
+            field=models.DateTimeField(blank=True, null=True),
+        ),
+    ]
diff --git a/beat/web/accounts/migrations/0008_auto_20170626_1516.py b/beat/web/accounts/migrations/0008_auto_20170626_1516.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1ac0352304f8d6924686edc1f12ddf0397abe4c
--- /dev/null
+++ b/beat/web/accounts/migrations/0008_auto_20170626_1516.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-06-26 15:16
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('accounts', '0007_profile_first_rejection_date'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='profile',
+            old_name='first_rejection_date',
+            new_name='rejection_date',
+        ),
+    ]
diff --git a/beat/web/accounts/migrations/0009_auto_20170627_0956.py b/beat/web/accounts/migrations/0009_auto_20170627_0956.py
new file mode 100644
index 0000000000000000000000000000000000000000..00e134aecaa5d92635068f42feab0b5102feeec5
--- /dev/null
+++ b/beat/web/accounts/migrations/0009_auto_20170627_0956.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-06-27 09:56
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('accounts', '0008_auto_20170626_1516'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='supervisiontrack',
+            name='supervisee',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supervisors', to=settings.AUTH_USER_MODEL),
+        ),
+    ]
diff --git a/beat/web/accounts/migrations/0010_check_all_accounts.py b/beat/web/accounts/migrations/0010_check_all_accounts.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1dc9a2f8f647fc5b70a2a9a8277064072682b73
--- /dev/null
+++ b/beat/web/accounts/migrations/0010_check_all_accounts.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2017 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+from __future__ import unicode_literals
+
+from django.db.models import Q
+from django.core.urlresolvers import reverse
+from django.template import loader
+from django.template import Context
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+from django.contrib.auth.models import User
+from datetime import datetime, timedelta
+import datetime
+import re
+from urlparse import urlparse
+
+import simplejson as json
+
+def set_profile_state(apps, schema_editor):
+    '''Set profile status'''
+
+    profiles = apps.get_model("accounts", "Profile")
+    supervisiontracks = apps.get_model("accounts", "SupervisionTrack")
+
+    users = User.objects.all()
+    now = datetime.datetime.now()
+    expiration_date_delta = datetime.timedelta(days=settings.ACCOUNT_BLOCKAGE_AFTER_FIRST_REJECTION_DAYS)
+
+    specialusers = ["AnonymousUser", "plot", "system", "scheduler"]
+    for user in users:
+        user.save()
+        if user.is_staff or user.username in specialusers:
+            user.profile.status = 'A'
+            user.profile.rejection_date = None
+        else:
+            #reject this account and inform by email the user
+            user.profile.status = 'R'
+            user.profile.rejection_date = now + expiration_date_delta
+            from django.core.mail import send_mail
+
+            parsed_url = urlparse(settings.URL_PREFIX)
+            server_address = '%s://%s' % (parsed_url.scheme, parsed_url.hostname)
+
+            c = Context({ 'user': user,
+                          'prefix': server_address,
+                        })
+
+            try:
+                t = loader.get_template('registration/mail.migration_10_accounts.subject.txt')
+                subject = t.render(c)
+
+                # Note: e-mail subject *must not* contain newlines
+                subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+                t = loader.get_template('registration/mail.migration_10_accounts.message.txt')
+                message = t.render(c)
+
+                send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email])
+            except:
+                pass
+
+        user.profile.supervision_key = None
+
+        user.profile.save()
+        user.save()
+
+def backward_dummy(apps, schema_editor):
+    pass
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('accounts', '0009_auto_20170627_0956'),
+    ]
+
+    operations = [
+        migrations.RunPython(set_profile_state, backward_dummy)
+    ]
diff --git a/beat/web/accounts/models.py b/beat/web/accounts/models.py
index 79b0629bda6e06f62d3e3f012418680ea968f4e7..ae35b2a0556be1daac5fb5e2a0f0b9fcc18bd2b8 100644
--- a/beat/web/accounts/models.py
+++ b/beat/web/accounts/models.py
@@ -27,6 +27,11 @@
 
 from django.db import models
 from django.contrib.auth.models import User
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+
+import random
+import string
 
 class AccountSettings(models.Model):
 
@@ -39,3 +44,77 @@ class AccountSettings(models.Model):
     experiment_mail_notifications_enabled = models.BooleanField(default=True)
     database_notifications_enabled = models.BooleanField(default=True)
     environment_notifications_enabled = models.BooleanField(default=True)
+
+
+class SupervisionTrack(models.Model):
+
+    #_____ Fields __________
+
+    supervisee           = models.ForeignKey(User, on_delete=models.CASCADE,
+        related_name='supervisors')
+    godfather            = models.ForeignKey(User, on_delete=models.CASCADE,
+        related_name='supervisees')
+    is_valid             = models.BooleanField(default=False)
+    start_date           = models.DateTimeField(null=True, blank=True)
+    expiration_date      = models.DateTimeField(null=True, blank=True)
+    last_validation_date = models.DateTimeField(null=True, blank=True)
+    supervision_key      = models.CharField(max_length=40, null=True, blank=True)
+
+    def __unicode__(self):
+      return u'Godfather: %s, Supervisee, %s, Validity: %s' % (self.godfather.username, self.supervisee.username, self.is_valid)
+
+
+class Profile(models.Model):
+    #_____ Constants __________
+
+    #New account creation 'N'/'W'
+    #Godfather acceptance/rejection 'A'/'R'
+    #Yearly revalidation/blockage  'Y'/'B'
+    NEWUSER           = 'N'
+    WAITINGVALIDATION = 'W'
+    ACCEPTED          = 'A'
+    REJECTED          = 'R'
+    YEARREVALIDATION  = 'Y'
+    BLOCKED           = 'B'
+
+    PROFILE_STATUS = (
+        (NEWUSER, 'New User'),
+        (WAITINGVALIDATION, 'Waiting Validation'),
+        (ACCEPTED,    'Accepted'),
+        (REJECTED,    'Rejected'),
+        (YEARREVALIDATION, 'Yearly revalidation'),
+        (BLOCKED, 'Blocked no supervisor'),
+    )
+
+    #_____ Fields __________
+
+    # This field is required.
+    user = models.OneToOneField(User, on_delete=models.CASCADE)
+
+    # Other fields here
+    status       = models.CharField(max_length=1, choices=PROFILE_STATUS, default=BLOCKED)
+    is_godfather = models.BooleanField(default=False)
+    supervision  = models.ManyToManyField(SupervisionTrack, related_name='profiles', blank=True)
+    supervision_key = models.CharField(max_length=40, null=True, blank=True)
+    registration_date = models.DateTimeField(null=True, blank=True)
+    rejection_date = models.DateTimeField(null=True, blank=True)
+
+    def __unicode__(self):
+      return u'User: %s' % self.user.username
+
+    def _generate_current_supervision_key(self):
+      length = 40
+      return ''.join(random.choice(string.ascii_letters + string.digits) for _
+          in range(length))
+
+@receiver(post_save, sender=User)
+def create_user_profile(sender, instance, created, **kwargs):
+    if created:
+        Profile.objects.create(user=instance)
+    else:
+        return
+
+@receiver(post_save, sender=User)
+def save_user_profile(sender, instance, **kwargs):
+    player, created = Profile.objects.get_or_create(user=instance)
+    instance.profile.save()
diff --git a/beat/web/accounts/permissions.py b/beat/web/accounts/permissions.py
new file mode 100644
index 0000000000000000000000000000000000000000..18f8c351a8ba901c8068d2ca826d25a0ac815948
--- /dev/null
+++ b/beat/web/accounts/permissions.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+from rest_framework import permissions
+
+
+#----------------------------------------------------------
+
+
+class IsGodfatherAndAuthor(permissions.BasePermission):
+    """
+    The logged in user should also be the author
+    """
+    message = 'Not a supervisor account'
+
+    def has_permission(self, request, view):
+        return request.user.profile.is_godfather
+
+
+#----------------------------------------------------------
+
+
+class IsAuthorAndNotGodfather(permissions.BasePermission):
+    """
+    The logged in user should also be the author
+    """
+    message = 'Not a supervisee account'
+
+    def has_permission(self, request, view):
+        return not request.user.profile.is_godfather
diff --git a/beat/web/accounts/serializers.py b/beat/web/accounts/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..491af9bda3c9e613818c0765f42d0b228e87c395
--- /dev/null
+++ b/beat/web/accounts/serializers.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+from django.contrib.auth.models import User, AnonymousUser
+
+from rest_framework import serializers
+
+from .models import Profile, SupervisionTrack
+from ..common.models import Contribution
+from ..common.fields import JSONSerializerField
+from ..ui.templatetags.markup import restructuredtext
+from ..common.utils import validate_restructuredtext
+
+import simplejson as json
+
+
+#----------------------------------------------------------
+
+
+class UserSerializer(serializers.ModelSerializer):
+    username = serializers.SerializerMethodField()
+    email = serializers.SerializerMethodField()
+
+    class Meta:
+        model = User
+        fields = ['username', 'email']
+
+    def get_username(self, obj):
+        return obj.username
+
+    def get_email(self, obj):
+        return obj.email
+
+
+#----------------------------------------------------------
+
+
+class BasicSupervisionTrackSerializer(serializers.ModelSerializer):
+    supervisee = UserSerializer()
+    godfather = UserSerializer()
+    is_valid = serializers.SerializerMethodField()
+    start_date = serializers.SerializerMethodField()
+    expiration_date = serializers.SerializerMethodField()
+    last_validation_date = serializers.SerializerMethodField()
+    supervision_key = serializers.SerializerMethodField()
+
+    class Meta:
+        model = SupervisionTrack
+        fields = ['is_valid']
+
+    #def get_supervisee(self, obj):
+    #    return obj.supervisee
+
+    #def get_godfather(self, obj):
+    #    return obj.godfather
+
+    def get_is_valid(self, obj):
+        return obj.is_valid
+
+    def get_start_date(self, obj):
+        return obj.start_date
+
+    def get_expiration_date(self, obj):
+        return obj.expiration_date
+
+    def get_last_validation_date(self, obj):
+        return obj.last_validation_date
+
+    def get_supervision_key(self, obj):
+        return obj.supervision_key
+
+
+#----------------------------------------------------------
+
+
+class FullSupervisionTrackSerializer(BasicSupervisionTrackSerializer):
+
+    class Meta(BasicSupervisionTrackSerializer.Meta):
+        fields = ['supervisee', 'godfather', 'is_valid', 'start_date', 'expiration_date','last_validation_date', 'supervision_key']
+
+
+#----------------------------------------------------------
+
+
+class SupervisionTrackUpdateSerializer(BasicSupervisionTrackSerializer):
+    pass
+
+
+#----------------------------------------------------------
diff --git a/beat/web/accounts/static/accounts/css/dialogs.css b/beat/web/accounts/static/accounts/css/dialogs.css
new file mode 100644
index 0000000000000000000000000000000000000000..004a329b992a0615901c2e7e2c606f69256f9f23
--- /dev/null
+++ b/beat/web/accounts/static/accounts/css/dialogs.css
@@ -0,0 +1,96 @@
+/****************** ACCOUNT DIALOG ******************************/
+
+div.renew_account .progress
+{
+    margin: 40px auto;
+}
+
+
+div.renew_account #infos
+{
+    text-align: center;
+    display: block;
+    margin-top: 10px;
+    margin-bottom: 10px;
+    color: #777777;
+}
+
+
+div.renew_account span.label
+{
+    display: inline-block;
+    width: 15%;
+    color: black;
+    font-size: 1em;
+}
+
+
+div.renew_account input#prefix,
+div.renew_account input#suffix
+{
+    display: inline-block;
+    width: 79%;
+}
+
+
+div.renew_account input#suffix
+{
+    margin-top: 10px;
+}
+
+
+div.renew_account span.documentation
+{
+    color: #AAAAAA;
+    font-size: 0.8em;
+}
+
+div.change_supervisor.progress
+{
+    margin: 40px auto;
+}
+
+
+div.change_supervisor #infos
+{
+    text-align: center;
+    display: block;
+    margin-top: 10px;
+    margin-bottom: 10px;
+    color: #777777;
+}
+
+
+div.change_supervisor span.label
+{
+    display: inline-block;
+    width: 50%;
+    color: black;
+    font-size: 1em;
+}
+
+
+div.change_supervisor input#new_godfather_username
+{
+    display: inline-block;
+    width: 50%;
+}
+
+
+div.change_supervisor input#new_godfather_username
+{
+    margin-top: 10px;
+}
+
+
+div.change_supervisor span.documentation
+{
+    color: #AAAAAA;
+    font-size: 0.8em;
+}
+
+.btn-supervisee
+{
+    padding-top: 0em;
+    padding-bottom: 0em;
+}
diff --git a/beat/web/accounts/static/accounts/js/dialogs.js b/beat/web/accounts/static/accounts/js/dialogs.js
new file mode 100644
index 0000000000000000000000000000000000000000..2e65ddcafc38b49acc4dedc80fe87c2a38c57b35
--- /dev/null
+++ b/beat/web/accounts/static/accounts/js/dialogs.js
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ * 
+ * This file is part of the beat.web module of the BEAT platform.
+ * 
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ * 
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * You should have received a copy of the GNU Affero Public License along
+ * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
+*/
+/**
+ * beat.accounts.dialogs.js
+ * Implementation of experiment-related dialogs
+ */
+if (beat === undefined) var beat = {};
+if (beat.accounts === undefined) beat.accounts = {};
+if (beat.accounts.dialogs === undefined) beat.accounts.dialogs = {};
+
+/**
+ * Implements a modal dialog to renew a user account.
+ * The user is presented with a small modal window that will allow
+ * him to certify the wish to renew his account. It is possible to cancel
+ * the action at any moment by pressing "Cancel" or hitting the ESC key.
+ *
+ * Parameters:
+ *
+ *   dialog_id: ID of the DOM element to use
+ *   api_url (str): The URL towards the API for renewing the account
+ *   redirect_url (str): The URL where to redirect the user if the operation is
+ *       successful
+ */
+beat.accounts.dialogs.modal_renew_account = function(dialog_id, api_url, redirect_url) {
+
+  // Create the dialog
+  $('#' + dialog_id).dialog({
+    autoOpen: false,
+    resizable: false,
+    width: Math.min($(window).width() * 0.6, 700),
+    position: { my: "center", at: "center", of: window },
+    modal: true,
+    closeOnEscape: true,
+    buttons: [
+    {
+      id: 'button-' + dialog_id + '-renameit',
+      text: 'Revalidate',
+      click: function() {
+
+        $.ajaxSetup({
+          beforeSend: function(xhr, settings) {
+            var csrftoken = $.cookie('csrftoken');
+            xhr.setRequestHeader('X-CSRFToken', csrftoken);
+          }
+        });
+
+        $.ajax({
+          type: "PUT",
+          url: api_url,
+          data: JSON.stringify({
+            name: name,
+          }),
+          contentType: "application/json; charset=utf-8",
+          dataType: "json",
+
+          success: function(data) {
+            $('#' + dialog_id).dialog("close");
+            window.location.href = redirect_url;
+          },
+
+          error: function(jqXHR, textStatus, errorThrown) {
+            if ((jqXHR.status == 400) && (jqXHR.responseText.length > 0))
+              alert(jqXHR.responseText);
+            else
+              alert('Error: ' + errorThrown);
+
+            $(this).dialog("close");
+          }
+        });
+      }
+    },
+    {
+      id: 'button-' + dialog_id + '-cancel',
+      text: 'Cancel',
+      click: function() {
+        $(this).dialog("close");
+      }
+    }
+    ]
+  });
+
+  // Display the dialog
+  $('#' + dialog_id).dialog("open");
+}
+
+/**
+ * Implements a modal dialog to change supervisor.
+ * The user is presented with a small modal window that will allow
+ * him to add the new supervisor username. It is possible to cancel the action at
+ * any moment by pressing "Cancel" or hitting the ESC key.
+ *
+ * Parameters:
+ *
+ *   dialog_id: ID of the DOM element to use
+ *   old_godfather_username: previous supervisor username
+ *   api_url (str): The URL towards the API for changing the supervisor
+ *   redirect_url (str): The URL where to redirect the user if the operation is
+ *       successful
+ */
+beat.accounts.dialogs.modal_change_supervisor = function(dialog_id, old_godfather_username, api_url, redirect_url) {
+
+  var split_api_url = api_url.split(old_godfather_username);
+
+  // Create the dialog
+  $('#' + dialog_id).dialog({
+    autoOpen: false,
+    resizable: false,
+    width: Math.min($(window).width() * 0.6, 700),
+    position: { my: "center", at: "center", of: window },
+    modal: true,
+    closeOnEscape: true,
+    buttons: [
+    {
+      id: 'button-' + dialog_id + '-renameit',
+      text: 'Request',
+      click: function() {
+
+      var new_godfather_username = $('#' + dialog_id + ' #new_godfather_username')[0].value;
+
+      var name = $('#' + dialog_id + ' input')[0].value.trim();
+
+        if (name.length == 0)
+        {
+          alert("You can't enter an empty supervisor username!");
+          return;
+        }
+
+        if (new_godfather_username == old_godfather_username)
+        {
+          alert("The new supervisor username is the same as the previous one!");
+          return;
+        }
+
+        $.ajaxSetup({
+          beforeSend: function(xhr, settings) {
+            var csrftoken = $.cookie('csrftoken');
+            xhr.setRequestHeader('X-CSRFToken', csrftoken);
+          }
+        });
+
+        $.ajax({
+          type: "POST",
+          url: split_api_url[0] + new_godfather_username + split_api_url[1],
+          data: JSON.stringify({
+            name: name,
+          }),
+          contentType: "application/json; charset=utf-8",
+          dataType: "json",
+
+          success: function(data) {
+            $('#' + dialog_id).dialog("close");
+            window.location.href = redirect_url;
+          },
+
+          error: function(jqXHR, textStatus, errorThrown) {
+            if ((jqXHR.status == 400) && (jqXHR.responseText.length > 0))
+              alert(jqXHR.responseText);
+            else
+              alert('Error: ' + errorThrown);
+          }
+        });
+      }
+    },
+    {
+      id: 'button-' + dialog_id + '-cancel',
+      text: 'Cancel',
+      click: function() {
+        $(this).dialog("close");
+      }
+    }
+    ]
+  });
+
+  // Initialise the dialog content
+  $('#' + dialog_id + ' input')[0].value = old_godfather_username;
+
+  // Display the dialog
+  $('#' + dialog_id).dialog("open");
+}
+
+/**
+ * Implements a modal dialog to add a supervisor.
+ * The user is presented with a small modal window that will allow
+ * him to add to set the supervisor username. It is possible to cancel the action at
+ * any moment by pressing "Cancel" or hitting the ESC key.
+ *
+ * Parameters:
+ *
+ *   dialog_id: ID of the DOM element to use
+ *   api_url (str): The URL towards the API for adding a supervisor
+ *   redirect_url (str): The URL where to redirect the user if the operation is
+ *       successful
+ */
+beat.accounts.dialogs.modal_add_supervisor = function(dialog_id, api_url, redirect_url) {
+
+  var split_api_url = api_url.split("nogodfather");
+
+  // Create the dialog
+  $('#' + dialog_id).dialog({
+    autoOpen: false,
+    resizable: false,
+    width: Math.min($(window).width() * 0.6, 700),
+    position: { my: "center", at: "center", of: window },
+    modal: true,
+    closeOnEscape: true,
+    buttons: [
+    {
+      id: 'button-' + dialog_id + '-renameit',
+      text: 'Request',
+      click: function() {
+
+      var new_godfather_username = $('#' + dialog_id + ' #new_godfather_username')[0].value;
+
+      var name = $('#' + dialog_id + ' input')[0].value.trim();
+
+        if (name.length == 0)
+        {
+          alert("You can't enter an empty supervisor username!");
+          return;
+        }
+
+        $.ajaxSetup({
+          beforeSend: function(xhr, settings) {
+            var csrftoken = $.cookie('csrftoken');
+            xhr.setRequestHeader('X-CSRFToken', csrftoken);
+          }
+        });
+
+        $.ajax({
+          type: "POST",
+          url: split_api_url[0] + new_godfather_username + split_api_url[1],
+          data: JSON.stringify({
+            name: name,
+          }),
+          contentType: "application/json; charset=utf-8",
+          dataType: "json",
+
+          success: function(data) {
+            $('#' + dialog_id).dialog("close");
+            window.location.href = redirect_url;
+          },
+
+          error: function(jqXHR, textStatus, errorThrown) {
+            if ((jqXHR.status == 400) && (jqXHR.responseText.length > 0))
+              alert(jqXHR.responseText);
+            else
+              alert('Error: ' + errorThrown);
+          }
+        });
+      }
+    },
+    {
+      id: 'button-' + dialog_id + '-cancel',
+      text: 'Cancel',
+      click: function() {
+        $(this).dialog("close");
+      }
+    }
+    ]
+  });
+
+  // Initialise the dialog content
+  $('#' + dialog_id + ' input')[0].value = '';
+
+  // Display the dialog
+  $('#' + dialog_id).dialog("open");
+}
+
+/**
+ * Implements a modal dialog for the supervisor to validate a supervisee
+ * account. It is possible to cancel the action at any moment by pressing
+ * "Cancel" or hitting the ESC key.
+ *
+ * Parameters:
+ *
+ *   dialog_id: ID of the DOM element to use
+ *   api_url (str): The URL towards the API to validate a supervisee
+ *   redirect_url (str): The URL where to redirect the user if the operation is
+ *       successful
+ */
+beat.accounts.dialogs.modal_validate_supervisee = function(dialog_id, api_url, redirect_url) {
+
+  // Create the dialog
+  $('#' + dialog_id).dialog({
+    autoOpen: false,
+    resizable: false,
+    width: Math.min($(window).width() * 0.6, 700),
+    position: { my: "center", at: "center", of: window },
+    modal: true,
+    closeOnEscape: true,
+    buttons: [
+    {
+      id: 'button-' + dialog_id + '-renameit',
+      text: 'Accept',
+      click: function() {
+
+        $.ajaxSetup({
+          beforeSend: function(xhr, settings) {
+            var csrftoken = $.cookie('csrftoken');
+            xhr.setRequestHeader('X-CSRFToken', csrftoken);
+          }
+        });
+
+        $.ajax({
+          type: "PUT",
+          url: api_url,
+          data: JSON.stringify({
+            name: name,
+          }),
+          contentType: "application/json; charset=utf-8",
+          dataType: "json",
+
+          success: function(data) {
+            $('#' + dialog_id).dialog("close");
+            window.location.href = redirect_url;
+          },
+
+          error: function(jqXHR, textStatus, errorThrown) {
+            if ((jqXHR.status == 400) && (jqXHR.responseText.length > 0))
+              alert(jqXHR.responseText);
+            else
+              alert('Error: ' + errorThrown);
+
+            $(this).dialog("close");
+          }
+        });
+      }
+    },
+    {
+      id: 'button-' + dialog_id + '-cancel',
+      text: 'Cancel',
+      click: function() {
+        $(this).dialog("close");
+      }
+    }
+    ]
+  });
+
+  // Display the dialog
+  $('#' + dialog_id).dialog("open");
+}
+
+/**
+ * Implements a modal dialog for the supervisor to remove a supervisee
+ * account. It is possible to cancel the action at any moment by pressing
+ * "Cancel" or hitting the ESC key.
+ *
+ * Parameters:
+ *
+ *   dialog_id: ID of the DOM element to use
+ *   api_url (str): The URL towards the API to remove a supervisee
+ *   redirect_url (str): The URL where to redirect the user if the operation is
+ *       successful
+ */
+beat.accounts.dialogs.modal_remove_supervisee = function(dialog_id, api_url, redirect_url) {
+
+  // Create the dialog
+  $('#' + dialog_id).dialog({
+    autoOpen: false,
+    resizable: false,
+    width: Math.min($(window).width() * 0.6, 700),
+    position: { my: "center", at: "center", of: window },
+    modal: true,
+    closeOnEscape: true,
+    buttons: [
+    {
+      id: 'button-' + dialog_id + '-renameit',
+      text: 'Reject',
+      click: function() {
+
+        $.ajaxSetup({
+          beforeSend: function(xhr, settings) {
+            var csrftoken = $.cookie('csrftoken');
+            xhr.setRequestHeader('X-CSRFToken', csrftoken);
+          }
+        });
+
+        $.ajax({
+          type: "PUT",
+          url: api_url,
+          data: JSON.stringify({
+            name: name,
+          }),
+          contentType: "application/json; charset=utf-8",
+          dataType: "json",
+
+          success: function(data) {
+            $('#' + dialog_id).dialog("close");
+            window.location.href = redirect_url;
+          },
+
+          error: function(jqXHR, textStatus, errorThrown) {
+            if ((jqXHR.status == 400) && (jqXHR.responseText.length > 0))
+              alert(jqXHR.responseText);
+            else
+              alert('Error: ' + errorThrown);
+
+            $(this).dialog("close");
+          }
+        });
+      }
+    },
+    {
+      id: 'button-' + dialog_id + '-cancel',
+      text: 'Cancel',
+      click: function() {
+        $(this).dialog("close");
+      }
+    }
+    ]
+  });
+
+  // Display the dialog
+  $('#' + dialog_id).dialog("open");
+}
diff --git a/beat/web/accounts/templates/accounts/dialogs/change_supervisor.html b/beat/web/accounts/templates/accounts/dialogs/change_supervisor.html
new file mode 100644
index 0000000000000000000000000000000000000000..b4156162b534b29c739d3d7ecce14c92675da92d
--- /dev/null
+++ b/beat/web/accounts/templates/accounts/dialogs/change_supervisor.html
@@ -0,0 +1,32 @@
+{% comment %}
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ * 
+ * This file is part of the beat.web module of the BEAT platform.
+ * 
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ * 
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * You should have received a copy of the GNU Affero Public License along
+ * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
+{% endcomment %}
+
+<div id="{{ dialog_id }}" class="change_supervisor" style="display:none;" title="Supervision update">
+    <div class="settings">
+        <div><span id="infos"></span></div>
+        Take into account that requesting a new supervision will temporarely set your account to an inactive state until the request is validated by your new supervisor.<br>
+        If your new supervisor rejects your supervision request, you need to find another supervisor.<br>
+        If no supervisor is found after a specific date, your account will be blocked and you will need to proceed with an account reactivation request<br>
+        <div><span class="label">New supervisor username: </span><input id="new_godfather_username" type="text" /></div>
+        <div><span class="documentation">A request for supervision will be sent to the new supervisor</span></div>
+    </div>
+</div>
diff --git a/beat/web/accounts/templates/accounts/dialogs/remove_supervisee.html b/beat/web/accounts/templates/accounts/dialogs/remove_supervisee.html
new file mode 100644
index 0000000000000000000000000000000000000000..a4743fbe2a34563d33aa71b2cc6fdccca05ab6cd
--- /dev/null
+++ b/beat/web/accounts/templates/accounts/dialogs/remove_supervisee.html
@@ -0,0 +1,28 @@
+{% comment %}
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ * 
+ * This file is part of the beat.web module of the BEAT platform.
+ * 
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ * 
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * You should have received a copy of the GNU Affero Public License along
+ * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
+{% endcomment %}
+
+<div id="{{ dialog_id }}" class="renew_account" style="display:none;" title="Remove supervisee">
+    <div class="settings">
+        <div><span id="infos"></span></div>
+        This action will remove the supervisee from your supervision list.
+    </div>
+</div>
diff --git a/beat/web/accounts/templates/accounts/dialogs/renew_account.html b/beat/web/accounts/templates/accounts/dialogs/renew_account.html
new file mode 100644
index 0000000000000000000000000000000000000000..e94afdb87174c7054c4a3c8db55a3cf643492431
--- /dev/null
+++ b/beat/web/accounts/templates/accounts/dialogs/renew_account.html
@@ -0,0 +1,29 @@
+{% comment %}
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ * 
+ * This file is part of the beat.web module of the BEAT platform.
+ * 
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ * 
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * You should have received a copy of the GNU Affero Public License along
+ * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
+{% endcomment %}
+
+<div id="{{ dialog_id }}" class="renew_account" style="display:none;" title="Account revalidation">
+    <div class="settings">
+        <div><span id="infos"></span></div>
+        By revalidating your account you acknowledge that you wish to continue using the platform and your
+        supervision is still valid.
+    </div>
+</div>
diff --git a/beat/web/accounts/templates/accounts/dialogs/validate_supervisee.html b/beat/web/accounts/templates/accounts/dialogs/validate_supervisee.html
new file mode 100644
index 0000000000000000000000000000000000000000..e90a8ed36cb54c4c6fedb0a9524dae437c179ee6
--- /dev/null
+++ b/beat/web/accounts/templates/accounts/dialogs/validate_supervisee.html
@@ -0,0 +1,28 @@
+{% comment %}
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ * 
+ * This file is part of the beat.web module of the BEAT platform.
+ * 
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ * 
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * You should have received a copy of the GNU Affero Public License along
+ * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
+{% endcomment %}
+
+<div id="{{ dialog_id }}" class="renew_account alert-warning" style="display:none;" title="Validate supervisee">
+    <div class="settings">
+        <div><span id="infos"></span></div>
+        This action will add the supervisee to your supervision list. You therefore acknowledge that this user works under your supervision and follows the platform terms of service.
+    </div>
+</div>
diff --git a/beat/web/accounts/templates/accounts/settings.html b/beat/web/accounts/templates/accounts/settings.html
index 6b00af35064bde03c0d6229d80a6e50cf790a5c8..075999f8cb4f68eba5714bb9d8f1ef0b341f2274 100644
--- a/beat/web/accounts/templates/accounts/settings.html
+++ b/beat/web/accounts/templates/accounts/settings.html
@@ -25,9 +25,27 @@
 {% load ui_tags %}
 {% load gravatar %}
 {% load registration_tags %}
+{% load account_tags %}
 
 {% block title %}Settings for {{ request.user.first_name }} ({{ request.user.username }}){% endblock %}
 
+{% block stylesheets %}
+{{ block.super }}
+<link rel="stylesheet" href="{% fingerprint "accounts/css/dialogs.css" %}" type="text/css" media="screen" />
+<link rel="stylesheet" href="{% fingerprint "jquery-ui/themes/base/minified/jquery-ui.min.css" %}" type="text/css" media="screen" />
+{% endblock %}
+
+{% block scripts %}
+{{ block.super }}
+{% csrf_token %}
+<script src="{% fingerprint "accounts/js/dialogs.js" %}" type="text/javascript" charset="utf-8"></script>
+<script src="{% fingerprint "jquery-ui/ui/minified/jquery.ui.core.min.js" %}" type="text/javascript" charset="utf-8"></script>
+<script src="{% fingerprint "jquery-ui/ui/minified/jquery.ui.position.min.js" %}" type="text/javascript" charset="utf-8"></script>
+<script src="{% fingerprint "jquery-ui/ui/minified/jquery.ui.widget.min.js" %}" type="text/javascript" charset="utf-8"></script>
+<script src="{% fingerprint "jquery-ui/ui/minified/jquery.ui.button.min.js" %}" type="text/javascript" charset="utf-8"></script>
+<script src="{% fingerprint "jquery-ui/ui/minified/jquery.ui.dialog.min.js" %}" type="text/javascript" charset="utf-8"></script>
+{% endblock %}
+
 {% block content %}
 <div class="row">
   <div class="col-sm-6 col-sm-offset-3">
@@ -178,6 +196,196 @@
   </div>
 </div>
 
+{% if not user.profile.is_godfather and not user.is_superuser %}
+
+<div class="row">
+
+  <div class="col-sm-4 col-sm-offset-1">
+
+    <div class="panel panel-default">
+      <div class="panel-heading"><i class="fa fa-credit-card-alt"></i> Account management
+          {% if user.profile.status == 'A' %}
+          <span class="label label-success" style="float:right">Status Accepted</span>
+          {% elif user.profile.status == 'R' %}
+            {% if user.profile.supervision_key == None  %}
+                <span class="label label-danger" style="float:right">Status Rejected</span>
+            {% else %}
+                <span class="label label-warning" style="float:right">Status Pending</span>
+            {% endif %}
+          {% elif user.profile.status == 'Y' %}
+          <span class="label label-warning" style="float:right">Status Revalidation Required</span>
+          {% endif %}
+      </div>
+      <div class="panel-body">
+        <form id="token" method="post" action="" class="form">
+          {% csrf_token %}
+          <div class="form-group" style="text-align: justify;">
+          {% if user.profile.status == 'A' %}
+          <li class="list-group-item list-group-item-success" style="text-align:center;">Status is currently accepted: No action is directly required</li>
+          <br>
+          <h4>Supervision Information</h4>
+          <ul class="list-group">
+            <li class="list-group-item"><b>Supervisor:</b> <a href="{% url "activity-stream" supervisiontrack.godfather.username %}">{{ supervisiontrack.godfather.username }}</a></li>
+            <li class="list-group-item"><b>Supervision start date:</b> {{ supervisiontrack.start_date }}</li>
+            <li class="list-group-item"><b>Supervision last validation date:</b> {{ supervisiontrack.last_validation_date }}</li>
+            <li class="list-group-item"><b>Supervision expiration date:</b> {{ supervisiontrack.expiration_date }}</li>
+          </ul>
+          <div class="form-group" style="text-align: center;">
+          <input type="hidden" name="token" value="true">
+            <button type="button" class="btn btn-warning" onclick="beat.accounts.dialogs.modal_change_supervisor('change_supervisor', '{{ supervisiontrack.godfather.username }}', '{% url 'api_accounts:add_supervisor' supervisiontrack.godfather.username %}', '{% url 'accounts:settings' %}');"><i class="fa fa-refresh fa-fw fa-lg"></i> Change supervisor?</button>
+          </div>
+          {% elif user.profile.status == 'R' %}
+                {% if user.profile.supervision_key == None  %}
+                <li class="list-group-item list-group-item-danger" style="text-align:center;">Status is rejected: You need to find a new supervisor</li>
+                <br>
+                <h4>General Information</h4>
+                <ul class="list-group">
+                  <li class="list-group-item"><b>You need to find a new supervisor to continue using your account</b></li>
+                  <li class="list-group-item"><b>Deadline account blockage date (if no supervisor):</b> {{ user.profile.rejection_date }}</li>
+                  <li class="list-group-item"><i>If your account gets blocked you can still follow the account reactivation procedure to re-activate it.</i></li>
+                </ul>
+                <div class="form-group" style="text-align: center;">
+                <input type="hidden" name="token" value="true">
+                  <button type="button" class="btn btn-danger" onclick="beat.accounts.dialogs.modal_add_supervisor('change_supervisor', '{% url 'api_accounts:add_supervisor' 'nogodfather' %}', '{% url 'accounts:settings' %}');"><i class="fa fa-plus fa-fw fa-lg"></i> Add a supervisor</button>
+                </div>
+                {% else %}
+                <li class="list-group-item list-group-item-warning" style="text-align:center;">Status is pending: You need to wait approval from supervisor</li>
+                <br>
+                <h4>General Information about a pending request</h4>
+                <ul class="list-group">
+                  <li class="list-group-item"><b>Pending request made to supervisor:</b> {{ supervisiontrack.godfather.username }}</li>
+                  <li class="list-group-item"><b>Deadline account blockage date (if no supervisor):</b> {{ user.profile.rejection_date }}</li>
+                  <li class="list-group-item"><i>You need to be patient until this supervisor accepts your request. An email was sent about your request, but in the meantime you can remind him to accept your supervision.<br>
+                  If this supervisor rejects your supervision request, you will be informed and you will get the opportunity to make a new one.<br>
+                  If your account gets blocked you can still follow the account reactivation procedure to re-activate it.</i></li>
+                </ul>
+                {% endif %}
+          <br>
+          {% elif user.profile.status == 'Y' %}
+          <li class="list-group-item list-group-item-warning" style="text-align:center;">Revalidation required: You need to confirm that you wish to revalidate your account (year basis check)</li>
+          <br>
+          <h4>General Information</h4>
+          <ul class="list-group">
+            <li class="list-group-item"><b>Supervisor:</b> {{ supervisiontrack.godfather.username }}</li>
+            <li class="list-group-item"><b>Supervision Track started on:</b> {{ supervisiontrack.start_date }}</li>
+            <li class="list-group-item"><b>Supervision Track last validation date:</b> {{ supervisiontrack.last_validation_date }}</li>
+            <li class="list-group-item"><b>Supervision Track expiration date:</b> {{ supervisiontrack.expiration_date }}</li>
+          </ul>
+          <div class="form-group" style="text-align: center;">
+          <input type="hidden" name="token" value="true">
+            <button type="button" class="btn btn-warning" onclick="beat.accounts.dialogs.modal_renew_account('account_renew', '{% url 'api_accounts:revalidate_account' %}', '{% url 'accounts:settings' %}');"><i class="fa fa-refresh fa-fw fa-lg"></i> Revalidate account for 12 months</button>
+          </div>
+          {% endif %}
+          </div>
+        </form>
+      </div>
+
+    </div>
+
+  </div>
+</div>
+
+{% else %}
+{% if supervisiontracks_pending|length > 0 or supervisiontracks_valid|length > 0 %}
+<div class="row">
+
+  <div class="col-sm-6 col-sm-offset-1">
+
+    <div class="panel panel-default">
+      <div class="panel-heading"><i class="fa fa-users"></i> Supervision management
+      </div>
+      <div class="panel-body">
+        <form id="token" method="post" action="" class="form">
+          {% csrf_token %}
+          <div class="form-group" style="text-align: justify;">
+          {% if supervisiontracks_pending|length > 0 %}
+          <li class="list-group-item list-group-item-danger" style="text-align:center;">Pending requests: You have a few days to accept or reject these supervision requests</li>
+          <br>
+          <table class="table">
+            <thead>
+              <tr>
+                <th>Firstname</th>
+                <th>Lastname</th>
+                <th>Username</th>
+                <th>Email</th>
+                <th>Actions</th>
+              </tr>
+            </thead>
+            <tbody>
+            {% for supervisiontrack_pending in supervisiontracks_pending%}
+              <tr class="danger">
+                <td>{{supervisiontrack_pending.supervisee.first_name}}</td>
+                <td>{{supervisiontrack_pending.supervisee.last_name}}</td>
+                <td>{{supervisiontrack_pending.supervisee.username}}</td>
+                <td>{{supervisiontrack_pending.supervisee.email}}</td>
+                <td>
+
+                    <button type="button" class="btn btn-success btn-supervisee" onclick="beat.accounts.dialogs.modal_validate_supervisee('validate_supervisee', '{% url 'api_accounts:validate_supervisee' supervisiontrack_pending.supervisee.username %}', '{% url 'accounts:settings' %}');"> Accept</button>
+                    <button type="button" class="btn btn-danger btn-supervisee" onclick="beat.accounts.dialogs.modal_remove_supervisee('remove_supervisee', '{% url 'api_accounts:remove_supervisee' supervisiontrack_pending.supervisee.username %}', '{% url 'accounts:settings' %}');"> Reject</button>
+                </td>
+              </tr>
+             {% endfor %}
+            </tbody>
+          </table>
+          <hr />
+          {% endif %}
+
+          {% if supervisiontracks_valid|length > 0 %}
+          <li class="list-group-item" style="text-align:center;"><b>Supervision list</b></li>
+          <table class="table">
+            <thead>
+              <tr>
+                <th>Firstname</th>
+                <th>Lastname</th>
+                <th>Username</th>
+                <th>Email</th>
+                <th>Start Date</th>
+                <th>Expiration Date</th>
+                <th>Actions</th>
+              </tr>
+            </thead>
+            <tbody>
+            {% for supervisiontrack_valid in supervisiontracks_valid%}
+              {% if supervisiontrack_valid.supervisee.profile.status == 'A' %}
+              <tr class="success">
+              {% else %}
+              <tr class="warning">
+              {% endif %}
+                <td>{{supervisiontrack_valid.supervisee.first_name}}</td>
+                <td>{{supervisiontrack_valid.supervisee.last_name}}</td>
+                <td>{{supervisiontrack_valid.supervisee.username}}</td>
+                <td>{{supervisiontrack_valid.supervisee.email}}</td>
+                <td>{{supervisiontrack_valid.start_date}}</td>
+                <td>{{supervisiontrack_valid.expiration_date}}</td>
+                <td>
+                    <button type="button" class="btn btn-danger btn-supervisee" onclick="beat.accounts.dialogs.modal_remove_supervisee('remove_supervisee', '{% url 'api_accounts:remove_supervisee' supervisiontrack_valid.supervisee.username %}', '{% url 'accounts:settings' %}');"> Reject</button>
+
+                </td>
+              </tr>
+             {% endfor %}
+            </tbody>
+          </table>
+
+          <li class="list-group-item"><i>Valid supervisees are marked in green and supervisees that are under a revalidation process (yearly basis) are marked in yellow.
+You can remind them to revalidate their account before the expiration date or else their account will get blocked (and need to pass through an account reactivation procedure)</i><br>
+         {% endif %}
+          </div>
+        </form>
+      </div>
+
+    </div>
+
+  </div>
+</div>
+
+{% endif %}
+{% endif %}
+
+{% change_supervisor "change_supervisor" %}
+{% account_renew "account_renew" %}
+{% validate_supervisee "validate_supervisee" %}
+{% remove_supervisee "remove_supervisee" %}
+
 <script type="text/javascript">
 jQuery(document).ready(function() {
   $('form#token').submit(function(event) {
diff --git a/beat/web/accounts/templatetags/__init__.py b/beat/web/accounts/templatetags/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/beat/web/accounts/templatetags/account_tags.py b/beat/web/accounts/templatetags/account_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..fbfe7d4921b8d44108829444988f669e436bca4a
--- /dev/null
+++ b/beat/web/accounts/templatetags/account_tags.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+
+import random
+
+from django import template
+from django.conf import settings
+
+register = template.Library()
+
+
+@register.inclusion_tag('accounts/dialogs/renew_account.html')
+def account_renew(id):
+    return { 'dialog_id': id,
+           }
+
+@register.inclusion_tag('accounts/dialogs/change_supervisor.html')
+def change_supervisor(id):
+    return { 'dialog_id': id,
+           }
+
+@register.inclusion_tag('accounts/dialogs/validate_supervisee.html')
+def validate_supervisee(id):
+    return { 'dialog_id': id,
+           }
+
+@register.inclusion_tag('accounts/dialogs/remove_supervisee.html')
+def remove_supervisee(id):
+    return { 'dialog_id': id,
+           }
diff --git a/beat/web/accounts/tests.py b/beat/web/accounts/tests.py
index 3d08ce2865cc3b14e5b4b0a366d198d64c01ce7c..81a61ce82a147cf52a3f11e0f887e7a68780de7b 100644
--- a/beat/web/accounts/tests.py
+++ b/beat/web/accounts/tests.py
@@ -3,7 +3,7 @@
 
 ###############################################################################
 #                                                                             #
-# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Copyright (c) 2017 Idiap Research Institute, http://www.idiap.ch/           #
 # Contact: beat.support@idiap.ch                                              #
 #                                                                             #
 # This file is part of the beat.web module of the BEAT platform.              #
@@ -25,6 +25,1087 @@
 #                                                                             #
 ###############################################################################
 
+from django.core.urlresolvers import reverse
 from django.test import TestCase
 
-# Create your tests here.
+
+from rest_framework import status
+from rest_framework.test import APITestCase
+
+from django.contrib.auth.models import User
+from django.conf import settings
+
+from .models import SupervisionTrack, Profile
+
+from datetime import datetime, timedelta
+import datetime
+import simplejson as json
+
+#----------------------------------------------------------
+
+
+class AccountTestCase(APITestCase):
+
+    def setUp(self):
+        self.tearDown()
+
+
+        now = datetime.datetime.now()
+        expiration_date_delta = datetime.timedelta(days=settings.ACCOUNT_BLOCKAGE_AFTER_FIRST_REJECTION_DAYS)
+
+        # Create the users
+        self.password = '1234'
+
+        self.firstgodfather = User.objects.create_user('firstgodfather', 'firstgodfather@test.org', self.password)
+        self.secondgodfather = User.objects.create_user('secondgodfather', 'secondgodfather@test.org', self.password)
+        self.invalidgodfather = User.objects.create_user('invalidgodfather', 'invalidgodfather@test.org', self.password)
+
+        self.accepteduser = User.objects.create_user('accepteduser', 'accepteduser@test.org', self.password)
+        self.rejecteduser = User.objects.create_user('rejecteduser', 'rejecteduser@test.org', self.password)
+        self.yearrevalidationuser = User.objects.create_user('yearrevalidationuser', 'yearrevalidationuser@test.org', self.password)
+        self.blockeduser = User.objects.create_user('blockeduser', 'blockeduser@test.org', self.password)
+
+        # Update the profiles
+        #godfathers
+        self.firstgodfather.profile.status = Profile.ACCEPTED
+        self.secondgodfather.profile.status = Profile.ACCEPTED
+        self.invalidgodfather.profile.status = Profile.BLOCKED
+        self.firstgodfather.profile.is_godfather = True
+        self.secondgodfather.profile.is_godfather = True
+        self.invalidgodfather.profile.is_godfather = True
+        self.invalidgodfather.is_active = False
+
+        self.firstgodfather.save()
+        self.secondgodfather.save()
+        self.invalidgodfather.save()
+        self.firstgodfather.profile.save()
+        self.secondgodfather.profile.save()
+        self.invalidgodfather.profile.save()
+
+        #users
+        self.accepteduser.profile.status = Profile.ACCEPTED
+        self.rejecteduser.profile.status = Profile.REJECTED
+        self.yearrevalidationuser.profile.status = Profile.YEARREVALIDATION
+        self.blockeduser.profile.status = Profile.BLOCKED
+
+        # Create the supervision tracks
+        #Accepted user
+        #Create and assign key
+        self.accepteduser.profile.supervision_key = self.accepteduser.profile._generate_current_supervision_key()
+        supervisiontrack = SupervisionTrack.objects.create(
+            supervisee = self.accepteduser,
+            godfather = self.firstgodfather,
+            is_valid = True,
+            start_date = now,
+            expiration_date = now + expiration_date_delta,
+            last_validation_date = now
+            )
+
+        #Assign key to supervision track
+        supervisiontrack.supervision_key = self.accepteduser.profile.supervision_key
+        supervisiontrack.save()
+        self.accepteduser.profile.supervision.add(supervisiontrack)
+        self.accepteduser.profile.save()
+        self.accepteduser.save()
+
+        #Rejected user
+        #No supervision key but a rejection date
+        self.rejecteduser.profile.supervision_key = None
+        self.rejecteduser.profile.rejection_date = now + expiration_date_delta
+        self.rejecteduser.profile.save()
+        self.rejecteduser.save()
+
+        #YearRevalidation user
+        #Create and assign key
+        self.yearrevalidationuser.profile.supervision_key = self.yearrevalidationuser.profile._generate_current_supervision_key()
+        supervisiontrack = SupervisionTrack.objects.create(
+            supervisee = self.yearrevalidationuser,
+            godfather = self.firstgodfather,
+            is_valid = True,
+            start_date = now,
+            expiration_date = now + expiration_date_delta,
+            last_validation_date = now
+            )
+
+        #Assign key to supervision track
+        supervisiontrack.supervision_key = self.yearrevalidationuser.profile.supervision_key
+        supervisiontrack.save()
+        self.yearrevalidationuser.profile.supervision.add(supervisiontrack)
+        self.yearrevalidationuser.profile.save()
+        self.yearrevalidationuser.save()
+
+        #Blocked user
+        self.blockeduser.profile.supervision_key = None
+        self.blockeduser.profile.rejection_date = None
+        self.blockeduser.is_active = False
+        self.blockeduser.profile.save()
+        self.blockeduser.save()
+
+
+    def tearDown(self):
+        user = None
+        pass
+
+    def test_check_access_and_setup(self):
+
+        #Godfathers
+        user = self.firstgodfather
+        logged_in = self.client.login(username=user.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(user.username, 'firstgodfather')
+        logged_out = self.client.logout()
+
+        user = self.secondgodfather
+        logged_in = self.client.login(username=user.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(user.username, 'secondgodfather')
+        logged_out = self.client.logout()
+
+        user = self.invalidgodfather
+        logged_in = self.client.login(username=user.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(user.username, 'invalidgodfather')
+        logged_out = self.client.logout()
+
+        #Users
+        user = self.accepteduser
+        logged_in = self.client.login(username=user.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(user.username, 'accepteduser')
+        logged_out = self.client.logout()
+
+        user = self.rejecteduser
+        logged_in = self.client.login(username=user.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(user.username, 'rejecteduser')
+        logged_out = self.client.logout()
+
+        user = self.yearrevalidationuser
+        logged_in = self.client.login(username=user.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(user.username, 'yearrevalidationuser')
+        logged_out = self.client.logout()
+
+        user = self.blockeduser
+        logged_in = self.client.login(username=user.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(user.username, 'blockeduser')
+        logged_out = self.client.logout()
+
+
+#----------------------------------------------------------
+
+
+class AccountListTestCase(AccountTestCase):
+
+    def setUp(self):
+        super(AccountListTestCase, self).setUp()
+
+        self.url = reverse('api_accounts:list_supervisee')
+
+    def test_anonymous_user(self):
+        response = self.client.get(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+    def test_logged_in_user_not_godfather(self):
+
+        client = self.accepteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'accepteduser')
+
+        response = self.client.get(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Not a supervisor account')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_user_godfather(self):
+
+        client = self.firstgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'firstgodfather')
+
+        response = self.client.get(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 2)
+        self.assertEqual(content[0]['supervisee']['username'], 'accepteduser')
+        self.assertEqual(content[1]['supervisee']['username'], 'yearrevalidationuser')
+
+        logged_out = self.client.logout()
+
+
+#----------------------------------------------------------
+
+
+class AccountRevalidationTestCase(AccountTestCase):
+
+    def setUp(self):
+        super(AccountRevalidationTestCase, self).setUp()
+
+        self.url = reverse('api_accounts:revalidate_account')
+
+    def test_anonymous_user(self):
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+    def test_logged_in_accepted_user(self):
+        client = self.accepteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'accepteduser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['error']
+        self.assertEqual(errormsg, "You don't need to revalidate at the moment, your supervision is still valid")
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_rejected_user(self):
+        client = self.rejecteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'rejecteduser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['error']
+        self.assertEqual(errormsg, 'You are not allowed to perform this action, you first need to get a valid supervision')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_yearrevalidation_user(self):
+
+        client = self.yearrevalidationuser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'yearrevalidationuser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_yearrevalidation_user_twice_validation(self):
+
+        client = self.yearrevalidationuser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'yearrevalidationuser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['error']
+        self.assertEqual(errormsg, "You don't need to revalidate at the moment, your supervision is still valid")
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_blocked_user(self):
+
+        client = self.blockeduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(client.username, 'blockeduser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_user_godfather(self):
+
+        client = self.firstgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'firstgodfather')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Not a supervisee account')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_invalid_godfather(self):
+
+        client = self.invalidgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(client.username, 'invalidgodfather')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+        logged_out = self.client.logout()
+
+
+#----------------------------------------------------------
+
+
+class AccountAddSupervisorTestCase(AccountTestCase):
+
+    def setUp(self):
+        super(AccountAddSupervisorTestCase, self).setUp()
+
+        self.url = reverse('api_accounts:add_supervisor', kwargs={'supervisor_name': self.secondgodfather.username})
+
+    def test_anonymous_user(self):
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+    def test_logged_in_accepted_user_supervision_request_to_invalid_godfather(self):
+        client = self.accepteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'accepteduser')
+
+        previous_supervision_key = client.profile.supervision_key
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+
+        self.assertEqual(previous_supervision_track.is_valid, True)
+
+        self.url = reverse('api_accounts:add_supervisor', kwargs={'supervisor_name': self.invalidgodfather.username})
+        #change supervisor
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['error']
+        self.assertEqual(errormsg, 'Not a valid supervisor request')
+
+        client = User.objects.get(username=client.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+        self.assertEqual(previous_supervision_track, new_supervision_track)
+        self.assertEqual(previous_supervision_track.is_valid, True)
+        self.assertEqual(previous_supervision_key, new_supervision_key)
+        self.assertEqual(client.profile.status, Profile.ACCEPTED)
+        logged_out = self.client.logout()
+
+    def test_logged_in_accepted_user(self):
+
+        client = self.accepteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'accepteduser')
+
+        previous_supervision_key = client.profile.supervision_key
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+
+        self.assertEqual(previous_supervision_track.is_valid, True)
+
+        #change supervisor
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        client = User.objects.get(username=client.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+        self.assertEqual(previous_supervision_track.is_valid, False)
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertNotEqual(previous_supervision_track.godfather, new_supervision_track.godfather)
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+        logged_out = self.client.logout()
+
+    def test_logged_in_rejected_user(self):
+
+        client = self.rejecteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'rejecteduser')
+
+        previous_supervision_key = client.profile.supervision_key
+        self.assertEqual(previous_supervision_key, None)
+
+        #change supervisor
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        client = User.objects.get(username=client.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertEqual(new_supervision_track.godfather.username, "secondgodfather")
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+        logged_out = self.client.logout()
+
+    def test_logged_in_rejected_user_twice(self):
+
+        client = self.rejecteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'rejecteduser')
+
+        previous_supervision_key = client.profile.supervision_key
+        self.assertEqual(previous_supervision_key, None)
+
+        #change supervisor
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        client = User.objects.get(username=client.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertEqual(new_supervision_track.godfather.username, "secondgodfather")
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+        #change supervisor second request
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['error']
+        self.assertEqual(errormsg, "You are not able to perform this action as you already have a pending request")
+
+        client = User.objects.get(username=client.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertEqual(new_supervision_track.godfather.username, "secondgodfather")
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+        logged_out = self.client.logout()
+
+    def test_logged_in_yearrevalidation_user(self):
+
+        client = self.yearrevalidationuser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'yearrevalidationuser')
+
+        previous_supervision_key = client.profile.supervision_key
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+
+        self.assertEqual(previous_supervision_track.is_valid, True)
+
+        #change supervisor
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        client = User.objects.get(username=client.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+        self.assertEqual(previous_supervision_track.is_valid, False)
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertNotEqual(previous_supervision_track.godfather, new_supervision_track.godfather)
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_blocked_user(self):
+
+        client = self.blockeduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(client.username, 'blockeduser')
+
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+        #This reactivation can be done through online request ONLY
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_user_godfather(self):
+
+        client = self.firstgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'firstgodfather')
+
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Not a supervisee account')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_invalid_godfather(self):
+
+        client = self.invalidgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(client.username, 'invalidgodfather')
+
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+        logged_out = self.client.logout()
+
+
+#----------------------------------------------------------
+
+
+class AccountValidateSuperviseeTestCase(AccountTestCase):
+
+    def setUp(self):
+        super(AccountValidateSuperviseeTestCase, self).setUp()
+
+        self.url = reverse('api_accounts:validate_supervisee', kwargs={'supervisee_name': self.accepteduser.username})
+
+    def test_anonymous_user(self):
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+    def test_logged_in_accepted_user(self):
+        client = self.accepteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'accepteduser')
+
+        previous_supervision_key = client.profile.supervision_key
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+
+        self.assertEqual(previous_supervision_track.is_valid, True)
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Not a supervisor account')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_rejected_user(self):
+        client = self.rejecteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'rejecteduser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Not a supervisor account')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_yearrevalidation_user(self):
+
+        client = self.yearrevalidationuser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'yearrevalidationuser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Not a supervisor account')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_blocked_user(self):
+
+        client = self.blockeduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(client.username, 'blockeduser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_user_godfather_validate_already_accepted_user(self):
+
+        client = self.firstgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'firstgodfather')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['error']
+        self.assertEqual(errormsg, "You can't validate this supervisee as his supervision is still valid")
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_user_secondgodfather_validates_previously_accepted_user(self):
+
+        self.url = reverse('api_accounts:add_supervisor', kwargs={'supervisor_name': self.secondgodfather.username})
+
+        client = self.accepteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'accepteduser')
+
+        previous_supervision_key = client.profile.supervision_key
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+
+        self.assertEqual(previous_supervision_track.is_valid, True)
+
+        #change supervisor
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        client = User.objects.get(username=client.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+        self.assertEqual(previous_supervision_track.is_valid, False)
+        self.assertEqual(new_supervision_track.is_valid, False)
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertNotEqual(previous_supervision_track.godfather, new_supervision_track.godfather)
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+        logged_out = self.client.logout()
+
+        self.url = reverse('api_accounts:validate_supervisee', kwargs={'supervisee_name': self.accepteduser.username})
+        client = self.secondgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'secondgodfather')
+
+        response = self.client.put(self.url, format='json')
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        logged_out = self.client.logout()
+
+        client = User.objects.get(username=self.accepteduser.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        self.assertEqual(previous_supervision_track.is_valid, False)
+        self.assertEqual(new_supervision_track.is_valid, True)
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertNotEqual(previous_supervision_track.godfather, new_supervision_track.godfather)
+        self.assertEqual(client.profile.status, Profile.ACCEPTED)
+
+
+    def test_logged_in_user_secondgodfather_validates_rejected_user(self):
+
+        self.url = reverse('api_accounts:add_supervisor', kwargs={'supervisor_name': self.secondgodfather.username})
+
+        client = self.rejecteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'rejecteduser')
+
+        previous_supervision_key = client.profile.supervision_key
+        self.assertEqual(previous_supervision_key, None)
+
+        #change supervisor
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        client = User.objects.get(username=client.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertEqual(new_supervision_track.godfather.username, "secondgodfather")
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+        logged_out = self.client.logout()
+
+        self.url = reverse('api_accounts:validate_supervisee', kwargs={'supervisee_name': self.rejecteduser.username})
+        client = self.secondgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'secondgodfather')
+
+        response = self.client.put(self.url, format='json')
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        logged_out = self.client.logout()
+
+        client = User.objects.get(username=self.rejecteduser.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        self.assertEqual(new_supervision_track.is_valid, True)
+        self.assertNotEqual(new_supervision_track.godfather, "secondgodfather")
+        self.assertEqual(client.profile.status, Profile.ACCEPTED)
+
+    def test_logged_in_user_secondgodfather_validates_yearrevalidation_user(self):
+
+        self.url = reverse('api_accounts:add_supervisor', kwargs={'supervisor_name': self.secondgodfather.username})
+
+        client = self.yearrevalidationuser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'yearrevalidationuser')
+
+        previous_supervision_key = client.profile.supervision_key
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+
+        self.assertEqual(previous_supervision_track.is_valid, True)
+
+        #change supervisor
+        response = self.client.post(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        client = User.objects.get(username=client.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+        self.assertEqual(previous_supervision_track.is_valid, False)
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertNotEqual(previous_supervision_track.godfather, new_supervision_track.godfather)
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+
+        logged_out = self.client.logout()
+
+        self.url = reverse('api_accounts:validate_supervisee', kwargs={'supervisee_name': self.yearrevalidationuser.username})
+        client = self.secondgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'secondgodfather')
+
+        response = self.client.put(self.url, format='json')
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        logged_out = self.client.logout()
+
+        client = User.objects.get(username=self.yearrevalidationuser.username)
+        new_supervision_key = client.profile.supervision_key
+        new_supervision_track = SupervisionTrack.objects.get(supervision_key = new_supervision_key)
+
+        #check track change and supervisor
+        self.assertEqual(previous_supervision_track.is_valid, False)
+        self.assertEqual(new_supervision_track.is_valid, True)
+        self.assertNotEqual(previous_supervision_key, new_supervision_key)
+        self.assertNotEqual(previous_supervision_track.godfather, new_supervision_track.godfather)
+        self.assertEqual(client.profile.status, Profile.ACCEPTED)
+
+    def test_logged_in_user_godfather_validation_try(self):
+
+        client = self.firstgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'firstgodfather')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['error']
+        self.assertEqual(errormsg, "You can't validate this supervisee as his supervision is still valid")
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_invalid_godfather(self):
+
+        client = self.invalidgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(client.username, 'invalidgodfather')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+        logged_out = self.client.logout()
+
+
+#----------------------------------------------------------
+
+
+class AccountRemoveSuperviseeTestCase(AccountTestCase):
+
+    def setUp(self):
+        super(AccountRemoveSuperviseeTestCase, self).setUp()
+
+        self.url = reverse('api_accounts:remove_supervisee', kwargs={'supervisee_name': self.accepteduser.username})
+
+    def test_anonymous_user(self):
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+    def test_logged_in_accepted_user(self):
+        client = self.accepteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'accepteduser')
+
+        previous_supervision_key = client.profile.supervision_key
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+
+        self.assertEqual(previous_supervision_track.is_valid, True)
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Not a supervisor account')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_rejected_user(self):
+        client = self.rejecteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'rejecteduser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Not a supervisor account')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_yearrevalidation_user(self):
+
+        client = self.yearrevalidationuser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'yearrevalidationuser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Not a supervisor account')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_blocked_user(self):
+
+        client = self.blockeduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(client.username, 'blockeduser')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+        logged_out = self.client.logout()
+
+    def test_logged_in_godfather_remove_accepted_user(self):
+        #accepted user before
+        client = self.accepteduser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'accepteduser')
+
+        previous_supervision_key = client.profile.supervision_key
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+
+        self.assertEqual(previous_supervision_track.is_valid, True)
+        self.assertEqual(client.profile.status, Profile.ACCEPTED)
+        logged_out = self.client.logout()
+
+        #accepted user removed
+        client = self.firstgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'firstgodfather')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        logged_out = self.client.logout()
+
+        #accepted user after
+        client = User.objects.get(username=self.accepteduser.username)
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'accepteduser')
+
+        new_supervision_key = client.profile.supervision_key
+        self.assertEqual(new_supervision_key, None)
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+        self.assertEqual(previous_supervision_track.is_valid, False)
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+        logged_out = self.client.logout()
+
+    def test_logged_in_godfather_remove_yearrevalidation_user(self):
+        self.url = reverse('api_accounts:remove_supervisee', kwargs={'supervisee_name': self.yearrevalidationuser.username})
+
+        #yearrevalidationuser user before
+        client = self.yearrevalidationuser
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'yearrevalidationuser')
+
+        previous_supervision_key = client.profile.supervision_key
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+
+        self.assertEqual(previous_supervision_track.is_valid, True)
+        self.assertEqual(client.profile.status, Profile.YEARREVALIDATION)
+        logged_out = self.client.logout()
+
+        #yearrevalidationuser user removed
+        client = self.firstgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'firstgodfather')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        logged_out = self.client.logout()
+
+        #yearrevalidationuser user after
+        client = User.objects.get(username=self.yearrevalidationuser.username)
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertTrue(logged_in)
+        self.assertEqual(client.username, 'yearrevalidationuser')
+
+        new_supervision_key = client.profile.supervision_key
+        self.assertEqual(new_supervision_key, None)
+        previous_supervision_track = SupervisionTrack.objects.get(supervision_key = previous_supervision_key)
+        self.assertEqual(previous_supervision_track.is_valid, False)
+        self.assertEqual(client.profile.status, Profile.REJECTED)
+        logged_out = self.client.logout()
+
+    def test_logged_in_invalid_godfather(self):
+
+        client = self.invalidgodfather
+
+        logged_in = self.client.login(username=client.username, password=self.password)
+        self.assertFalse(logged_in)
+        self.assertEqual(client.username, 'invalidgodfather')
+
+        response = self.client.put(self.url, format='json')
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+        content = json.loads(response.content)
+        self.assertEqual(len(json.loads(response.content)), 1)
+        errormsg = content['detail']
+        self.assertEqual(errormsg, 'Authentication credentials were not provided.')
+
+        logged_out = self.client.logout()
diff --git a/beat/web/accounts/views.py b/beat/web/accounts/views.py
index 4e4917de05c589a049e80206cf27a07590b7535d..823516c60c547728c2c9f5d710e9cfeb55ff0539 100644
--- a/beat/web/accounts/views.py
+++ b/beat/web/accounts/views.py
@@ -29,11 +29,14 @@ from django.shortcuts import render
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.forms import PasswordChangeForm
 from django.contrib import messages
+from django.db import models
+from django.db.models import Q
 
 from rest_framework.authtoken.models import Token
 
 from .forms import AccountSettingsForm
 from .models import AccountSettings
+from .models import SupervisionTrack, Profile
 
 @login_required
 def account_settings(request):
@@ -65,8 +68,28 @@ def account_settings(request):
         account_settings_form = AccountSettingsForm(instance=account_settings)
         password_change_form = PasswordChangeForm(user=user)
 
+    supervisiontrack = None
+    supervisiontracks_valid = None
+    supervisiontracks_pending = None
+    if user.profile.is_godfather == False:
+        supervisee = user
+        if supervisee.profile.supervision_key is not None:
+            #There's a key check if there's a valid track
+            supervisiontrack = SupervisionTrack.objects.get(supervisee=supervisee, supervisee__profile__supervision_key=models.F('supervision_key'))
+        else:
+            supervisiontrack = None
+    else:
+        godfather = user
+        #Filter and get all supervision tracks valid and
+        supervisiontracks_valid = SupervisionTrack.objects.filter(godfather=godfather, is_valid=True)
+        supervisiontracks_pending = SupervisionTrack.objects.filter(godfather=godfather, is_valid=False, start_date=None)
+
     return render(request,
                   'accounts/settings.html',
                   {'account_settings_form': account_settings_form,
                    'password_change_form': password_change_form,
+                   'user': user,
+                   'supervisiontrack': supervisiontrack,
+                   'supervisiontracks_valid': supervisiontracks_valid,
+                   'supervisiontracks_pending': supervisiontracks_pending,
                    'token' : user.auth_token})
diff --git a/beat/web/navigation/admin.py b/beat/web/navigation/admin.py
index 924827c59d2a52f2ec274662e9a9ca94eb5e3e9b..85d82d7ca1322e2ac4b2d0e3ae4f1daec1c96d8a 100644
--- a/beat/web/navigation/admin.py
+++ b/beat/web/navigation/admin.py
@@ -31,6 +31,8 @@ from django.contrib.auth.models import User
 
 from .models import Agreement
 from ..accounts.models import AccountSettings
+from ..accounts.models import SupervisionTrack
+from ..accounts.models import Profile
 
 
 #----------------------------------------------------------
@@ -50,6 +52,21 @@ class AccountSettingsInline(admin.StackedInline):
 #----------------------------------------------------------
 
 
+class SupervisionTrackInline(admin.StackedInline):
+    model = SupervisionTrack
+    fk_name = 'godfather'
+
+
+#----------------------------------------------------------
+
+
+class ProfileInline(admin.StackedInline):
+    model = Profile
+
+
+#----------------------------------------------------------
+
+
 class UserAdmin(UserAdmin):
 
     def agreement_number(self, obj):
@@ -62,6 +79,30 @@ class UserAdmin(UserAdmin):
             int(obj.accountsettings.database_notifications_enabled) + \
             int(obj.accountsettings.environment_notifications_enabled)
 
+    def godfather(self, obj):
+        return obj.profile.is_godfather
+
+    def status(self, obj):
+        return obj.profile.status
+
+    def supervision(self, obj):
+        if obj.is_staff:
+            return "Staff account"
+        if obj.profile.is_godfather:
+            return "Supervisor account"
+        else:
+            supervisiontrack = SupervisionTrack.objects.get(supervisee=obj,
+                is_valid=True)
+            godfather = supervisiontrack.godfather
+            return godfather
+
+    def supervision_key(self, obj):
+        return obj.profile.supervision_key
+
+    def rejection_date(self, obj):
+        return obj.profile.rejection_date
+
+
     list_display = (
         'username',
         'is_staff',
@@ -69,6 +110,11 @@ class UserAdmin(UserAdmin):
         'notifications',
         'agreement_number',
         'last_login',
+        'godfather',
+        'status',
+        'supervision',
+        'supervision_key',
+        'rejection_date',
     )
 
     ordering = (
@@ -80,7 +126,54 @@ class UserAdmin(UserAdmin):
     inlines = (
         AgreementInline,
         AccountSettingsInline,
+        SupervisionTrackInline,
+        ProfileInline,
     )
 
 admin.site.unregister(User)
 admin.site.register(User, UserAdmin)
+
+
+#----------------------------------------------------------
+
+
+class SupervisionTrackAdmin(admin.ModelAdmin):
+
+
+    list_display = (
+        'godfather',
+        'supervisee',
+        'is_valid',
+        'start_date',
+        'expiration_date',
+        'last_validation_date',
+        'supervision_key',
+    )
+
+    ordering = (
+        'godfather',
+        )
+
+admin.site.register(SupervisionTrack, SupervisionTrackAdmin)
+
+
+#----------------------------------------------------------
+
+
+class ProfileAdmin(admin.ModelAdmin):
+
+
+    list_display = (
+        'user',
+        'status',
+        'registration_date',
+        'is_godfather',
+        'supervision_key',
+        'rejection_date',
+    )
+
+    ordering = (
+        'user',
+        )
+
+admin.site.register(Profile, ProfileAdmin)
diff --git a/beat/web/settings/settings.py b/beat/web/settings/settings.py
index 17a7b1d25b1d807ac7ea186406fd9b2e03126c96..61ec9923e3be878a19d47649852d11bc6190fe0d 100755
--- a/beat/web/settings/settings.py
+++ b/beat/web/settings/settings.py
@@ -216,6 +216,10 @@ DATASETS_ROOT_PATH = None
 ##############################################################################
 
 ACCOUNT_ACTIVATION_DAYS  = 2
+ACCOUNT_ACTIVATION_DAYS_FROM_GODFATHER  = 7
+ACCOUNT_EXPIRATION_DAYS  = 365
+ACCOUNT_BLOCKAGE_AFTER_FIRST_REJECTION_DAYS  = 56
+
 LOGIN_REDIRECT_URL       = '/'
 LOGIN_URL                = '/login/'
 SYSTEM_ACCOUNT           = 'system'
@@ -429,6 +433,8 @@ AUTHENTICATION_BACKENDS = (
 
 ANONYMOUS_USER_ID = -1
 
+AUTH_PROFILE_MODULE = 'accounts.Profile'
+
 ##############################################################################
 #
 # REST FRAMEWORK PERMISSION CLASSES: http://www.django-rest-framework.org/api-guide/permissions/
@@ -465,6 +471,7 @@ REST_FRAMEWORK = {
 #In days
 EXPIRATION_DELTA = 180
 EXPIRATION_REMINDERS = [1, 7, 30]
+EXPIRATION_REMINDERS_REVALIDATION = [1, 7, 30, 50]
 
 ##############################################################################
 #
diff --git a/beat/web/statistics/templates/statistics/statistics.html b/beat/web/statistics/templates/statistics/statistics.html
index ebd752a7164dd0d46532e4fa5b417b1130f1d95f..14431d83d3e7ceceb5c84f0164fbfeeda278fc19 100644
--- a/beat/web/statistics/templates/statistics/statistics.html
+++ b/beat/web/statistics/templates/statistics/statistics.html
@@ -46,7 +46,7 @@
 
 <!-- summary statistics -->
 <div class="row">
-  <div class="col-sm-3">
+  <div class="col-sm-offset-1 col-sm-2">
     <h3>Totals <span class="badge">{{ totals.counter }} entries</span></h3>
     <ul class="list-group">
       <li class="list-group-item">CPU Time <span class="badge">{{ totals.cpu_time_hours }} h</span></li>
@@ -58,7 +58,7 @@
     </ul>
     <p class="help">Instantaneous (not averaged over wallclock time)</p>
   </div>
-  <div class="col-sm-3">
+  <div class="col-sm-2">
     <h3>User Objects</h3>
     <ul class="list-group">
       <li class="list-group-item">Experiments <span class="badge">{{ totals.experiments }}</span></li>
@@ -70,7 +70,7 @@
     </ul>
     <p class="help">Items contributed by users</p>
   </div>
-  <div class="col-sm-3">
+  <div class="col-sm-2">
     <h3>User Queries</h3>
     <ul class="list-group">
       <li class="list-group-item">Attestations <span class="badge">{{ totals.attestations }}</span></li>
@@ -79,7 +79,7 @@
     </ul>
     <p class="help">User analysis and certification</p>
   </div>
-  <div class="col-sm-3">
+  <div class="col-sm-2">
     <h3>System Properties</h3>
     <ul class="list-group">
       <li class="list-group-item">Users <span class="badge">{{ totals.users }}</span></li>
@@ -90,9 +90,21 @@
     </ul>
     <p class="help">Items available and managed by administrators</p>
   </div>
+  <div class="col-sm-2">
+    <h3>User Profiles</h3>
+    <ul class="list-group">
+      <li class="list-group-item">Total <span class="badge">{{ totals.users }}</span></li>
+      <li class="list-group-item">Awaiting Activation <span class="badge">{{ totals.newusers }}</span></li>
+      <li class="list-group-item">Awaiting Supervisor Validation <span class="badge">{{ totals.waitingvalidationusers}}</span></li>
+      <li class="list-group-item">Active <span class="badge">{{ totals.acceptedusers }}</span></li>
+      <li class="list-group-item">Year Revalidation <span class="badge">{{ totals.yearrevalidationusers }}</span></li>
+      <li class="list-group-item">Without Supervisor <span class="badge">{{ totals.rejectedusers }}</span></li>
+      <li class="list-group-item">Inactive <span class="badge">{{ totals.blockedusers }}</span></li>
+    </ul>
+    <p class="help">Profile status of users on the platform</p>
+  </div>
 </div>
 
-
 <!-- tabs -->
 <div class="row">
   <div class="col-sm-12">
diff --git a/beat/web/statistics/views.py b/beat/web/statistics/views.py
index d13a7e073a0087ec5d5c852117f528288bc2dbcb..2b1664ffc68c8a9ee7d3f9ed14b344f25953a28b 100644
--- a/beat/web/statistics/views.py
+++ b/beat/web/statistics/views.py
@@ -56,6 +56,7 @@ def calculate_totals():
     from ..plotters.models import Plotter
     from ..plotters.models import PlotterParameter
     from ..search.models import Search
+    from ..accounts.models import Profile
 
     # for calculating the total cpu time, we use the HourlyStatistics and
     # accumulate over the whole history
@@ -78,6 +79,13 @@ def calculate_totals():
     output_size = details['data_written_size']
     output_time = details['data_written_time']
 
+    new_users = User.objects.filter(profile__status = Profile.NEWUSER)
+    waiting_validation_users = User.objects.filter(profile__status = Profile.WAITINGVALIDATION)
+    accepted_users = User.objects.filter(profile__status = Profile.ACCEPTED)
+    rejected_users = User.objects.filter(profile__status = Profile.REJECTED)
+    yearrevalidation_users = User.objects.filter(profile__status = Profile.YEARREVALIDATION)
+    blocked_users = User.objects.filter(profile__status = Profile.BLOCKED)
+
     return dict(
 
             counter=counter,
@@ -89,6 +97,12 @@ def calculate_totals():
             output_bw_mb_s = int((output_size/float(2**20))/output_time) if output_size else 0,
 
             users=User.objects.count(),
+            newusers=new_users.count(),
+            waitingvalidationusers=waiting_validation_users.count(),
+            acceptedusers=accepted_users.count(),
+            rejectedusers=rejected_users.count(),
+            yearrevalidationusers=yearrevalidation_users.count(),
+            blockedusers=blocked_users.count(),
             databases=Database.objects.count(),
             environments=Environment.objects.count(),
             queues=Queue.objects.count(),
diff --git a/beat/web/ui/registration/docs/forms.txt b/beat/web/ui/registration/docs/forms.txt
index e779d433ebcb76a3e27a219ec90eaae61e252d59..5435413ae83c17ae9a65c4657708fb5c267db8f6 100644
--- a/beat/web/ui/registration/docs/forms.txt
+++ b/beat/web/ui/registration/docs/forms.txt
@@ -23,7 +23,7 @@ requires the password to be entered twice to catch typos.
 Subclasses should feel free to add any additional validation they
 need, but should either preserve the base ``save()`` or implement a
 ``save()`` method which returns a ``User``.
-    
+
 Fields:
 
 ``username``
@@ -40,6 +40,10 @@ Fields:
 ``password2``
     The password, again, to catch typos.
 
+``godfather``
+    The godfather that will validate this account.
+
+
 
 Non-validation methods:
 
@@ -71,6 +75,6 @@ implementations of useful customizations:
     Subclass of ``RegistrationForm`` which disallows registration with
     email addresses from popular free webmail services; moderately
     useful for preventing automated spam registrations.
-    
+
     To change the list of banned domains, subclass this form and
     override the attribute ``bad_domains``.
diff --git a/beat/web/ui/registration/forms.py b/beat/web/ui/registration/forms.py
index b41f5b997be29376f31a78fc9ed0339c6966eaac..355dcc2d01d9d1013285ef66ff435348380186b8 100644
--- a/beat/web/ui/registration/forms.py
+++ b/beat/web/ui/registration/forms.py
@@ -30,6 +30,7 @@ Forms and validation code for user registration.
 
 """
 
+import datetime
 
 from django.contrib.auth.models import User
 from django import forms
@@ -39,6 +40,8 @@ from django.utils.translation import ugettext_lazy as _
 from .models import RegistrationProfile
 from .models import PreregistrationProfile
 
+from ...accounts.models import SupervisionTrack
+from ...accounts.models import Profile
 
 # I put this on all required fields, because it's easier to pick up
 # on them with CSS or JavaScript if they have a class of "required"
@@ -79,6 +82,11 @@ class RegistrationForm(forms.Form):
                                 label=_(u'Password'))
     password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
                                 label=_(u'Password (again)'))
+    godfather = forms.RegexField(regex=r'^\w+$',
+                                max_length=30,
+                                widget=forms.TextInput(attrs=attrs_dict),
+                                label=_(u'Supervisor Username'))
+
 
     def clean_username(self):
         """
@@ -92,6 +100,24 @@ class RegistrationForm(forms.Form):
             return self.cleaned_data['username']
         raise forms.ValidationError(_(u'This username is already taken. Please choose another.'))
 
+    def clean_godfather(self):
+        """
+        Validate that the username is alphanumeric and exists.
+
+        """
+        try:
+            user = User.objects.get(username__iexact=self.cleaned_data['godfather'])
+            if user.profile.status == Profile.BLOCKED:
+                raise forms.ValidationError(_(u'This user is not a valid supervisor. Please choose another.'))
+        except User.DoesNotExist:
+            raise forms.ValidationError(_(u'This supervisor username does not exist. Please choose another.'))
+
+        if not user.profile.is_godfather:
+            raise forms.ValidationError(_(u'This user is not a recognized supervisor. Please choose another.'))
+
+        return self.cleaned_data['godfather']
+
+
     def clean(self):
         """
         Verifiy that the values entered into the two password fields
@@ -123,6 +149,26 @@ class RegistrationForm(forms.Form):
                 password=self.cleaned_data['password1'],
                 email=self.cleaned_data['email'],
                 )
+
+        #Create and assign key
+        new_user.profile.supervision_key = new_user.profile._generate_current_supervision_key()
+        godfather = User.objects.get(username = self.cleaned_data['godfather'])
+        supervisiontrack = SupervisionTrack.objects.create(
+            supervisee = new_user,
+            godfather = godfather,
+            is_valid = False,
+            )
+
+        #Assign key to supervision track
+        supervisiontrack.supervision_key = new_user.profile.supervision_key
+        supervisiontrack.save()
+        new_user.profile.is_godfather = False
+        new_user.profile.status = Profile.NEWUSER
+        new_user.profile.registration_date = datetime.datetime.now()
+        new_user.profile.supervision.add(supervisiontrack)
+        new_user.save()
+
+
         return new_user
 
 
@@ -206,3 +252,72 @@ class RegistrationFormNoFreeEmail(RegistrationForm):
         if email_domain in self.bad_domains:
             raise forms.ValidationError(_(u'Registration using free email addresses is prohibited. Please supply a different email address.'))
         return self.cleaned_data['email']
+
+
+class BlockedUserRevalidationForm(forms.Form):
+    """
+    Form for registering a new user account.
+
+    Validates that the requested username is not already in use, and
+    requires the password to be entered twice to catch typos.
+
+    Subclasses should feel free to add any additional validation they
+    need, but should either preserve the base ``save()`` or implement
+    a ``save()`` method which returns a ``User``.
+
+    """
+
+    username = forms.RegexField(regex=r'^\w+$',
+                                max_length=30,
+                                widget=forms.TextInput(attrs=attrs_dict),
+                                label=_(u'Username'))
+    password = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
+                                label=_(u'Password'))
+    godfather = forms.RegexField(regex=r'^\w+$',
+                                max_length=30,
+                                widget=forms.TextInput(attrs=attrs_dict),
+                                label=_(u'Supervisor Username (Your account needs to be re-validated by a recognized person)'))
+
+
+    def clean_username(self):
+        """
+        Validate that the username is alphanumeric and user exists
+
+        """
+        try:
+            user = User.objects.get(username__iexact=self.cleaned_data['username'])
+            return self.cleaned_data['username']
+        except User.DoesNotExist:
+            raise forms.ValidationError(_(u'This username has not been recognized'))
+
+    def clean_godfather(self):
+        """
+        Validate that the username is alphanumeric and exists.
+
+        """
+        try:
+            user = User.objects.get(username__iexact=self.cleaned_data['godfather'])
+        except User.DoesNotExist:
+            raise forms.ValidationError(_(u'This supervisor username does not exist. Please choose another.'))
+
+        if not user.profile.is_godfather:
+            raise forms.ValidationError(_(u'This user is not a recognized supervisor. Please choose another.'))
+
+        return self.cleaned_data['godfather']
+
+
+    def clean(self):
+        """
+        Verifiy that the values entered into the two password fields
+        match. Note that an error here will end up in
+        ``non_field_errors()`` because it doesn't apply to a single
+        field.
+
+        """
+        if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
+            if self.cleaned_data['password1'] != self.cleaned_data['password2']:
+                self._errors["password2"] = self.error_class([_(u'You must type the same password each time')])
+                del self.cleaned_data["password1"]
+                del self.cleaned_data["password2"]
+
+        return self.cleaned_data
diff --git a/beat/web/ui/registration/models.py b/beat/web/ui/registration/models.py
index 546669521a234c86ac4f1ec1672fd0a0aa2dbe24..405c31daf6734d0b7fea08c3103962ff1113d666 100644
--- a/beat/web/ui/registration/models.py
+++ b/beat/web/ui/registration/models.py
@@ -39,6 +39,9 @@ from django.template import loader
 from django.template import Context
 from django.utils.translation import ugettext_lazy as _
 
+from ...accounts.models import SupervisionTrack
+from ...accounts.models import Profile
+
 SHA1_RE = re.compile('^[a-f0-9]{40}$')
 
 
@@ -93,6 +96,43 @@ class RegistrationManager(models.Manager):
                 profile.activation_key = self.model.ACTIVATED
                 profile.save()
                 user_activated.send(sender=self.model, user=user)
+                # The user activated via the link but activation from godfather or admin
+                # is requested now to have access to the platform (so set it
+                # inactive again)
+                user.is_active = False
+                user.profile.status = Profile.WAITINGVALIDATION
+                user.profile.registration_date = datetime.datetime.now()
+                user.save()
+
+                # Send email to godfather
+                # Fetch Godfather user from supervision track
+                supervisiontrack = SupervisionTrack.objects.get(supervision_key=user.profile.supervision_key)
+                godfather_user = supervisiontrack.godfather
+
+                from django.core.mail import send_mail
+
+                parsed_url = urlparse(settings.URL_PREFIX)
+                server_address = '%s://%s' % (parsed_url.scheme, parsed_url.hostname)
+
+                c = Context({ 'supervisor': godfather_user,
+                              'supervisee': user,
+                              'prefix': server_address,
+                            })
+
+                try:
+                    t = loader.get_template('registration/mail.godfather_validation.subject.txt')
+                    subject = t.render(c)
+
+                    # Note: e-mail subject *must not* contain newlines
+                    subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+                    t = loader.get_template('registration/mail.godfather_validation.message.txt')
+                    message = t.render(c)
+
+                    send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [godfather_user.email])
+                except:
+                    pass
+
                 return user
         return False
 
diff --git a/beat/web/ui/registration/templates/registration/activate.html b/beat/web/ui/registration/templates/registration/activate.html
index 54c1d444cab38651557336e686e0b3de16bc6b72..d434cebb12d5ed98d0ecb6d1c676370953873a4a 100644
--- a/beat/web/ui/registration/templates/registration/activate.html
+++ b/beat/web/ui/registration/templates/registration/activate.html
@@ -2,21 +2,21 @@
 {% comment %}
  * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
  * Contact: beat.support@idiap.ch
- * 
+ *
  * This file is part of the beat.web module of the BEAT platform.
- * 
+ *
  * Commercial License Usage
  * Licensees holding valid commercial BEAT licenses may use this file in
  * accordance with the terms contained in a written agreement between you
  * and Idiap. For further information contact tto@idiap.ch
- * 
+ *
  * Alternatively, this file may be used under the terms of the GNU Affero
  * Public License version 3 as published by the Free Software and appearing
  * in the file LICENSE.AGPL included in the packaging of this file.
  * The BEAT platform is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  * or FITNESS FOR A PARTICULAR PURPOSE.
- * 
+ *
  * You should have received a copy of the GNU Affero Public License along
  * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
 {% endcomment %}
@@ -26,13 +26,19 @@
 <div class="row">
   <div class="col-sm-4 col-sm-offset-4">
 
-    <h4>{% if account %}Account activated{% else %}Error{% endif %}</h4>
+    <h4>{% if account %}Account almost activated{% else %}Error{% endif %}</h4>
 
     {% if account %}
-    <p class="text">Your account was successfully activated. Welcome {{ account.username }}.
-    You can now <a href="{% url 'login' %}">sign in</a> and start your own experiments!</p>
+    <p class="text">Your account is almost activated. Welcome {{ account.username }}.
+    We have asked your "supervisor" to validate your account and accept you as a
+    supervisee. You will be informed by email once this is done.
+    In the meantime you can remind your "supervisor" to validate
+    your account. You will then be able to  <a href="{% url 'login' %}">sign in</a> and start your own experiments!</p>
     {% else %}
-    <p class="text">Your account wasn't activated. Check that the URL you entered is correct. If you still have problems to activate your account, <a href="{% url 'contact' %}">contact a member of our support staff</a>.</p>
+    <p class="text">Your account wasn't activated. Check that the URL you entered is correct.
+    If you already clicked on the link sent to you by email, you need to wait
+    for your "supervisor" to accept your account activation request.
+    If you still have problems to activate your account, <a href="{% url 'contact' %}">contact a member of our support staff</a>.</p>
     {% endif %}
 
   </div>
diff --git a/beat/web/ui/registration/templates/registration/blocked_user_reactivate.html b/beat/web/ui/registration/templates/registration/blocked_user_reactivate.html
new file mode 100644
index 0000000000000000000000000000000000000000..91e7f88c44ac15816a883b668cc3aeacbc5be2b9
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/blocked_user_reactivate.html
@@ -0,0 +1,82 @@
+{% extends "base.html" %}
+{% comment %}
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ * 
+ * This file is part of the beat.web module of the BEAT platform.
+ * 
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ * 
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * You should have received a copy of the GNU Affero Public License along
+ * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
+{% endcomment %}
+
+{% load registration_tags %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col-sm-4 col-sm-offset-4">
+
+    <div class="panel panel-default login-panel">
+
+      <div class="panel-heading">Account Reactivation</div>
+
+      <div class="panel-body">
+
+        <form method="post" action="" class="form">
+          {% csrf_token %}
+
+          {% for error in form.non_field_errors %}
+          <div class="alert alert-danger" role="alert">{{ error }}</div>
+          {% endfor %}
+
+          <div class="form-group{% if form.username.errors %} has-error{% endif %}">
+            {{ form.username.label_tag }}
+            {{ form.username|tabindex:1|addclass:"form-control" }}
+            {% for error in form.username.errors %}
+            <div class="alert alert-danger" role="alert">{{ error }}</div>
+            {% endfor %}
+          </div>
+
+          <div class="form-group{% if form.password.errors %} has-error{% endif %}">
+            {{ form.password.label_tag }}
+            {{ form.password|tabindex:2|addclass:"form-control" }}
+            {% for error in form.password.errors %}
+            <div class="alert alert-danger" role="alert">{{ error }}</div>
+            {% endfor %}
+          </div>
+
+          <div class="form-group{% if form.godfather.errors %} has-error{% endif %}">
+            {{ form.godfather.label_tag }}
+            {{ form.godfather|tabindex:2|addclass:"form-control" }}
+            {% for error in form.godfather.errors %}
+            <div class="alert alert-danger" role="alert">{{ error }}</div>
+            {% endfor %}
+          </div>
+
+          <div class="form-group">
+            <button type="submit" class="btn btn-success" tabindex="3"><i class="fa fa-plug fa-fw fa-lg"></i> Reactivation request</button>
+          </div>
+
+        </form>
+
+        <p class="comment">In order to sign in, you must be <a href="{% url 'registration' %}">registered</a></p>
+        <p class="comment">Already registered and not blocked? <a href="{% url 'login' %}">login</a></p>
+      </div>
+    </div>
+
+  </div>
+</div>
+
+{% endblock content %}
diff --git a/beat/web/ui/registration/templates/registration/login.html b/beat/web/ui/registration/templates/registration/login.html
index bf265cdad5926f95a4255a42fdc86f520dfcbb16..a08be74b799e4ab9f7eae5ee969268623f9dd189 100644
--- a/beat/web/ui/registration/templates/registration/login.html
+++ b/beat/web/ui/registration/templates/registration/login.html
@@ -64,7 +64,7 @@
         </form>
 
         <p class="comment">In order to sign in, you must be <a href="{% url 'registration' %}">registered</a></p>
-
+        <p class="comment">Inactive/Blocked user need to get their account reactivated <a href="{% url 'blocked_user_reactivation' %}">reactivation</a></p>
       </div>
     </div>
 
diff --git a/beat/web/ui/registration/templates/registration/mail.account_revalidation.message.txt b/beat/web/ui/registration/templates/registration/mail.account_revalidation.message.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2603383696072abc0f910d2b44df157aecba8ec8
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.account_revalidation.message.txt
@@ -0,0 +1,13 @@
+Dear {{ user.first_name }},
+
+Your need to re-confirm that you still use your account on the BEAT Platform.
+This check is done on a yearly basis.
+
+This can be done by going to your profile and making a revalidation request by
+clicking on the corresponding button.
+
+If you haven't done this by this expiration date: {{ expiration_date }}, your account
+will be blocked and you will then need to go trough an account revalidation
+procedure to re-activate it.
+
+BEAT Administrators at the Idiap Research Institute
diff --git a/beat/web/ui/registration/templates/registration/mail.account_revalidation.subject.txt b/beat/web/ui/registration/templates/registration/mail.account_revalidation.subject.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1de7fbb2d93421d8508fa45aeb420ab044bb6c40
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.account_revalidation.subject.txt
@@ -0,0 +1 @@
+Account re-validation - Required Confirmation
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_rejected.message.txt b/beat/web/ui/registration/templates/registration/mail.godfather_rejected.message.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f52e65923653bc83d7a12b6ecbd10575da8f32dd
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_rejected.message.txt
@@ -0,0 +1,17 @@
+Dear {{ supervisee.first_name }},
+
+Your supervision request was not accepted and has been rejected by the supervisor {{ supervisor.username }}.
+
+Either you made a mistake or you are not a supervisee anymore for {{ supervisor.username }}
+
+Your account {{ supervisee.username }} is currently not fully validated.
+
+You can request to validate your account through a new supervision request
+either through an existing validated supervisor from the platform or by asking your current supervisor
+to register on the platform as a supervisor and when his account is validated,
+you can request a supervision from him and get a re-activation of your
+account.
+
+Your account will be blocked on {{ supervisee.profile.rejection_date }}. You have until this date to find a
+supervisor. Passed this date your account will be blocked and you will need to
+proceed with an account reactivation request.
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_rejected.subject.txt b/beat/web/ui/registration/templates/registration/mail.godfather_rejected.subject.txt
new file mode 100644
index 0000000000000000000000000000000000000000..09ffd3d8ce91f60e543a60ebbe2736f7a706119d
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_rejected.subject.txt
@@ -0,0 +1 @@
+Account not confirmed - Supervision Rejected
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_rejected_delete_account.message.txt b/beat/web/ui/registration/templates/registration/mail.godfather_rejected_delete_account.message.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ca0d45a1ab8cc8b9aa93489458b690540d650945
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_rejected_delete_account.message.txt
@@ -0,0 +1,9 @@
+Dear {{ supervisee.first_name }},
+
+Your supervision request was not accepted and has been rejected by the supervisor {{ supervisor.username }}.
+
+Either you made a mistake or you are not a supervisee of {{ supervisor.username }}
+
+Your newly created account {{ supervisee.username }} has therefore currently been deleted.
+
+You can re-create a new account and make a proper supervision request.
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_rejection.message.txt b/beat/web/ui/registration/templates/registration/mail.godfather_rejection.message.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f3be84a068a3ff804ff3a92a4f909f7dd663ca8c
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_rejection.message.txt
@@ -0,0 +1,12 @@
+Dear {{ supervisor.first_name }},
+
+One of your supervisees has made a request to end your current supervision and request for a new one towards another supervisor related to his current situation.
+
+First Name: {{ supervisee.first_name }}
+Last Name: {{ supervisee.last_name }}
+Email: {{ supervisee.email }}
+Username: {{ supervisee.username }}
+
+This message is to inform you that you are no longer responsible towards this supervisee in the current future.
+
+BEAT Administrators at the Idiap Research Institute
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_rejection.subject.txt b/beat/web/ui/registration/templates/registration/mail.godfather_rejection.subject.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bab40169e7e2493347274d54f5b7fa894e048d14
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_rejection.subject.txt
@@ -0,0 +1 @@
+Account validation - Revoked Supervision
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_validated.message.txt b/beat/web/ui/registration/templates/registration/mail.godfather_validated.message.txt
new file mode 100644
index 0000000000000000000000000000000000000000..aac566f8f1fa0286795b1cf0c25c857ea347fe15
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_validated.message.txt
@@ -0,0 +1,6 @@
+Dear {{ supervisee.first_name }},
+
+Your account was successfully activated by your supervisor {{ supervisor.username }}.
+
+You can now <a href="{% url 'login' %}">sign in</a> and start your own
+experiments!</p>
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_validated.subject.txt b/beat/web/ui/registration/templates/registration/mail.godfather_validated.subject.txt
new file mode 100644
index 0000000000000000000000000000000000000000..af97067a67b3cb9a6a2731e828ffe90391c3558c
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_validated.subject.txt
@@ -0,0 +1 @@
+Account successfully activated - Supervision Confirmed
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_validation.message.txt b/beat/web/ui/registration/templates/registration/mail.godfather_validation.message.txt
new file mode 100644
index 0000000000000000000000000000000000000000..47fac57756aa39b614136bf5bb10c32c25e61ee7
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_validation.message.txt
@@ -0,0 +1,18 @@
+Dear {{ supervisor.first_name }},
+
+Thank you in advance for validating the registration of one your supervisees at the Idiap Research Institute's Biometric
+Evaluation and Testing (BEAT) platform. Before we can activate his account
+you must login to your account and under supervision tab validate the following supervisee:
+
+First Name: {{ supervisee.first_name }}
+Last Name: {{ supervisee.last_name }}
+Email: {{ supervisee.email }}
+Username: {{ supervisee.username }}
+
+If you don't do this the supervisee will not be able to use his account and
+will be deleted after 7 days.
+
+If you are having problems to activate your supervisee account, contact a member of our
+support staff at {{ prefix }}{% url 'contact' %}.
+
+BEAT Administrators at the Idiap Research Institute
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_validation.subject.txt b/beat/web/ui/registration/templates/registration/mail.godfather_validation.subject.txt
new file mode 100644
index 0000000000000000000000000000000000000000..00bae0b5ed20b2984ffb59c3b98098c9aefc7faa
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_validation.subject.txt
@@ -0,0 +1 @@
+Account validation - Supervision Confirmation required
diff --git a/beat/web/ui/registration/templates/registration/mail.godfather_validation_supervisee_add_request.message.txt b/beat/web/ui/registration/templates/registration/mail.godfather_validation_supervisee_add_request.message.txt
new file mode 100644
index 0000000000000000000000000000000000000000..813b7ebe96c75181b8d1682e717ea754f7783656
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.godfather_validation_supervisee_add_request.message.txt
@@ -0,0 +1,18 @@
+Dear {{ supervisor.first_name }},
+
+Thank you in advance for validating the request of one your supervisees at the Idiap Research Institute's Biometric
+Evaluation and Testing (BEAT) platform. Before we can activate this
+supervision request, you must login to your account and under supervision tab validate the following supervisee:
+
+First Name: {{ supervisee.first_name }}
+Last Name: {{ supervisee.last_name }}
+Email: {{ supervisee.email }}
+Username: {{ supervisee.username }}
+
+If you don't do this the supervisee will not be able to use his account in
+future and will not be recognized as your supervisee.
+
+If you are having problems to activate your supervisee account, contact a member of our
+support staff at {{ prefix }}{% url 'contact' %}.
+
+BEAT Administrators at the Idiap Research Institute
diff --git a/beat/web/ui/registration/templates/registration/mail.migration_10_accounts.message.txt b/beat/web/ui/registration/templates/registration/mail.migration_10_accounts.message.txt
new file mode 100644
index 0000000000000000000000000000000000000000..cb0958238029a6e40799d9ad3a4b72459c619d5e
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.migration_10_accounts.message.txt
@@ -0,0 +1,18 @@
+Dear {{ user.first_name }},
+
+The admin of the BEAT platform would like to inform you of a security update
+to your account used under username {{ user.username}}
+
+From now on you will need to be supervised by a known supervisor.
+
+To comply with this new rule, please go to your account settings and under
+Account Management click on "Add a supervisor" and validate it with a known
+supervisor.
+
+Failing to do so, your account will be blocked after a few weeks! (you
+can however still re-activate it through an account reactivation procedure)
+
+If you are having problems to revalidate your account, contact a member of our
+support staff at {{ prefix }}{% url 'contact' %}.
+
+BEAT Administrators at the Idiap Research Institute
diff --git a/beat/web/ui/registration/templates/registration/mail.migration_10_accounts.subject.txt b/beat/web/ui/registration/templates/registration/mail.migration_10_accounts.subject.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4d87013ca9ff2b727c1404229184e22c140cbcb6
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.migration_10_accounts.subject.txt
@@ -0,0 +1 @@
+Major Account Update - Supervision Required (BEAT platform)
diff --git a/beat/web/ui/registration/templates/registration/mail.supervisee_blocked_state_wait_for_activation.message.txt b/beat/web/ui/registration/templates/registration/mail.supervisee_blocked_state_wait_for_activation.message.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0473236bdb2393d170df4cf92b4a22cf379fce5b
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.supervisee_blocked_state_wait_for_activation.message.txt
@@ -0,0 +1,14 @@
+Dear {{ supervisee.first_name }},
+
+Your account is currently blocked due to a lack of supervisor.
+
+We acknowledge your request made to re-activate your account via a supervision request to {{ supervisor.username }}.
+
+You will be informed if your supervisor has accepted or rejected your request.
+
+If no information is given by your supervisor, you will be allowed to make a new supervision request after a week.
+
+If you are having problems to activate your account, contact a member of our
+support staff at {{ prefix }}{% url 'contact' %}.
+
+BEAT Administrators at the Idiap Research Institute
diff --git a/beat/web/ui/registration/templates/registration/mail.supervisee_blocked_validation_wait.subject.txt b/beat/web/ui/registration/templates/registration/mail.supervisee_blocked_validation_wait.subject.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1c53371c0bb8c748a3baee534fdd9c1b71995b5d
--- /dev/null
+++ b/beat/web/ui/registration/templates/registration/mail.supervisee_blocked_validation_wait.subject.txt
@@ -0,0 +1 @@
+Blocked Account re-validation - Supervision request confirmation
diff --git a/beat/web/ui/registration/templates/registration/registration_form.html b/beat/web/ui/registration/templates/registration/registration_form.html
index eb3cc436cd9ce29188dfa7afdf8b5a6f3fe2abf8..3dc5dd1fc78079147141b8e7c62003d9e1cca108 100644
--- a/beat/web/ui/registration/templates/registration/registration_form.html
+++ b/beat/web/ui/registration/templates/registration/registration_form.html
@@ -2,21 +2,21 @@
 {% comment %}
  * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
  * Contact: beat.support@idiap.ch
- * 
+ *
  * This file is part of the beat.web module of the BEAT platform.
- * 
+ *
  * Commercial License Usage
  * Licensees holding valid commercial BEAT licenses may use this file in
  * accordance with the terms contained in a written agreement between you
  * and Idiap. For further information contact tto@idiap.ch
- * 
+ *
  * Alternatively, this file may be used under the terms of the GNU Affero
  * Public License version 3 as published by the Free Software and appearing
  * in the file LICENSE.AGPL included in the packaging of this file.
  * The BEAT platform is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  * or FITNESS FOR A PARTICULAR PURPOSE.
- * 
+ *
  * You should have received a copy of the GNU Affero Public License along
  * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
 {% endcomment %}
@@ -91,6 +91,19 @@
             {% endfor %}
           </div>
 
+          <div class="form-group{% if form.godfather.errors %} has-error{% endif %}">
+            {{ form.godfather.label_tag }}
+            {{ form.godfather|tabindex:7|addclass:"form-control" }}
+            {% for error in form.godfather.errors %}
+            <div class="alert alert-danger" role="alert">{{ error }}</div>
+            {% endfor %}
+            <div class="alert alert-warning" role="alert">Your account needs to
+              be validated by a recognized person on the platform. This recognized
+              person is known as a supervisor.
+              If you wish to become a supervisor, please follow the <a href="{% url 'contact' %}">contact information</a>
+            </div>
+          </div>
+
           <div class="form-group{% if form.tos.errors %} has-error{% endif %}">
             {{ form.tos }} I have carefully read the <a href="{% url 'terms-of-service' %}">Terms of Service</a>, which include the Privacy and Data Protection Terms of Use, and fully agree and undertake to comply with all provisions therein by checking this box.
             {% for error in form.tos.errors %}
diff --git a/beat/web/ui/urls.py b/beat/web/ui/urls.py
index 75dcf8a9eb76845c119d4c78beeb67020ee13468..960b0c3ada55cf26ade4f71b31e5d8dc4e0479bb 100644
--- a/beat/web/ui/urls.py
+++ b/beat/web/ui/urls.py
@@ -48,6 +48,11 @@ urlpatterns = [
         name='login',
         ),
 
+    url(r'^blocked_user_reactivation/$',
+        views.blocked_user_reactivation,
+        name='blocked_user_reactivation',
+        ),
+
     url(r'^events/(?P<author_name>\w+)/$',
         views.activity_stream,
         name='activity-stream',
diff --git a/beat/web/ui/views.py b/beat/web/ui/views.py
index 07bbef7f5e0a16b299afbfeadc100b3ae19d8f34..b6183a915274f732ba8c010bdadcff91aef12099 100644
--- a/beat/web/ui/views.py
+++ b/beat/web/ui/views.py
@@ -29,6 +29,8 @@
 from django.shortcuts import get_object_or_404
 from django.shortcuts import render_to_response
 from django.template import RequestContext
+from django.template import loader
+from django.template import Context
 from django.contrib.auth.views import login as django_login
 from django.contrib.auth.forms import PasswordChangeForm
 from django.contrib.auth.models import User
@@ -45,6 +47,11 @@ from rest_framework.authtoken.models import Token
 
 from ..import __version__
 from ..common.models import Shareable
+from registration.forms import BlockedUserRevalidationForm
+from ..accounts.models import Profile, SupervisionTrack
+
+import datetime
+from urlparse import urlparse
 
 import logging
 logger = logging.getLogger(__name__)
@@ -71,6 +78,113 @@ def login(request):
     return response
 
 
+def blocked_user_reactivation(request):
+    '''Reactivation page'''
+
+    if request.method == "POST":
+        form = BlockedUserRevalidationForm(request.POST)
+        if form.is_valid():
+            try:
+                user = User.objects.get(username=request.POST["username"])
+                if user.check_password(request.POST["password"]):
+                    # Check if user is a blocked user
+                    if user.profile.status == Profile.BLOCKED:
+                        godfather = User.objects.get(username=request.POST["godfather"])
+                        # Check the godfather
+                        if godfather.profile.status == Profile.ACCEPTED:
+                            # Check if supervision track already exists
+                            if user.profile.supervision_key is None:
+                                # Create and assign key
+                                supervisee = user
+                                supervisee.profile.supervision_key = supervisee.profile._generate_current_supervision_key()
+                                supervisiontrack = SupervisionTrack.objects.create(
+                                    supervisee = supervisee,
+                                    godfather = godfather,
+                                    is_valid = False,
+                                    )
+
+                                # Assign key to supervision track
+                                supervisiontrack.supervision_key = supervisee.profile.supervision_key
+                                supervisiontrack.save()
+                                supervisee.profile.supervision.add(supervisiontrack)
+                                # Add a rejection date to the supervisee profile
+                                now = datetime.datetime.now()
+                                expiration_date_delta = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS_FROM_GODFATHER)
+                                if supervisee.profile.rejection_date == None:
+                                    supervisee.profile.rejection_date = now + expiration_date_delta
+
+                                supervisee.profile.save()
+                                supervisee.save()
+
+                                #Inform by email the supervisor that he has a new supervisee request
+                                from django.core.mail import send_mail
+
+                                parsed_url = urlparse(settings.URL_PREFIX)
+                                server_address = '%s://%s' % (parsed_url.scheme, parsed_url.hostname)
+
+                                c = Context({ 'supervisor': godfather,
+                                              'supervisee': supervisee,
+                                              'prefix': server_address,
+                                            })
+
+                                try:
+                                    t = loader.get_template('registration/mail.godfather_validation.subject.txt')
+                                    subject = t.render(c)
+
+                                    # Note: e-mail subject *must not* contain newlines
+                                    subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+                                    t = loader.get_template('registration/mail.godfather_validation_supervisee_add_request.message.txt')
+                                    message = t.render(c)
+
+                                    send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [godfather.email])
+                                except:
+                                    pass
+
+                                # inform the supervisee of his request
+                                c = Context({ 'supervisor': godfather,
+                                              'supervisee': supervisee,
+                                              'prefix': server_address,
+                                            })
+
+                                try:
+                                    t = loader.get_template('registration/mail.supervisee_blocked_validation_wait.subject.txt')
+                                    subject = t.render(c)
+
+                                    # Note: e-mail subject *must not* contain newlines
+                                    subject = settings.EMAIL_SUBJECT_PREFIX + ''.join(subject.splitlines())
+
+                                    t = loader.get_template('registration/mail.supervisee_blocked_state_wait_for_activation.message.txt')
+                                    message = t.render(c)
+
+                                    send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [supervisee.email])
+                                except:
+                                    pass
+
+                                messages.success(request, "Your supervision request has been successfully processed.")
+                            else:
+                                messages.error(request, "A supervision request already exists for this account, you need to wait for your supervisor's decision.")
+                        else:
+                            messages.error(request, "The selected supervisor is not valid.")
+                    else:
+                        path = request.GET.get('next', '/')
+                        messages.error(request, "Your profile is not blocked, you can go to the login page instead.")
+                else:
+                    # Don't give too much details though we know the problem is the password only at this step
+                    messages.error(request, "Wrong user or password combination!")
+            except User.DoesNotExist:
+                # Don't give too much details though we know the problem is the password only at this step
+                messages.error(request, "Wrong user or password combination!")
+                pass
+
+            #return redirect('blocked_user_reactivation', pk=post.pk)
+    else:
+        form = BlockedUserRevalidationForm()
+
+    return render_to_response('registration/blocked_user_reactivate.html', {'form': form},
+        context_instance=RequestContext(request))
+
+
 def gather_contributions(requestor, author):
     '''Gather contributions that are accessible to a certain requestor'''
 
diff --git a/beat/web/urls.py b/beat/web/urls.py
index b0b91118c37c8cec212927313415f824806064ad..081492e4b8ae69284430ba754999dde87eab4a07 100644
--- a/beat/web/urls.py
+++ b/beat/web/urls.py
@@ -164,6 +164,10 @@ unprefixed_patterns += [
         include('beat.web.reports.api_urls', namespace='api_reports'),
         ),
 
+    url(r'^api/v1/accounts/',
+        include('beat.web.accounts.api_urls', namespace='api_accounts'),
+        ),
+
     ]
 
 
diff --git a/beat/web/utils/management/commands/daily_cron_actions.py b/beat/web/utils/management/commands/daily_cron_actions.py
index d92e736856a9b31dd9ed3f8b5775e80172cab478..88d08e1d02d394fa5375779154846e5a71215a6d 100644
--- a/beat/web/utils/management/commands/daily_cron_actions.py
+++ b/beat/web/utils/management/commands/daily_cron_actions.py
@@ -52,3 +52,15 @@ class Command(BaseCommand):
 
         # Send report cleanup warnings and cleanup reports
         call_command('send_report_cleanup_warning_and_cleanup')
+
+        # Clean and remove invalid users
+        call_command('clean_invalid_users')
+
+        # Block rejected users with no supervision after specific rejection date
+        call_command('block_rejected_users')
+
+        # Clean blocked users with no supervision after specific rejection date
+        call_command('clean_blocked_users_expired_requests')
+
+        # Yearly revalidation process with warnings, status change and blockage
+        call_command('year_revalidation_users')
diff --git a/beat/web/utils/management/commands/install.py b/beat/web/utils/management/commands/install.py
index 7201b86eddc7a1b13c4339a5ce0a8bab86f817ca..a735c8b5f8a15bc4fe0237d71800dc3afe9393d4 100755
--- a/beat/web/utils/management/commands/install.py
+++ b/beat/web/utils/management/commands/install.py
@@ -98,6 +98,12 @@ def add_user(name, passwd, token_key):
             user.is_staff = True
             user.is_superuser = True
         user.save()
+        #set profile
+        user.profile.status = 'A'
+        user.profile.rejection_date = None
+        user.profile.supervision_key = None
+        user.profile.save()
+        user.save()
 
         if name == passwd:
             logger.info("Created user `%s' with password `%s'", name, passwd)