Commit 890b0eaf authored by Sofia Papagiannaki's avatar Sofia Papagiannaki
Browse files

-add docs

-incorporate CsrfViewMiddleware
-change tamplates to render forms
-change email template location
-api.activate() returns only information about token
- In prepare_response, instead of setting the cookie, log the user in
- use django.contrib.auth.views for reseting and changing user password

Ref: #1874
parent 0905ccd2
# Copyright 2011 GRNET S.A. All rights reserved.
#
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
......@@ -25,61 +25,38 @@
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import socket
import logging
from django.core.mail import send_mail
from django.conf import settings
from django.template.loader import render_to_string
from django import forms
from django.utils.translation import ugettext as _
from smtplib import SMTPException
from urllib import quote
from django.contrib.auth.forms import UserCreationForm
from django.conf import settings
from hashlib import new as newhasher
from astakos.im.forms import LocalRegisterForm
from astakos.im.util import get_or_create_user
from astakos.im.models import AstakosUser
from astakos.im.util import get_or_create_user
class Backend(object):
def get_signup_form(self, request):
initial_data = request.POST if request.method == 'POST' else None
return LocalRegisterForm(initial_data)
class AdminProfileForm(forms.ModelForm):
"""
Subclass of ``ModelForm`` for permiting user to edit his/her profile.
Most of the fields are readonly since the user is not allowed to change them.
def signup(self, request, form, success_url):
kwargs = {}
for field in form.fields:
if hasattr(AstakosUser(), field):
kwargs[field] = form.cleaned_data[field]
user = get_or_create_user(**kwargs)
status = 'success'
try:
send_verification(request.build_absolute_uri('/').rstrip('/'), user)
message = _('Verification sent to %s' % user.email)
except (SMTPException, socket.error) as e:
status = 'error'
name = 'strerror'
message = getattr(e, name) if hasattr(e, name) else e
if user and status == 'error':
#delete created user
user.delete()
return status, message
def send_verification(baseurl, user):
url = settings.ACTIVATION_LOGIN_TARGET % (baseurl,
quote(user.auth_token),
quote(baseurl))
message = render_to_string('activation.txt', {
'user': user,
'url': url,
'baseurl': baseurl,
'service': settings.SERVICE_NAME,
'support': settings.DEFAULT_CONTACT_EMAIL})
sender = settings.DEFAULT_FROM_EMAIL
send_mail('Pithos account activation', message, sender, [user.email])
logging.info('Sent activation %s', user)
\ No newline at end of file
The class defines a save method which sets ``is_verified`` to True so as the user
during the next login will not to be redirected to profile page.
"""
class Meta:
model = AstakosUser
def __init__(self, *args, **kwargs):
super(AdminProfileForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
ro_fields = ('username','date_joined', 'auth_token', 'last_login', 'email')
if instance and instance.id:
for field in ro_fields:
if isinstance(self.fields[field].widget, forms.CheckboxInput):
self.fields[field].widget.attrs['disabled'] = True
self.fields[field].widget.attrs['readonly'] = True
\ No newline at end of file
......@@ -36,7 +36,7 @@
<td>{{ user.email }}</td>
<td>{{ user.inviter.realname }}</td>
<td>
<form action="{% url astakos.im.admin.views.users_activate user.id %}" method="post">
<form action="{% url astakos.im.admin.views.users_activate user.id %}" method="post">{% csrf_token %}
<input type="hidden" name="page" value="{{ page }}">
<button type="submit" class="btn primary">Activate</button>
</form>
......
......@@ -2,7 +2,7 @@
{% block body %}
<form action="{% url astakos.im.admin.views.users_create %}" method="post">
<form action="{% url astakos.im.admin.views.users_create %}" method="post">{% csrf_token %}
<div class="clearfix">
<label for="user-username">Username</label>
<div class="input">
......
......@@ -4,119 +4,8 @@
{% block body %}
<form action="{% url astakos.im.admin.views.users_modify user.id %}" method="post">
<div class="clearfix">
<label for="user-id">ID</label>
<div class="input">
<span class="uneditable-input" id="user-id">{{ user.id }}</span>
</div>
</div>
<div class="clearfix">
<label for="user-username">Username</label>
<div class="input">
<input class="span4" id="user-username" name="username" value="{{ user.username }}" type="text" />
</div>
</div>
<div class="clearfix">
<label for="user-first-name">First Name</label>
<div class="input">
<input class="span4" id="user-first-name" name="first_name" value="{{ user.first_name }}" type="text" />
</div>
</div>
<div class="clearfix">
<label for="user-last-name">Last Name</label>
<div class="input">
<input class="span4" id="user-last-name" name="last_name" value="{{ user.last_name }}" type="text" />
</div>
</div>
<div class="clearfix">
<label for="user-admin">Admin</label>
<div class="input">
<ul class="inputs-list">
<li>
<label>
<input type="checkbox" id="user-admin" name="admin"{% if user.is_superuser %} checked{% endif %}>
</label>
</li>
</ul>
</div>
</div>
<div class="clearfix">
<label for="user-affiliation">Affiliation</label>
<div class="input">
<input class="span4" id="user-affiliation" name="affiliation" value="{{ user.affiliation }}" type="text" />
</div>
</div>
<div class="clearfix">
<label for="user-is-active">Is active?</label>
<div class="input">
<ul class="inputs-list">
<li>
<label>
<input type="checkbox" id="user-is-active" name="is_active"{% if user.is_active %} checked{% endif %}>
</label>
</li>
</ul>
</div>
</div>
<div class="clearfix">
<label for="user-invitations">Invitations</label>
<div class="input">
<input class="span2" id="user-invitations" name="invitations" value="{{ user.invitations }}" type="text" />
</div>
</div>
<div class="clearfix">
<label for="user-quota">Quota</label>
<div class="input">
<div class="input-append">
<input class="span2" id="user-quota" name="quota" value="{{ user.quota|GiB }}" type="text" />
<span class="add-on">GiB</span>
</div>
</div>
</div>
<div class="clearfix">
<label for="user-token">Token</label>
<div class="input">
<input class="span4" id="user-token" name="auth_token" value="{{ user.auth_token }}" type="text" />
</div>
</div>
<div class="clearfix">
<label for="token-created">Token Created</label>
<div class="input">
<span class="uneditable-input" id="token-created">{{ user.auth_token_created }}</span>
</div>
</div>
<div class="clearfix">
<label for="token-expires">Token Expires</label>
<div class="input">
<input type="datetime" class="span4" id="token-expires" name="auth_token_expires" value="{{ user.auth_token_expires|isoformat }}" />
</div>
</div>
<div class="clearfix">
<label for="user-date-joined">Created</label>
<div class="input">
<span class="uneditable-input" id="user-date-joined">{{ user.date_joined }}</span>
</div>
</div>
<div class="clearfix">
<label for="user-updated">Updated</label>
<div class="input">
<span class="uneditable-input" id="user-updated">{{ user.updated }}</span>
</div>
</div>
<form action="{% url astakos.im.admin.views.users_modify user.id %}" method="post">{% csrf_token %}
{{ form.as_p }}
<div class="actions">
<button type="submit" class="btn primary">Save Changes</button>
......
......@@ -2,7 +2,7 @@
Αγαπητέ/η {{ user.realname }},
Ο λογαρισμός σας για την υπηρεσία {{ service }} της ΕΔΕΤκατά την Alpha (πιλοτική)
Ο λογαρισμός σας για την υπηρεσία {{ site_name }} της ΕΔΕΤκατά την Alpha (πιλοτική)
φάση λειτουργίας της έχει ενεργοποιηθεί.
Για να συνδεθείτε, χρησιμοποιήστε τον παρακάτω σύνδεσμο:
......@@ -16,7 +16,7 @@
υπηρεσίας, δεν αποκλείεται να εμφανιστούν προβλήματα στο λογισμικό
διαχείρισης ή η υπηρεσία να μην είναι διαθέσιμη κατά διαστήματα. Για
αυτό το λόγο, σας παρακαλούμε να μη μεταφέρετε ακόμη σημαντικά κομμάτια
της δουλειάς σας στην υπηρεσία {{ service }}. Επίσης, παρακαλούμε να έχετε
της δουλειάς σας στην υπηρεσία {{ site_name }}. Επίσης, παρακαλούμε να έχετε
υπόψη σας ότι όλα τα δεδομένα, θα διαγραφούν κατά τη μετάβαση από την
έκδοση Alpha στην έκδοση Beta. Θα υπάρξει έγκαιρη ειδοποίησή σας πριν
από τη μετάβαση αυτή.
......@@ -30,7 +30,7 @@
Για όποιες παρατηρήσεις ή προβλήματα στη λειτουργεία της υπηρεσίας μπορείτε να
απευθυνθείτε στο {{ support }}.
Σας ευχαριστούμε πολύ για τη συμμετοχή στην Alpha λειτουργία του {{ service }}.
Σας ευχαριστούμε πολύ για τη συμμετοχή στην Alpha λειτουργία του {{ site_name }}.
Με εκτίμηση,
......@@ -40,7 +40,7 @@
Dear {{ user.realname }},
Your account for GRNET's {{ service }} service for its Alpha test phase has been
Your account for GRNET's {{ site_name }} service for its Alpha test phase has been
activated.
To login, please use the following link:
......@@ -53,7 +53,7 @@ This service is, for a few weeks, in Alpha. Although every care has been
taken to ensure high quality, it is possible there may still be software
bugs, or periods of limited service availability. For this reason, we
kindly request you do not transfer important parts of your work to
{{ service }}, yet. Also, please bear in mind that all data, will be deleted
{{ site_name }}, yet. Also, please bear in mind that all data, will be deleted
when the service moves to Beta test. Before the transition, you will be
notified in time.
......@@ -65,6 +65,6 @@ reliability of this innovative service.
For any remarks or problems you can contact {{ support }}.
Thank you for participating in the Alpha test of {{ service }}.
Thank you for participating in the Alpha test of {{ site_name }}.
Greek Research and Technonogy Network - GRNET
......@@ -53,19 +53,27 @@ from django.template.loader import render_to_string
from django.utils.http import urlencode
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.db import transaction
from django.contrib.auth.models import AnonymousUser
from django.contrib.sites.models import get_current_site
#from astakos.im.openid_store import PithosOpenIDStore
from astakos.im.models import AstakosUser, Invitation
from astakos.im.util import isoformat, get_or_create_user, get_context
from astakos.im.forms import *
from astakos.im.backends import get_backend
from astakos.im.views import render_response, index
from astakos.im.admin.forms import AdminProfileForm
def requires_admin(func):
"""
Decorator checkes whether the request.user is a superuser and if not
redirects to login page.
"""
@wraps(func)
def wrapper(request, *args):
if not settings.BYPASS_ADMIN_AUTH:
if not request.user:
if isinstance(request.user, AnonymousUser):
next = urlencode({'next': request.build_absolute_uri()})
login_uri = reverse(index) + '?' + next
return HttpResponseRedirect(login_uri)
......@@ -76,6 +84,31 @@ def requires_admin(func):
@requires_admin
def admin(request, template_name='admin.html', extra_context={}):
"""
Renders the admin page
If the ``request.user`` is not a superuser redirects to login page.
**Arguments**
``template_name``
A custom template to use. This is optional; if not specified,
this will default to ``admin.html``.
``extra_context``
An dictionary of variables to add to the template context.
**Template:**
admin.html or ``template_name`` keyword argument.
**Template Context:**
The template context is extended by:
* tab: the name of the active tab
* stats: dictionary containing the number of all and prending users
"""
stats = {}
stats['users'] = AstakosUser.objects.count()
stats['pending'] = AstakosUser.objects.filter(is_active = False).count()
......@@ -85,11 +118,43 @@ def admin(request, template_name='admin.html', extra_context={}):
stats['invitations_consumed'] = invitations.filter(is_consumed=True).count()
kwargs = {'tab': 'home', 'stats': stats}
context = get_context(request, extra_context, **kwargs)
context = get_context(request, extra_context,**kwargs)
return render_response(template_name, context_instance = context)
@requires_admin
def users_list(request, template_name='users_list.html', extra_context={}):
"""
Displays the list of all users.
If the ``request.user`` is not a superuser redirects to login page.
**Arguments**
``template_name``
A custom template to use. This is optional; if not specified,
this will default to ``users_list.html``.
``extra_context``
An dictionary of variables to add to the template context.
**Template:**
users_list.html or ``template_name`` keyword argument.
**Template Context:**
The template context is extended by:
* users: list of users fitting in current page
* filter: search key
* pages: the number of pages
* prev: the previous page
* next: the current page
**Settings:**
* ADMIN_PAGE_LIMIT: Show these many users per page in admin interface
"""
users = AstakosUser.objects.order_by('id')
filter = request.GET.get('filter', '')
......@@ -115,46 +180,102 @@ def users_list(request, template_name='users_list.html', extra_context={}):
'pages':range(1, npages + 1),
'prev':prev,
'next':next}
context = get_context(request, extra_context, **kwargs)
context = get_context(request, extra_context,**kwargs)
return render_response(template_name, context_instance = context)
@requires_admin
def users_info(request, user_id, template_name='users_info.html', extra_context={}):
"""
Displays the specific user profile.
If the ``request.user`` is not a superuser redirects to login page.
**Arguments**
``template_name``
A custom template to use. This is optional; if not specified,
this will default to ``users_info.html``.
``extra_context``
An dictionary of variables to add to the template context.
**Template:**
users_info.html or ``template_name`` keyword argument.
**Template Context:**
The template context is extended by:
* user: the user instance identified by ``user_id`` keyword argument
"""
if not extra_context:
extra_context = {}
kwargs = {'user':AstakosUser.objects.get(id=user_id)}
context = get_context(request, extra_context, **kwargs)
return render_response(template_name, context_instance = context)
user = AstakosUser.objects.get(id=user_id)
return render_response(template_name,
form = AdminProfileForm(instance=user),
context_instance = get_context(request, extra_context))
@requires_admin
def users_modify(request, user_id):
user = AstakosUser.objects.get(id=user_id)
user.username = request.POST.get('username')
user.first_name = request.POST.get('first_name')
user.last_name = request.POST.get('last_name')
user.is_superuser = True if request.POST.get('admin') else False
user.affiliation = request.POST.get('affiliation')
user.is_active = True if request.POST.get('is_active') else False
user.invitations = int(request.POST.get('invitations') or 0)
#user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3) # In GiB
user.auth_token = request.POST.get('auth_token')
try:
auth_token_expires = request.POST.get('auth_token_expires')
d = datetime.strptime(auth_token_expires, '%Y-%m-%dT%H:%MZ')
user.auth_token_expires = d
except ValueError:
pass
user.save()
return redirect(users_info, user.id)
def users_modify(request, user_id, template_name='users_info.html', extra_context={}):
"""
Update the specific user information. Upon success redirects to ``user_info`` view.
If the ``request.user`` is not a superuser redirects to login page.
"""
form = AdminProfileForm(request.POST)
if form.is_valid():
form.save()
return redirect(users_info, user.id, template_name, extra_context)
return render_response(template_name,
form = form,
context_instance = get_context(request, extra_context))
@requires_admin
def users_delete(request, user_id):
"""
Deletes the specified user
If the ``request.user`` is not a superuser redirects to login page.
"""
user = AstakosUser.objects.get(id=user_id)
user.delete()
return redirect(users_list)
@requires_admin
def pending_users(request, template_name='pending_users.html', extra_context={}):
"""
Displays the list of the pending users.
If the ``request.user`` is not a superuser redirects to login page.
**Arguments**
``template_name``
A custom template to use. This is optional; if not specified,
this will default to ``users_list.html``.
``extra_context``
An dictionary of variables to add to the template context.
**Template:**
pending_users.html or ``template_name`` keyword argument.
**Template Context:**
The template context is extended by:
* users: list of pending users fitting in current page
* filter: search key
* pages: the number of pages
* prev: the previous page
* next: the current page
**Settings:**
* ADMIN_PAGE_LIMIT: Show these many users per page in admin interface
"""
users = AstakosUser.objects.order_by('id')
users = users.filter(is_active = False)
......@@ -183,34 +304,70 @@ def pending_users(request, template_name='pending_users.html', extra_context={})
'prev':prev,
'next':next}
return render_response(template_name,
context_instance = get_context(request, extra_context, **kwargs))
context_instance = get_context(request, extra_context,**kwargs))
def _send_greeting(baseurl, user):
def _send_greeting(request, user, template_name):
url = reverse('astakos.im.views.index')
subject = _('Welcome to %s' %settings.SERVICE_NAME)
message = render_to_string('welcome.txt', {
site = get_current_site(request)
baseurl = request.build_absolute_uri('/').rstrip('/')
message = render_to_string(template_name, {
'user': user,
'url': url,
'baseurl': baseurl,
'service': settings.SERVICE_NAME,
'site_name': site.name,
'support': settings.DEFAULT_CONTACT_EMAIL})
sender = settings.DEFAULT_FROM_EMAIL
send_mail(subject, message, sender, [user.email])
logging.info('Sent greeting %s', user)
@requires_admin
def users_activate(request, user_id, template_name='pending_users.html', extra_context={}):
@transaction.commit_manually
def users_activate(request, user_id, template_name='pending_users.html', extra_context={}, email_template_name='welcome_email.txt'):
"""
Activates the specific user and sends an email. Upon success renders the
``template_name`` keyword argument if exists else renders ``pending_users.html``.
If the ``request.user`` is not a superuser redirects to login page.
**Arguments**
``template_name``
A custom template to use. This is optional; if not specified,
this will default to ``users_list.html``.
``extra_context``
An dictionary of variables to add to the template context.
**Templates:**
pending_users.html or ``template_name`` keyword argument.
welcome_email.txt or ``email_template_name`` keyword argument.
**Template Context:**
The template context is extended by:
* users: list of pending users fitting in current page
* filter: search key
* pages: the number of pages
* prev: the previous page
* next: the current page
"""
user = AstakosUser.objects.get(id=user_id)
user.is_active = True
status = 'success'
user.save()
status = messages.SUCCESS
try:
_send_greeting(request.build_absolute_uri('/').rstrip('/'), user)
_send_greeting(request, user, email_template_name)
message = _('Greeting sent to %s' % user.email)
user.save()
transaction.commit()
except (SMTPException, socket.error) as e:
status = 'error'
status = messages.ERROR
name = 'strerror'
message = getattr(e, name) if hasattr(e, name) else e
transaction.rollback()
messages.add_message(request, status, message)
users = AstakosUser.objects.order_by('id')
users = users.filter(is_active = False)
......@@ -230,13 +387,40 @@ def users_activate(request, user_id, template_name='pending_users.html', extra_c
'pages':range(1,