Revision b4c241e6
b/pithos/im/migrations/0002_auto__add_field_user_is_verified.py | ||
---|---|---|
1 |
# encoding: utf-8 |
|
2 |
import datetime |
|
3 |
from south.db import db |
|
4 |
from south.v2 import SchemaMigration |
|
5 |
from django.db import models |
|
6 |
|
|
7 |
class Migration(SchemaMigration): |
|
8 |
|
|
9 |
def forwards(self, orm): |
|
10 |
|
|
11 |
# Adding field 'User.is_verified' |
|
12 |
db.add_column('im_user', 'is_verified', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) |
|
13 |
|
|
14 |
|
|
15 |
def backwards(self, orm): |
|
16 |
|
|
17 |
# Deleting field 'User.is_verified' |
|
18 |
db.delete_column('im_user', 'is_verified') |
|
19 |
|
|
20 |
|
|
21 |
models = { |
|
22 |
'im.invitation': { |
|
23 |
'Meta': {'object_name': 'Invitation'}, |
|
24 |
'accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
25 |
'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), |
|
26 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
27 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
28 |
'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.User']"}), |
|
29 |
'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
30 |
'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
|
31 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
|
32 |
}, |
|
33 |
'im.user': { |
|
34 |
'Meta': {'object_name': 'User'}, |
|
35 |
'affiliation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
36 |
'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
|
37 |
'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
38 |
'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
39 |
'created': ('django.db.models.fields.DateTimeField', [], {}), |
|
40 |
'email': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
41 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
42 |
'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
43 |
'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
44 |
'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
45 |
'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}), |
|
46 |
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
47 |
'realname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
48 |
'state': ('django.db.models.fields.CharField', [], {'default': "'ACTIVE'", 'max_length': '16'}), |
|
49 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), |
|
50 |
'updated': ('django.db.models.fields.DateTimeField', [], {}) |
|
51 |
} |
|
52 |
} |
|
53 |
|
|
54 |
complete_apps = ['im'] |
b/pithos/im/models.py | ||
---|---|---|
79 | 79 |
created = models.DateTimeField('Creation date') |
80 | 80 |
updated = models.DateTimeField('Update date') |
81 | 81 |
|
82 |
is_verified = models.BooleanField('Verified?', default=False) |
|
83 |
|
|
82 | 84 |
@property |
83 | 85 |
def quota(self): |
84 | 86 |
return get_quota(self.uniq) |
b/pithos/im/target/invitation.py | ||
---|---|---|
37 | 37 |
|
38 | 38 |
from django.conf import settings |
39 | 39 |
from django.http import HttpResponseBadRequest |
40 |
from django.core.urlresolvers import reverse |
|
41 |
from django.utils.http import urlencode |
|
40 | 42 |
|
41 | 43 |
from pithos.im.models import Invitation |
42 | 44 |
from pithos.im.target.util import get_or_create_user, prepare_response |
... | ... | |
59 | 61 |
invitation.realname, |
60 | 62 |
'Invitation', |
61 | 63 |
invitation.inviter.level + 1) |
64 |
|
|
62 | 65 |
next = request.GET.get('next') |
66 |
if settings.FORCE_PROFILE_UPDATE and not user.is_verified: |
|
67 |
profile_url = reverse('pithos.im.views.users_profile', args=(user.id,)) |
|
68 |
next = urlencode({'next': next}) |
|
69 |
profile_url = profile_url + '?' + next |
|
70 |
return prepare_response(request, user, profile_url) |
|
71 |
|
|
63 | 72 |
return prepare_response(request, user, next, 'renew' in request.GET) |
b/pithos/im/target/local.py | ||
---|---|---|
34 | 34 |
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest |
35 | 35 |
from django.conf import settings |
36 | 36 |
from django.template.loader import render_to_string |
37 |
from django.core.urlresolvers import reverse |
|
38 |
from django.utils.http import urlencode |
|
37 | 39 |
|
38 | 40 |
from pithos.im.target.util import prepare_response |
39 | 41 |
from pithos.im.models import User |
... | ... | |
62 | 64 |
return HttpResponseBadRequest('Unverified account') |
63 | 65 |
|
64 | 66 |
next = request.POST.get('next') |
67 |
if settings.FORCE_PROFILE_UPDATE and not user.is_verified: |
|
68 |
profile_url = reverse('pithos.im.views.users_profile', args=(user.id,)) |
|
69 |
next = urlencode({'next': next}) |
|
70 |
profile_url = profile_url + '?' + next |
|
71 |
return prepare_response(request, user, profile_url) |
|
72 |
|
|
65 | 73 |
return prepare_response(request, user, next) |
66 | 74 |
|
67 | 75 |
def activate(request): |
b/pithos/im/target/shibboleth.py | ||
---|---|---|
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 | 34 |
from django.http import HttpResponseBadRequest |
35 |
from django.core.urlresolvers import reverse |
|
36 |
from django.utils.http import urlencode |
|
37 |
from django.conf import settings |
|
35 | 38 |
|
36 | 39 |
from pithos.im.target.util import get_or_create_user, prepare_response |
37 | 40 |
|
... | ... | |
66 | 69 |
|
67 | 70 |
affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, '') |
68 | 71 |
|
72 |
user = get_or_create_user(eppn, realname, affiliation, 0) |
|
73 |
next = request.GET.get('next') |
|
74 |
if settings.FORCE_PROFILE_UPDATE and not user.is_verified: |
|
75 |
profile_url = reverse('pithos.im.views.users_profile', args=(user.id,)) |
|
76 |
next = urlencode({'next': next}) |
|
77 |
profile_url = profile_url + '?' + next |
|
78 |
return prepare_response(request, user, profile_url) |
|
79 |
|
|
69 | 80 |
return prepare_response(request, |
70 |
get_or_create_user(eppn, realname, affiliation, 0),
|
|
71 |
request.GET.get('next'),
|
|
81 |
user,
|
|
82 |
next,
|
|
72 | 83 |
'renew' in request.GET) |
b/pithos/im/target/util.py | ||
---|---|---|
80 | 80 |
# TODO: Avoid redirect loops. |
81 | 81 |
parts = list(urlsplit(next)) |
82 | 82 |
# Do not pass on user and token if we are on the same server. |
83 |
if request.get_host() != parts[1]: |
|
83 |
if parts[1] and request.get_host() != parts[1]:
|
|
84 | 84 |
parts[3] = urlencode({'user': user.uniq, 'token': user.auth_token}) |
85 | 85 |
next = urlunsplit(parts) |
86 | 86 |
|
b/pithos/im/templates/admin.html | ||
---|---|---|
1 |
{% extends "base.html" %} |
|
1 |
{% extends "admin_base.html" %}
|
|
2 | 2 |
|
3 | 3 |
{% block body %} |
4 | 4 |
<ul class="unstyled"> |
b/pithos/im/templates/admin_base.html | ||
---|---|---|
1 |
{% extends "base.html" %} |
|
2 |
|
|
3 |
{% block tabs %} |
|
4 |
<ul class="tabs"> |
|
5 |
<li{% ifequal tab "home" %} class="active"{% endifequal %}> |
|
6 |
<a href="{% url pithos.im.views.admin %}">Home</a> |
|
7 |
</li> |
|
8 |
<li{% ifequal tab "users" %} class="active"{% endifequal %}> |
|
9 |
<a href="{% url pithos.im.views.users_list %}">Users</a> |
|
10 |
</li> |
|
11 |
<li{% ifequal tab "invitations" %} class="active"{% endifequal %}> |
|
12 |
<a href="{% url pithos.im.views.invitations_list %}">Invitations</a> |
|
13 |
</li> |
|
14 |
</ul> |
|
15 |
{% endblock %} |
|
16 |
|
|
17 |
{% block body %}{% endblock %} |
b/pithos/im/templates/base.html | ||
---|---|---|
14 | 14 |
<div style="padding: 5px 0px 0px 0px"> |
15 | 15 |
<img src="/im/static/banner.png" width="900" height="200"> |
16 | 16 |
</div> |
17 |
{% block title %}{% endblock %} |
|
18 |
|
|
19 |
{% if message %} |
|
20 |
<br /> |
|
21 |
<div class="alert-message.{{ status }}"> |
|
22 |
<p>{{ message }}</p> |
|
23 |
</div> |
|
24 |
{% endif %} |
|
17 | 25 |
|
18 |
<ul class="tabs"> |
|
19 |
<li{% ifequal tab "home" %} class="active"{% endifequal %}> |
|
20 |
<a href="{% url pithos.im.views.admin %}">Home</a> |
|
21 |
</li> |
|
22 |
<li{% ifequal tab "users" %} class="active"{% endifequal %}> |
|
23 |
<a href="{% url pithos.im.views.users_list %}">Users</a> |
|
24 |
</li> |
|
25 |
<li{% ifequal tab "invitations" %} class="active"{% endifequal %}> |
|
26 |
<a href="{% url pithos.im.views.invitations_list %}">Invitations</a> |
|
27 |
</li> |
|
28 |
</ul> |
|
26 |
{% block tabs %}{% endblock %} |
|
29 | 27 |
|
30 | 28 |
{% block body %}{% endblock %} |
31 | 29 |
</div> |
b/pithos/im/templates/index.html | ||
---|---|---|
1 |
{% extends 'local_base.html'%}
|
|
1 |
{% extends 'base.html'%} |
|
2 | 2 |
|
3 | 3 |
{% block title%} |
4 | 4 |
<h2>Welcome</h2> |
b/pithos/im/templates/invitations_list.html | ||
---|---|---|
1 |
{% extends "base.html" %} |
|
1 |
{% extends "admin_base.html" %}
|
|
2 | 2 |
|
3 | 3 |
{% load formatters %} |
4 | 4 |
|
/dev/null | ||
---|---|---|
1 |
<!DOCTYPE html> |
|
2 |
<html> |
|
3 |
<head> |
|
4 |
<meta charset="utf-8" /> |
|
5 |
<title>{{ title|default:"User Login" }}</title> |
|
6 |
<link rel="stylesheet" href="/im/static/bootstrap.css"> |
|
7 |
<script src="/im/static/jquery.js"></script> |
|
8 |
<script src="/im/static/jquery.tablesorter.js"></script> |
|
9 |
<script src="/im/static/main.js"></script> |
|
10 |
</head> |
|
11 |
<body> |
|
12 |
<div class="container"> |
|
13 |
<div style="padding: 5px 0px 0px 0px"> |
|
14 |
<img src="/im/static/banner.png" width="900" height="200"> |
|
15 |
</div> |
|
16 |
{% block title%}{% endblock title%} |
|
17 |
|
|
18 |
{% if message %} |
|
19 |
<br /> |
|
20 |
<div class="alert-message.{{ status }}"> |
|
21 |
<p>{{ message }}</p> |
|
22 |
</div> |
|
23 |
{% endif %} |
|
24 |
|
|
25 |
{% block body%}{% endblock %} |
|
26 |
</div> |
|
27 |
</body> |
|
28 |
</html> |
b/pithos/im/templates/local_create.html | ||
---|---|---|
1 |
{% extends "local_base.html" %}
|
|
1 |
{% extends "base.html" %} |
|
2 | 2 |
|
3 | 3 |
{% block title%} |
4 | 4 |
<h2>Sign up</h2> |
b/pithos/im/templates/reclaim.html | ||
---|---|---|
1 |
{% extends "local_base.html" %}
|
|
1 |
{% extends "base.html" %} |
|
2 | 2 |
|
3 | 3 |
{% block title%} |
4 | 4 |
<h2>Reclaim password</h2> |
b/pithos/im/templates/reset.html | ||
---|---|---|
1 |
{% extends "local_base.html" %}
|
|
1 |
{% extends "base.html" %} |
|
2 | 2 |
|
3 | 3 |
{% block title%} |
4 | 4 |
<h2>Reset password</h2> |
b/pithos/im/templates/users_create.html | ||
---|---|---|
1 |
{% extends "base.html" %} |
|
1 |
{% extends "admin_base.html" %}
|
|
2 | 2 |
|
3 | 3 |
{% block body %} |
4 | 4 |
|
b/pithos/im/templates/users_info.html | ||
---|---|---|
1 |
{% extends "base.html" %} |
|
1 |
{% extends "admin_base.html" %}
|
|
2 | 2 |
|
3 | 3 |
{% load formatters %} |
4 | 4 |
|
b/pithos/im/templates/users_list.html | ||
---|---|---|
1 |
{% extends "base.html" %} |
|
1 |
{% extends "admin_base.html" %}
|
|
2 | 2 |
|
3 | 3 |
{% load formatters %} |
4 | 4 |
|
b/pithos/im/templates/users_profile.html | ||
---|---|---|
1 |
{% extends "base.html" %} |
|
2 |
|
|
3 |
{% load formatters %} |
|
4 |
|
|
5 |
{% block title%} |
|
6 |
<h2>User Profile</h2> |
|
7 |
{% endblock title%} |
|
8 |
|
|
9 |
{% block body %} |
|
10 |
|
|
11 |
<form action="{% url pithos.im.views.users_edit user.id %}" method="post"> |
|
12 |
<div class="clearfix"> |
|
13 |
<label for="user-id">ID</label> |
|
14 |
<div class="input"> |
|
15 |
<span class="uneditable-input" id="user-id">{{ user.id }}</span> |
|
16 |
</div> |
|
17 |
</div> |
|
18 |
|
|
19 |
<div class="clearfix"> |
|
20 |
<label for="user-uniq">Uniq</label> |
|
21 |
<div class="input"> |
|
22 |
<span class="uneditable-input" id="user-uniq">{{ user.uniq }}</span> |
|
23 |
</div> |
|
24 |
</div> |
|
25 |
|
|
26 |
<div class="clearfix"> |
|
27 |
<label for="user-realname">Real Name</label> |
|
28 |
<div class="input"> |
|
29 |
<input class="span4" id="user-realname" name="realname" value="{{ user.realname }}" type="text" /> |
|
30 |
</div> |
|
31 |
</div> |
|
32 |
|
|
33 |
<div class="clearfix"> |
|
34 |
<label for="user-admin">Admin</label> |
|
35 |
<div class="input"> |
|
36 |
<ul class="inputs-list"> |
|
37 |
<li> |
|
38 |
<label> |
|
39 |
<input type="checkbox" id="user-admin" name="admin"{% if user.is_admin %} checked{% endif %} disabled="disabled"> |
|
40 |
</label> |
|
41 |
</li> |
|
42 |
</ul> |
|
43 |
</div> |
|
44 |
</div> |
|
45 |
|
|
46 |
<div class="clearfix"> |
|
47 |
<label for="user-affiliation">Affiliation</label> |
|
48 |
<div class="input"> |
|
49 |
<input class="span4" id="user-affiliation" name="affiliation" value="{{ user.affiliation }}" type="text" /> |
|
50 |
</div> |
|
51 |
</div> |
|
52 |
|
|
53 |
<div class="clearfix"> |
|
54 |
<label for="user-state">State</label> |
|
55 |
<div class="input"> |
|
56 |
<select class="medium" id="user-state" name="state" disabled="disabled"> |
|
57 |
{% for state in states %} |
|
58 |
<option{% ifequal state user.state %} selected{% endifequal %}>{{ state }}</option> |
|
59 |
{% endfor %} |
|
60 |
</select> |
|
61 |
</div> |
|
62 |
</div> |
|
63 |
|
|
64 |
<div class="clearfix"> |
|
65 |
<label for="user-invitations">Invitations</label> |
|
66 |
<div class="input"> |
|
67 |
<span class="uneditable-input" id="user-invitations">{{ user.invitations }}</span> |
|
68 |
</div> |
|
69 |
</div> |
|
70 |
|
|
71 |
<div class="clearfix"> |
|
72 |
<label for="user-quota">Quota</label> |
|
73 |
<div class="input"> |
|
74 |
<div class="input-append"> |
|
75 |
<input class="span2" id="user-quota" name="quota" value="{{ user.quota|GiB }}" type="text" readonly="readonly"/> |
|
76 |
<span class="add-on">GiB</span> |
|
77 |
</div> |
|
78 |
</div> |
|
79 |
</div> |
|
80 |
|
|
81 |
<div class="clearfix"> |
|
82 |
<label for="user-token">Token</label> |
|
83 |
<div class="input"> |
|
84 |
<span class="uneditable-input" id="user-token">{{ user.auth_token }}</span> |
|
85 |
</div> |
|
86 |
</div> |
|
87 |
|
|
88 |
<div class="clearfix"> |
|
89 |
<label for="token-created">Token Created</label> |
|
90 |
<div class="input"> |
|
91 |
<span class="uneditable-input" id="token-created">{{ user.auth_token_created }}</span> |
|
92 |
</div> |
|
93 |
</div> |
|
94 |
|
|
95 |
<div class="clearfix"> |
|
96 |
<label for="token-expires">Token Expires</label> |
|
97 |
<div class="input"> |
|
98 |
<span class="uneditable-input" id="token-expires">{{ user.auth_token_expires }}</span> |
|
99 |
</div> |
|
100 |
</div> |
|
101 |
|
|
102 |
<div class="clearfix"> |
|
103 |
<label for="user-created">Created</label> |
|
104 |
<div class="input"> |
|
105 |
<span class="uneditable-input" id="user-created">{{ user.created }}</span> |
|
106 |
</div> |
|
107 |
</div> |
|
108 |
|
|
109 |
<div class="clearfix"> |
|
110 |
<label for="user-updated">Updated</label> |
|
111 |
<div class="input"> |
|
112 |
<span class="uneditable-input" id="user-updated">{{ user.updated }}</span> |
|
113 |
</div> |
|
114 |
</div> |
|
115 |
|
|
116 |
<div class="actions"> |
|
117 |
<input type="hidden" name="next" value="{{ next }}"> |
|
118 |
<button type="submit" class="btn primary">Verify</button> |
|
119 |
</div> |
|
120 |
|
|
121 |
</form> |
|
122 |
{% endblock body %} |
b/pithos/im/urls.py | ||
---|---|---|
50 | 50 |
|
51 | 51 |
(r'^admin/invitations/?$', 'invitations_list'), |
52 | 52 |
(r'^admin/invitations/export/?$', 'invitations_export'), |
53 |
|
|
54 |
(r'^profile/(\d+)/?$', 'users_profile'), |
|
55 |
(r'^profile/(\d+)/edit/?$', 'users_edit'), |
|
53 | 56 |
) |
54 | 57 |
|
55 | 58 |
urlpatterns += patterns('pithos.im.target', |
b/pithos/im/views.py | ||
---|---|---|
76 | 76 |
return func(request, *args) |
77 | 77 |
return wrapper |
78 | 78 |
|
79 |
def requires_my_login(func): |
|
80 |
@wraps(func) |
|
81 |
def wrapper(request, *args): |
|
82 |
print '>', request.user, args |
|
83 |
if not settings.BYPASS_ADMIN_AUTH: |
|
84 |
if not request.user: |
|
85 |
next = urlencode({'next': request.build_absolute_uri()}) |
|
86 |
login_uri = reverse(index) + '?' + next |
|
87 |
return HttpResponseRedirect(login_uri) |
|
88 |
else: |
|
89 |
user = User.objects.get(uniq=request.user) |
|
90 |
if user.id != int(args[0]): |
|
91 |
next = urlencode({'next': request.build_absolute_uri()}) |
|
92 |
login_uri = reverse(index) + '?' + next |
|
93 |
return HttpResponseRedirect(login_uri) |
|
94 |
return func(request, *args) |
|
95 |
return wrapper |
|
96 |
|
|
79 | 97 |
|
80 | 98 |
def requires_admin(func): |
81 | 99 |
@wraps(func) |
... | ... | |
455 | 473 |
user.renew_token() |
456 | 474 |
user.save() |
457 | 475 |
return redirect(users_info, user.id) |
476 |
|
|
477 |
@requires_my_login |
|
478 |
def users_profile(request, user_id): |
|
479 |
next = request.GET.get('next') |
|
480 |
user = User.objects.get(id=user_id) |
|
481 |
states = [x[0] for x in User.ACCOUNT_STATE] |
|
482 |
return render_response('users_profile.html', |
|
483 |
user=user, |
|
484 |
states=states, |
|
485 |
next=next) |
|
486 |
|
|
487 |
@requires_my_login |
|
488 |
def users_edit(request, user_id): |
|
489 |
user = User.objects.get(id=user_id) |
|
490 |
user.realname = request.POST.get('realname') |
|
491 |
user.affiliation = request.POST.get('affiliation') |
|
492 |
user.is_verified = True |
|
493 |
user.save() |
|
494 |
next = request.POST.get('next') |
|
495 |
if next: |
|
496 |
return redirect(next) |
|
497 |
|
|
498 |
status = 'success' |
|
499 |
message = _('Profile has been updated') |
|
500 |
html = render_to_string('users_profile.html', { |
|
501 |
'user': user, |
|
502 |
'status': status, |
|
503 |
'message': message}) |
|
504 |
return HttpResponse(html) |
|
505 |
|
b/pithos/settings.py.dist | ||
---|---|---|
204 | 204 |
'?username=%s' \ |
205 | 205 |
'&next=%s' |
206 | 206 |
|
207 |
# Force user profile verification |
|
208 |
FORCE_PROFILE_UPDATE = False |
|
209 |
|
|
207 | 210 |
# The server is behind a proy (apache and gunicorn setup). |
208 | 211 |
USE_X_FORWARDED_HOST = False |
209 | 212 |
|
Also available in: Unified diff