--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'User.is_verified'
+ db.add_column('im_user', 'is_verified', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'User.is_verified'
+ db.delete_column('im_user', 'is_verified')
+
+
+ models = {
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.User']"}),
+ 'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.user': {
+ 'Meta': {'object_name': 'User'},
+ 'affiliation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'email': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}),
+ 'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'state': ('django.db.models.fields.CharField', [], {'default': "'ACTIVE'", 'max_length': '16'}),
+ 'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
+ }
+ }
+
+ complete_apps = ['im']
created = models.DateTimeField('Creation date')
updated = models.DateTimeField('Update date')
+ is_verified = models.BooleanField('Verified?', default=False)
+
@property
def quota(self):
return get_quota(self.uniq)
from django.conf import settings
from django.http import HttpResponseBadRequest
+from django.core.urlresolvers import reverse
+from django.utils.http import urlencode
from pithos.im.models import Invitation
from pithos.im.target.util import get_or_create_user, prepare_response
invitation.realname,
'Invitation',
invitation.inviter.level + 1)
+
next = request.GET.get('next')
+ if settings.FORCE_PROFILE_UPDATE and not user.is_verified:
+ profile_url = reverse('pithos.im.views.users_profile', args=(user.id,))
+ next = urlencode({'next': next})
+ profile_url = profile_url + '?' + next
+ return prepare_response(request, user, profile_url)
+
return prepare_response(request, user, next, 'renew' in request.GET)
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from django.conf import settings
from django.template.loader import render_to_string
+from django.core.urlresolvers import reverse
+from django.utils.http import urlencode
from pithos.im.target.util import prepare_response
from pithos.im.models import User
return HttpResponseBadRequest('Unverified account')
next = request.POST.get('next')
+ if settings.FORCE_PROFILE_UPDATE and not user.is_verified:
+ profile_url = reverse('pithos.im.views.users_profile', args=(user.id,))
+ next = urlencode({'next': next})
+ profile_url = profile_url + '?' + next
+ return prepare_response(request, user, profile_url)
+
return prepare_response(request, user, next)
def activate(request):
# or implied, of GRNET S.A.
from django.http import HttpResponseBadRequest
+from django.core.urlresolvers import reverse
+from django.utils.http import urlencode
+from django.conf import settings
from pithos.im.target.util import get_or_create_user, prepare_response
affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, '')
+ user = get_or_create_user(eppn, realname, affiliation, 0)
+ next = request.GET.get('next')
+ if settings.FORCE_PROFILE_UPDATE and not user.is_verified:
+ profile_url = reverse('pithos.im.views.users_profile', args=(user.id,))
+ next = urlencode({'next': next})
+ profile_url = profile_url + '?' + next
+ return prepare_response(request, user, profile_url)
+
return prepare_response(request,
- get_or_create_user(eppn, realname, affiliation, 0),
- request.GET.get('next'),
+ user,
+ next,
'renew' in request.GET)
# TODO: Avoid redirect loops.
parts = list(urlsplit(next))
# Do not pass on user and token if we are on the same server.
- if request.get_host() != parts[1]:
+ if parts[1] and request.get_host() != parts[1]:
parts[3] = urlencode({'user': user.uniq, 'token': user.auth_token})
next = urlunsplit(parts)
-{% extends "base.html" %}
+{% extends "admin_base.html" %}
{% block body %}
<ul class="unstyled">
--- /dev/null
+{% extends "base.html" %}
+
+{% block tabs %}
+<ul class="tabs">
+ <li{% ifequal tab "home" %} class="active"{% endifequal %}>
+ <a href="{% url pithos.im.views.admin %}">Home</a>
+ </li>
+ <li{% ifequal tab "users" %} class="active"{% endifequal %}>
+ <a href="{% url pithos.im.views.users_list %}">Users</a>
+ </li>
+ <li{% ifequal tab "invitations" %} class="active"{% endifequal %}>
+ <a href="{% url pithos.im.views.invitations_list %}">Invitations</a>
+ </li>
+</ul>
+{% endblock %}
+
+{% block body %}{% endblock %}
<div style="padding: 5px 0px 0px 0px">
<img src="/im/static/banner.png" width="900" height="200">
</div>
+ {% block title %}{% endblock %}
+
+ {% if message %}
+ <br />
+ <div class="alert-message.{{ status }}">
+ <p>{{ message }}</p>
+ </div>
+ {% endif %}
- <ul class="tabs">
- <li{% ifequal tab "home" %} class="active"{% endifequal %}>
- <a href="{% url pithos.im.views.admin %}">Home</a>
- </li>
- <li{% ifequal tab "users" %} class="active"{% endifequal %}>
- <a href="{% url pithos.im.views.users_list %}">Users</a>
- </li>
- <li{% ifequal tab "invitations" %} class="active"{% endifequal %}>
- <a href="{% url pithos.im.views.invitations_list %}">Invitations</a>
- </li>
- </ul>
+ {% block tabs %}{% endblock %}
{% block body %}{% endblock %}
</div>
-{% extends 'local_base.html'%}
+{% extends 'base.html'%}
{% block title%}
<h2>Welcome</h2>
-{% extends "base.html" %}
+{% extends "admin_base.html" %}
{% load formatters %}
+++ /dev/null
-<!DOCTYPE html>
-<html>
-<head>
- <meta charset="utf-8" />
- <title>{{ title|default:"User Login" }}</title>
- <link rel="stylesheet" href="/im/static/bootstrap.css">
- <script src="/im/static/jquery.js"></script>
- <script src="/im/static/jquery.tablesorter.js"></script>
- <script src="/im/static/main.js"></script>
-</head>
-<body>
- <div class="container">
- <div style="padding: 5px 0px 0px 0px">
- <img src="/im/static/banner.png" width="900" height="200">
- </div>
- {% block title%}{% endblock title%}
-
- {% if message %}
- <br />
- <div class="alert-message.{{ status }}">
- <p>{{ message }}</p>
- </div>
- {% endif %}
-
- {% block body%}{% endblock %}
- </div>
-</body>
-</html>
-{% extends "local_base.html" %}
+{% extends "base.html" %}
{% block title%}
<h2>Sign up</h2>
-{% extends "local_base.html" %}
+{% extends "base.html" %}
{% block title%}
<h2>Reclaim password</h2>
-{% extends "local_base.html" %}
+{% extends "base.html" %}
{% block title%}
<h2>Reset password</h2>
-{% extends "base.html" %}
+{% extends "admin_base.html" %}
{% block body %}
-{% extends "base.html" %}
+{% extends "admin_base.html" %}
{% load formatters %}
-{% extends "base.html" %}
+{% extends "admin_base.html" %}
{% load formatters %}
--- /dev/null
+{% extends "base.html" %}
+
+{% load formatters %}
+
+{% block title%}
+ <h2>User Profile</h2>
+{% endblock title%}
+
+{% block body %}
+
+<form action="{% url pithos.im.views.users_edit 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-uniq">Uniq</label>
+ <div class="input">
+ <span class="uneditable-input" id="user-uniq">{{ user.uniq }}</span>
+ </div>
+ </div>
+
+ <div class="clearfix">
+ <label for="user-realname">Real Name</label>
+ <div class="input">
+ <input class="span4" id="user-realname" name="realname" value="{{ user.realname }}" 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_admin %} checked{% endif %} disabled="disabled">
+ </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-state">State</label>
+ <div class="input">
+ <select class="medium" id="user-state" name="state" disabled="disabled">
+ {% for state in states %}
+ <option{% ifequal state user.state %} selected{% endifequal %}>{{ state }}</option>
+ {% endfor %}
+ </select>
+ </div>
+ </div>
+
+ <div class="clearfix">
+ <label for="user-invitations">Invitations</label>
+ <div class="input">
+ <span class="uneditable-input" id="user-invitations">{{ user.invitations }}</span>
+ </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" readonly="readonly"/>
+ <span class="add-on">GiB</span>
+ </div>
+ </div>
+ </div>
+
+ <div class="clearfix">
+ <label for="user-token">Token</label>
+ <div class="input">
+ <span class="uneditable-input" id="user-token">{{ user.auth_token }}</span>
+ </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">
+ <span class="uneditable-input" id="token-expires">{{ user.auth_token_expires }}</span>
+ </div>
+ </div>
+
+ <div class="clearfix">
+ <label for="user-created">Created</label>
+ <div class="input">
+ <span class="uneditable-input" id="user-created">{{ user.created }}</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>
+
+ <div class="actions">
+ <input type="hidden" name="next" value="{{ next }}">
+ <button type="submit" class="btn primary">Verify</button>
+ </div>
+
+</form>
+{% endblock body %}
(r'^admin/invitations/?$', 'invitations_list'),
(r'^admin/invitations/export/?$', 'invitations_export'),
+
+ (r'^profile/(\d+)/?$', 'users_profile'),
+ (r'^profile/(\d+)/edit/?$', 'users_edit'),
)
urlpatterns += patterns('pithos.im.target',
return func(request, *args)
return wrapper
+def requires_my_login(func):
+ @wraps(func)
+ def wrapper(request, *args):
+ print '>', request.user, args
+ if not settings.BYPASS_ADMIN_AUTH:
+ if not request.user:
+ next = urlencode({'next': request.build_absolute_uri()})
+ login_uri = reverse(index) + '?' + next
+ return HttpResponseRedirect(login_uri)
+ else:
+ user = User.objects.get(uniq=request.user)
+ if user.id != int(args[0]):
+ next = urlencode({'next': request.build_absolute_uri()})
+ login_uri = reverse(index) + '?' + next
+ return HttpResponseRedirect(login_uri)
+ return func(request, *args)
+ return wrapper
+
def requires_admin(func):
@wraps(func)
user.renew_token()
user.save()
return redirect(users_info, user.id)
+
+@requires_my_login
+def users_profile(request, user_id):
+ next = request.GET.get('next')
+ user = User.objects.get(id=user_id)
+ states = [x[0] for x in User.ACCOUNT_STATE]
+ return render_response('users_profile.html',
+ user=user,
+ states=states,
+ next=next)
+
+@requires_my_login
+def users_edit(request, user_id):
+ user = User.objects.get(id=user_id)
+ user.realname = request.POST.get('realname')
+ user.affiliation = request.POST.get('affiliation')
+ user.is_verified = True
+ user.save()
+ next = request.POST.get('next')
+ if next:
+ return redirect(next)
+
+ status = 'success'
+ message = _('Profile has been updated')
+ html = render_to_string('users_profile.html', {
+ 'user': user,
+ 'status': status,
+ 'message': message})
+ return HttpResponse(html)
+
'?username=%s' \
'&next=%s'
+# Force user profile verification
+FORCE_PROFILE_UPDATE = False
+
# The server is behind a proy (apache and gunicorn setup).
USE_X_FORWARDED_HOST = False