Revision 64cd4730
b/.gitignore | ||
---|---|---|
1 |
docs/build |
|
2 |
*.db |
|
3 |
*.pyc |
|
4 |
.DS_Store |
b/LICENSE | ||
---|---|---|
1 |
Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
|
|
3 |
Redistribution and use in source and binary forms, with or |
|
4 |
without modification, are permitted provided that the following |
|
5 |
conditions are met: |
|
6 |
|
|
7 |
1. Redistributions of source code must retain the above |
|
8 |
copyright notice, this list of conditions and the following |
|
9 |
disclaimer. |
|
10 |
|
|
11 |
2. Redistributions in binary form must reproduce the above |
|
12 |
copyright notice, this list of conditions and the following |
|
13 |
disclaimer in the documentation and/or other materials |
|
14 |
provided with the distribution. |
|
15 |
|
|
16 |
THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
POSSIBILITY OF SUCH DAMAGE. |
|
28 |
|
|
29 |
The views and conclusions contained in the software and |
|
30 |
documentation are those of the authors and should not be |
|
31 |
interpreted as representing official policies, either expressed |
|
32 |
or implied, of GRNET S.A. |
b/MANIFEST.in | ||
---|---|---|
1 |
global-include */templates/* */fixtures/* */static/* |
|
2 |
global-exclude */.DS_Store |
|
3 |
include astakos/settings.d/* |
|
4 |
prune docs |
|
5 |
prune other |
b/README | ||
---|---|---|
1 |
README |
|
2 |
====== |
|
3 |
|
|
4 |
Astakos is an identity management service, built by GRNET using Django (https://www.djangoproject.com/). |
|
5 |
Learn more about Astakos at: http://code.grnet.gr/projects/astakos |
|
6 |
Consult LICENSE for licensing information. |
|
7 |
|
|
8 |
Documentation |
|
9 |
------------- |
|
10 |
|
|
11 |
All docs are in the docs/source directory. The .rst files are perfectly readable in source form. |
|
12 |
|
|
13 |
To build the documentation you need to have Sphinx (http://sphinx.pocoo.org/) installed. |
|
14 |
|
|
15 |
On a typical debian-based Linux system run: |
|
16 |
apt-get install python-django python-django-south python-setuptools python-sphinx python-httplib2 |
|
17 |
|
|
18 |
Then run: |
|
19 |
python setup.py build_sphinx |
|
20 |
|
|
21 |
The documentation will be built in the docs/build/html directory. |
|
22 |
|
|
23 |
Also run: |
|
24 |
python setup.py build_sphinx -b text |
|
25 |
|
|
26 |
Then find the plain text version of the docs in docs/build/text. |
|
27 |
|
|
28 |
Running the server |
|
29 |
------------------ |
|
30 |
|
|
31 |
Make sure you have all required packages installed: |
|
32 |
apt-get install python-django python-setuptools python-sphinx python-httplib2 |
|
33 |
|
|
34 |
Then run: |
|
35 |
python manage.py syncdb |
|
36 |
python manage.py migrate im |
|
37 |
python manage.py loaddata admin_user |
|
38 |
python manage.py runserver |
|
39 |
|
|
40 |
Go to: |
|
41 |
http://127.0.0.1:8000/im/admin?user=admin&token=0000 |
|
42 |
|
|
43 |
This server is useful during development, but should not be used for deployment. |
|
44 |
To deploy Astakos using Apache, take a look at the Administrator Guide in docs. |
b/README.upgrade | ||
---|---|---|
1 |
UPGRADE |
|
2 |
======= |
|
3 |
|
|
4 |
0.7.9 -> 0.7.10 |
|
5 |
--------------- |
|
6 |
* Update settings.py (BACKEND_*, SERVICE_NAME, *_EMAIL, *_TARGET, IM_*) |
|
7 |
* Update 'attributes' table in mysql (backend): |
|
8 |
|
|
9 |
mysql> update attributes set `key`='ETag' where `key`='hash'; |
|
10 |
|
|
11 |
* Upgrade 'im_user' table (im app): |
|
12 |
|
|
13 |
ALTER TABLE im_user ADD COLUMN 'password' VARCHAR(255); |
|
14 |
|
|
15 |
0.7.10 -> 0.8.0 |
|
16 |
--------------- |
|
17 |
* Upgrade 'public' table in mysql (backend): |
|
18 |
* Run: mysqldump pithosdb public > public-table.sql |
|
19 |
* mysql> drop table public; |
|
20 |
* Update the codebase and run the server so the new public table is created |
|
21 |
* From the sql dump above, take the row: |
|
22 |
|
|
23 |
INSERT INTO `public` VALUES (...); |
|
24 |
|
|
25 |
Rewrite as: |
|
26 |
|
|
27 |
INSERT INTO `public`(`path`) VALUES (...); |
|
28 |
|
|
29 |
And execute in the database |
|
30 |
* Create settings.local with local setting overrides |
|
31 |
* Install python-django-south |
|
32 |
* Setup south: |
|
33 |
python manage.py syncdb |
|
34 |
python manage.py migrate im 0001 --fake |
|
35 |
python manage.py migrate im |
|
36 |
|
|
37 |
0.8.0 -> 0.8.1 |
|
38 |
-------------- |
|
39 |
* Reset 'policy' table in mysql (backend): |
|
40 |
|
|
41 |
mysql> update policy set `value`='auto' where `key`='versioning'; |
|
42 |
|
|
43 |
0.8.1 -> 0.8.2 |
|
44 |
-------------- |
|
45 |
* Add the 'X-Forwarded-Protocol' header directive in the apache configuration, as described in the admin guide |
|
46 |
* Update 'attributes' table in mysql (backend): |
|
47 |
|
|
48 |
mysql> CREATE TABLE `attributes_new` ( |
|
49 |
`serial` int(11) NOT NULL, |
|
50 |
`domain` varchar(255) COLLATE utf8_bin NOT NULL, |
|
51 |
`key` varchar(255) COLLATE utf8_bin NOT NULL, |
|
52 |
`value` varchar(255) COLLATE utf8_bin DEFAULT NULL, |
|
53 |
PRIMARY KEY (`serial`,`domain`,`key`), |
|
54 |
CONSTRAINT FOREIGN KEY (`serial`) REFERENCES `versions` (`serial`) ON DELETE CASCADE ON UPDATE CASCADE |
|
55 |
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; |
|
56 |
mysql> insert into attributes_new select `serial`, 'pithos', `key`, `value` from attributes; |
|
57 |
mysql> drop table attributes; |
|
58 |
mysql> alter table attributes_new rename to attributes; |
|
59 |
|
|
60 |
* Update 'versions' table in mysql (backend): |
|
61 |
|
|
62 |
mysql> create temporary table tmp_uuids as select distinct node, uuid() as `uuid` from versions; |
|
63 |
mysql> alter table versions add column `uuid` varchar(64) DEFAULT '' NOT NULL after `muser`; |
|
64 |
mysql> update versions v, tmp_uuids u set v.`uuid` = u.`uuid` where v.`node` = u.`node`; |
|
65 |
mysql> create index idx_versions_node_uuid on versions(uuid); |
b/astakos/__init__.py | ||
---|---|---|
1 |
# Copyright (c) Django Software Foundation and individual contributors. |
|
2 |
# All rights reserved. |
|
3 |
# |
|
4 |
# Redistribution and use in source and binary forms, with or without modification, |
|
5 |
# are permitted provided that the following conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above copyright notice, |
|
8 |
# this list of conditions and the following disclaimer. |
|
9 |
# |
|
10 |
# 2. Redistributions in binary form must reproduce the above copyright |
|
11 |
# notice, this list of conditions and the following disclaimer in the |
|
12 |
# documentation and/or other materials provided with the distribution. |
|
13 |
# |
|
14 |
# 3. Neither the name of Django nor the names of its contributors may be used |
|
15 |
# to endorse or promote products derived from this software without |
|
16 |
# specific prior written permission. |
|
17 |
# |
|
18 |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
19 |
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
20 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
21 |
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
|
22 |
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
23 |
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
24 |
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|
25 |
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
26 |
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
27 |
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
28 |
|
|
29 |
VERSION = (0, 1, 0, 'alpha', 0) |
|
30 |
|
|
31 |
def get_version(): |
|
32 |
version = '%s.%s' % (VERSION[0], VERSION[1]) |
|
33 |
if VERSION[2]: |
|
34 |
version = '%s.%s' % (version, VERSION[2]) |
|
35 |
if VERSION[3:] == ('alpha', 0): |
|
36 |
version = '%s pre-alpha' % version |
|
37 |
else: |
|
38 |
if VERSION[3] != 'final': |
|
39 |
version = '%s %s %s' % (version, VERSION[3], VERSION[4]) |
|
40 |
return version |
b/astakos/im/api.py | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
from traceback import format_exc |
|
35 |
from time import time, mktime |
|
36 |
from django.conf import settings |
|
37 |
from django.http import HttpResponse |
|
38 |
from django.utils import simplejson as json |
|
39 |
|
|
40 |
from astakos.im.faults import BadRequest, Unauthorized, ServiceUnavailable |
|
41 |
from astakos.im.models import User |
|
42 |
|
|
43 |
import datetime |
|
44 |
|
|
45 |
def render_fault(request, fault): |
|
46 |
if settings.DEBUG or settings.TEST: |
|
47 |
fault.details = format_exc(fault) |
|
48 |
|
|
49 |
request.serialization = 'text' |
|
50 |
data = '\n'.join((fault.message, fault.details)) + '\n' |
|
51 |
response = HttpResponse(data, status=fault.code) |
|
52 |
return response |
|
53 |
|
|
54 |
def update_response_headers(response): |
|
55 |
response['Content-Type'] = 'application/json; charset=UTF-8' |
|
56 |
response['Content-Length'] = len(response.content) |
|
57 |
|
|
58 |
def authenticate(request): |
|
59 |
# Normal Response Codes: 204 |
|
60 |
# Error Response Codes: serviceUnavailable (503) |
|
61 |
# badRequest (400) |
|
62 |
# unauthorised (401) |
|
63 |
try: |
|
64 |
if request.method != 'GET': |
|
65 |
raise BadRequest('Method not allowed.') |
|
66 |
x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN') |
|
67 |
if not x_auth_token: |
|
68 |
return render_fault(request, BadRequest('Missing X-Auth-Token')) |
|
69 |
|
|
70 |
try: |
|
71 |
user = User.objects.get(auth_token=x_auth_token) |
|
72 |
except User.DoesNotExist, e: |
|
73 |
return render_fault(request, Unauthorized('Invalid X-Auth-Token')) |
|
74 |
|
|
75 |
# Check if the is active. |
|
76 |
if user.state != 'ACTIVE': |
|
77 |
return render_fault(request, Unauthorized('User inactive')) |
|
78 |
|
|
79 |
# Check if the token has expired. |
|
80 |
if (time() - mktime(user.auth_token_expires.timetuple())) > 0: |
|
81 |
return render_fault(request, Unauthorized('Authentication expired')) |
|
82 |
|
|
83 |
response = HttpResponse() |
|
84 |
response.status=204 |
|
85 |
user_info = user.__dict__ |
|
86 |
for k,v in user_info.items(): |
|
87 |
if isinstance(v, datetime.datetime): |
|
88 |
user_info[k] = v.strftime('%a, %d-%b-%Y %H:%M:%S %Z') |
|
89 |
user_info.pop('_state') |
|
90 |
response.content = json.dumps(user_info) |
|
91 |
update_response_headers(response) |
|
92 |
return response |
|
93 |
except BaseException, e: |
|
94 |
fault = ServiceUnavailable('Unexpected error') |
|
95 |
return render_fault(request, fault) |
b/astakos/im/faults.py | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
def camelCase(s): |
|
35 |
return s[0].lower() + s[1:] |
|
36 |
|
|
37 |
class Fault(Exception): |
|
38 |
def __init__(self, message='', details='', name=''): |
|
39 |
Exception.__init__(self, message, details, name) |
|
40 |
self.message = message |
|
41 |
self.details = details |
|
42 |
self.name = name or camelCase(self.__class__.__name__) |
|
43 |
|
|
44 |
class BadRequest(Fault): |
|
45 |
code = 400 |
|
46 |
|
|
47 |
class Unauthorized(Fault): |
|
48 |
code = 401 |
|
49 |
|
|
50 |
class ServiceUnavailable(Fault): |
|
51 |
code = 503 |
b/astakos/im/fixtures/admin_user.json | ||
---|---|---|
1 |
[ |
|
2 |
{ |
|
3 |
"model": "im.User", |
|
4 |
"pk": 1, |
|
5 |
"fields": { |
|
6 |
"uniq": "admin", |
|
7 |
"password": "admin", |
|
8 |
"level": 0, |
|
9 |
"state": ACTIVE, |
|
10 |
"invitations": 10000, |
|
11 |
"is_admin": true, |
|
12 |
"auth_token": "0000", |
|
13 |
"auth_token_created": "2011-09-11 09:17:14", |
|
14 |
"auth_token_expires": "2012-09-11 09:17:14", |
|
15 |
"created": "2011-09-11", |
|
16 |
"updated": "2011-09-11" |
|
17 |
} |
|
18 |
} |
|
19 |
] |
b/astakos/im/fixtures/auth_test_data.json | ||
---|---|---|
1 |
[ |
|
2 |
{ |
|
3 |
"model": "im.User", |
|
4 |
"pk": 1, |
|
5 |
"fields": { |
|
6 |
"uniq": "test", |
|
7 |
"level": 0, |
|
8 |
"state": "ACTIVE", |
|
9 |
"invitations": 10000, |
|
10 |
"auth_token": "0000", |
|
11 |
"auth_token_created": "2011-04-07 09:17:14", |
|
12 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
13 |
"created": "2011-02-06", |
|
14 |
"updated": "2011-02-06" |
|
15 |
} |
|
16 |
}, |
|
17 |
{ |
|
18 |
"model": "im.User", |
|
19 |
"pk": 2, |
|
20 |
"fields": { |
|
21 |
"uniq": "verigak", |
|
22 |
"level": 1, |
|
23 |
"state": "ACTIVE", |
|
24 |
"invitations": 3, |
|
25 |
"is_admin": 1, |
|
26 |
"auth_token": "0001", |
|
27 |
"auth_token_created": "2011-04-07 09:17:14", |
|
28 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
29 |
"created": "2011-02-06", |
|
30 |
"updated": "2011-02-06" |
|
31 |
} |
|
32 |
}, |
|
33 |
{ |
|
34 |
"model": "im.User", |
|
35 |
"pk": 3, |
|
36 |
"fields": { |
|
37 |
"uniq": "chazapis", |
|
38 |
"level": 1, |
|
39 |
"state": "ACTIVE", |
|
40 |
"invitations": 3, |
|
41 |
"auth_token": "0002", |
|
42 |
"auth_token_created": "2011-04-07 09:17:14", |
|
43 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
44 |
"created": "2011-02-06", |
|
45 |
"updated": "2011-02-06" |
|
46 |
} |
|
47 |
}, |
|
48 |
{ |
|
49 |
"model": "im.User", |
|
50 |
"pk": 4, |
|
51 |
"fields": { |
|
52 |
"uniq": "gtsouk", |
|
53 |
"level": 1, |
|
54 |
"state": "ACTIVE", |
|
55 |
"invitations": 3, |
|
56 |
"auth_token": "0003", |
|
57 |
"auth_token_created": "2011-04-07 09:17:14", |
|
58 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
59 |
"created": "2011-02-06", |
|
60 |
"updated": "2011-02-06" |
|
61 |
} |
|
62 |
}, |
|
63 |
{ |
|
64 |
"model": "im.User", |
|
65 |
"pk": 5, |
|
66 |
"fields": { |
|
67 |
"uniq": "papagian", |
|
68 |
"level": 1, |
|
69 |
"state": "ACTIVE", |
|
70 |
"invitations": 3, |
|
71 |
"auth_token": "0004", |
|
72 |
"auth_token_created": "2011-04-07 09:17:14", |
|
73 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
74 |
"created": "2011-02-06", |
|
75 |
"updated": "2011-02-06" |
|
76 |
} |
|
77 |
}, |
|
78 |
{ |
|
79 |
"model": "im.User", |
|
80 |
"pk": 6, |
|
81 |
"fields": { |
|
82 |
"uniq": "louridas", |
|
83 |
"level": 1, |
|
84 |
"state": "ACTIVE", |
|
85 |
"invitations": 3, |
|
86 |
"auth_token": "0005", |
|
87 |
"auth_token_created": "2011-04-07 09:17:14", |
|
88 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
89 |
"created": "2011-02-06", |
|
90 |
"updated": "2011-02-06" |
|
91 |
} |
|
92 |
}, |
|
93 |
{ |
|
94 |
"model": "im.User", |
|
95 |
"pk": 7, |
|
96 |
"fields": { |
|
97 |
"uniq": "chstath", |
|
98 |
"level": 1, |
|
99 |
"state": "ACTIVE", |
|
100 |
"invitations": 3, |
|
101 |
"auth_token": "0006", |
|
102 |
"auth_token_created": "2011-04-07 09:17:14", |
|
103 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
104 |
"created": "2011-02-06", |
|
105 |
"updated": "2011-02-06" |
|
106 |
} |
|
107 |
}, |
|
108 |
{ |
|
109 |
"model": "im.User", |
|
110 |
"pk": 8, |
|
111 |
"fields": { |
|
112 |
"uniq": "pkanavos", |
|
113 |
"level": 1, |
|
114 |
"state": "ACTIVE", |
|
115 |
"invitations": 3, |
|
116 |
"auth_token": "0007", |
|
117 |
"auth_token_created": "2011-04-07 09:17:14", |
|
118 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
119 |
"created": "2011-02-06", |
|
120 |
"updated": "2011-02-06" |
|
121 |
} |
|
122 |
}, |
|
123 |
{ |
|
124 |
"model": "im.User", |
|
125 |
"pk": 9, |
|
126 |
"fields": { |
|
127 |
"uniq": "mvasilak", |
|
128 |
"level": 1, |
|
129 |
"state": "ACTIVE", |
|
130 |
"invitations": 3, |
|
131 |
"auth_token": "0008", |
|
132 |
"auth_token_created": "2011-04-07 09:17:14", |
|
133 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
134 |
"created": "2011-02-06", |
|
135 |
"updated": "2011-02-06" |
|
136 |
} |
|
137 |
}, |
|
138 |
{ |
|
139 |
"model": "im.User", |
|
140 |
"pk": 10, |
|
141 |
"fields": { |
|
142 |
"uniq": "ฮดฮนฮฟฮณฮญฮฝฮทฯ", |
|
143 |
"level": 2, |
|
144 |
"state": "ACTIVE", |
|
145 |
"invitations": 2, |
|
146 |
"auth_token": "0009", |
|
147 |
"auth_token_created": "2011-04-07 09:17:14", |
|
148 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
149 |
"created": "2011-02-06", |
|
150 |
"updated": "2011-02-06" |
|
151 |
} |
|
152 |
} |
|
153 |
] |
b/astakos/im/forms.py | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
from django import forms |
|
35 |
from django.utils.translation import ugettext as _ |
|
36 |
from django.conf import settings |
|
37 |
|
|
38 |
from astakos.im.models import User |
|
39 |
|
|
40 |
openid_providers = ( |
|
41 |
('Google','https://www.google.com/accounts/o8/id'), |
|
42 |
('Yahoo', 'http://yahoo.com/'), |
|
43 |
('AOL','http://openid.aol.com/%s/'), |
|
44 |
('OpenID', None), |
|
45 |
('MyOpenID','http://%s.myopenid.com/'), |
|
46 |
('LiveJournal', 'http://%s.livejournal.com/'), |
|
47 |
('Flickr', 'http://flickr.com/%s/'), |
|
48 |
('Technorati', 'http://technorati.com/people/technorati/%s/'), |
|
49 |
('Wordpress', 'http://%s.wordpress.com/'), |
|
50 |
('Blogger', 'http://%s.blogspot.com/'), |
|
51 |
('Verisign', 'http://%s.pip.verisignlabs.com/'), |
|
52 |
('Vidoop', 'http://%s.myvidoop.com/'), |
|
53 |
('ClaimID','http://claimid.com/%s') |
|
54 |
) |
|
55 |
|
|
56 |
class RegisterForm(forms.Form): |
|
57 |
uniq = forms.CharField(widget=forms.widgets.TextInput()) |
|
58 |
provider = forms.CharField(widget=forms.TextInput(), |
|
59 |
label=u'Identity Provider') |
|
60 |
email = forms.EmailField(widget=forms.TextInput(), |
|
61 |
label=_('Email address')) |
|
62 |
realname = forms.CharField(widget=forms.TextInput(), |
|
63 |
label=u'Real Name') |
|
64 |
|
|
65 |
def __init__(self, *args, **kwargs): |
|
66 |
super(forms.Form, self).__init__(*args, **kwargs) |
|
67 |
|
|
68 |
#set readonly form fields |
|
69 |
self.fields['provider'].widget.attrs['readonly'] = True |
|
70 |
|
|
71 |
def clean_uniq(self): |
|
72 |
""" |
|
73 |
Validate that the uniq is alphanumeric and is not already |
|
74 |
in use. |
|
75 |
|
|
76 |
""" |
|
77 |
try: |
|
78 |
user = User.objects.get(uniq__iexact=self.cleaned_data['uniq']) |
|
79 |
except User.DoesNotExist: |
|
80 |
return self.cleaned_data['uniq'] |
|
81 |
raise forms.ValidationError(_("A user with that uniq already exists.")) |
|
82 |
|
|
83 |
class ShibbolethRegisterForm(RegisterForm): |
|
84 |
pass |
|
85 |
|
|
86 |
class TwitterRegisterForm(RegisterForm): |
|
87 |
pass |
|
88 |
|
|
89 |
class OpenidRegisterForm(RegisterForm): |
|
90 |
openidurl = forms.ChoiceField(widget=forms.Select, |
|
91 |
choices=((url, l) for l, url in openid_providers)) |
|
92 |
|
|
93 |
class LocalRegisterForm(RegisterForm): |
|
94 |
""" local signup form""" |
|
95 |
password = forms.CharField(widget=forms.PasswordInput(render_value=False), |
|
96 |
label=_('Password')) |
|
97 |
password2 = forms.CharField(widget=forms.PasswordInput(render_value=False), |
|
98 |
label=_('Confirm Password')) |
|
99 |
|
|
100 |
def __init__(self, *args, **kwargs): |
|
101 |
super(LocalRegisterForm, self).__init__(*args, **kwargs) |
|
102 |
|
|
103 |
def clean_uniq(self): |
|
104 |
""" |
|
105 |
Validate that the uniq is alphanumeric and is not already |
|
106 |
in use. |
|
107 |
|
|
108 |
""" |
|
109 |
try: |
|
110 |
user = User.objects.get(uniq__iexact=self.cleaned_data['uniq']) |
|
111 |
except User.DoesNotExist: |
|
112 |
return self.cleaned_data['uniq'] |
|
113 |
raise forms.ValidationError(_("A user with that uniq already exists.")) |
|
114 |
|
|
115 |
def clean(self): |
|
116 |
""" |
|
117 |
Verifiy that the values entered into the two password fields |
|
118 |
match. Note that an error here will end up in |
|
119 |
``non_field_errors()`` because it doesn't apply to a single |
|
120 |
field. |
|
121 |
|
|
122 |
""" |
|
123 |
if 'password' in self.cleaned_data and 'password2' in self.cleaned_data: |
|
124 |
if self.cleaned_data['password'] != self.cleaned_data['password2']: |
|
125 |
raise forms.ValidationError(_("The two password fields didn't match.")) |
|
126 |
return self.cleaned_data |
|
127 |
|
|
128 |
class InvitedRegisterForm(RegisterForm): |
|
129 |
inviter = forms.CharField(widget=forms.TextInput(), |
|
130 |
label=_('Inviter Real Name')) |
|
131 |
|
|
132 |
def __init__(self, *args, **kwargs): |
|
133 |
super(RegisterForm, self).__init__(*args, **kwargs) |
|
134 |
|
|
135 |
#set readonly form fields |
|
136 |
self.fields['uniq'].widget.attrs['readonly'] = True |
|
137 |
self.fields['inviter'].widget.attrs['readonly'] = True |
|
138 |
self.fields['provider'].widget.attrs['provider'] = True |
|
139 |
|
|
140 |
class InvitedLocalRegisterForm(LocalRegisterForm, InvitedRegisterForm): |
|
141 |
pass |
|
142 |
|
|
143 |
class InvitedOpenidRegisterForm(OpenidRegisterForm, InvitedRegisterForm): |
|
144 |
pass |
|
145 |
|
|
146 |
class InvitedTwitterRegisterForm(TwitterRegisterForm, InvitedRegisterForm): |
|
147 |
pass |
|
148 |
|
|
149 |
class InvitedShibbolethRegisterForm(ShibbolethRegisterForm, InvitedRegisterForm): |
|
150 |
pass |
b/astakos/im/interface.py | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
from pithos.backends import connect_backend |
|
35 |
|
|
36 |
def get_quota(user): |
|
37 |
backend = connect_backend() |
|
38 |
quota = backend.get_account_policy(user, user)['quota'] |
|
39 |
backend.close() |
|
40 |
return quota |
|
41 |
|
|
42 |
def set_quota(user, quota): |
|
43 |
backend = connect_backend() |
|
44 |
backend.update_account_policy(user, user, {'quota': quota}) |
|
45 |
backend.close() |
|
46 |
return quota |
b/astakos/im/migrations/0001_initial.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 model 'User' |
|
12 |
db.create_table('im_user', ( |
|
13 |
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
14 |
('uniq', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)), |
|
15 |
('realname', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
16 |
('email', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
17 |
('affiliation', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
18 |
('state', self.gf('django.db.models.fields.CharField')(default='ACTIVE', max_length=16)), |
|
19 |
('level', self.gf('django.db.models.fields.IntegerField')(default=4)), |
|
20 |
('invitations', self.gf('django.db.models.fields.IntegerField')(default=0)), |
|
21 |
('password', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), |
|
22 |
('is_admin', self.gf('django.db.models.fields.BooleanField')(default=False)), |
|
23 |
('auth_token', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
|
24 |
('auth_token_created', self.gf('django.db.models.fields.DateTimeField')(null=True)), |
|
25 |
('auth_token_expires', self.gf('django.db.models.fields.DateTimeField')(null=True)), |
|
26 |
('created', self.gf('django.db.models.fields.DateTimeField')()), |
|
27 |
('updated', self.gf('django.db.models.fields.DateTimeField')()), |
|
28 |
)) |
|
29 |
db.send_create_signal('im', ['User']) |
|
30 |
|
|
31 |
# Adding model 'Invitation' |
|
32 |
db.create_table('im_invitation', ( |
|
33 |
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
34 |
('inviter', self.gf('django.db.models.fields.related.ForeignKey')(related_name='invitations_sent', null=True, to=orm['im.User'])), |
|
35 |
('realname', self.gf('django.db.models.fields.CharField')(max_length=255)), |
|
36 |
('uniq', self.gf('django.db.models.fields.CharField')(max_length=255)), |
|
37 |
('code', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)), |
|
38 |
('is_accepted', self.gf('django.db.models.fields.BooleanField')(default=False)), |
|
39 |
('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), |
|
40 |
('accepted', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), |
|
41 |
)) |
|
42 |
db.send_create_signal('im', ['Invitation']) |
|
43 |
|
|
44 |
|
|
45 |
def backwards(self, orm): |
|
46 |
|
|
47 |
# Deleting model 'User' |
|
48 |
db.delete_table('im_user') |
|
49 |
|
|
50 |
# Deleting model 'Invitation' |
|
51 |
db.delete_table('im_invitation') |
|
52 |
|
|
53 |
|
|
54 |
models = { |
|
55 |
'im.invitation': { |
|
56 |
'Meta': {'object_name': 'Invitation'}, |
|
57 |
'accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
58 |
'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), |
|
59 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
60 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
61 |
'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.User']"}), |
|
62 |
'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
63 |
'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
|
64 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
|
65 |
}, |
|
66 |
'im.user': { |
|
67 |
'Meta': {'object_name': 'User'}, |
|
68 |
'affiliation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
69 |
'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
|
70 |
'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
71 |
'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
72 |
'created': ('django.db.models.fields.DateTimeField', [], {}), |
|
73 |
'email': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
74 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
75 |
'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
76 |
'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
77 |
'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}), |
|
78 |
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
79 |
'realname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
80 |
'state': ('django.db.models.fields.CharField', [], {'default': "'ACTIVE'", 'max_length': '16'}), |
|
81 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), |
|
82 |
'updated': ('django.db.models.fields.DateTimeField', [], {}) |
|
83 |
} |
|
84 |
} |
|
85 |
|
|
86 |
complete_apps = ['im'] |
b/astakos/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/astakos/im/migrations/0003_auto__add_field_user_provider__add_field_user_openidurl__add_field_inv.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.provider' |
|
12 |
db.add_column('im_user', 'provider', self.gf('django.db.models.fields.CharField')(default='', max_length=255), keep_default=False) |
|
13 |
|
|
14 |
# Adding field 'User.openidurl' |
|
15 |
db.add_column('im_user', 'openidurl', self.gf('django.db.models.fields.CharField')(default='', max_length=255), keep_default=False) |
|
16 |
|
|
17 |
# Adding field 'Invitation.is_consumed' |
|
18 |
db.add_column('im_invitation', 'is_consumed', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) |
|
19 |
|
|
20 |
# Adding field 'Invitation.consumed' |
|
21 |
db.add_column('im_invitation', 'consumed', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), keep_default=False) |
|
22 |
|
|
23 |
|
|
24 |
def backwards(self, orm): |
|
25 |
|
|
26 |
# Deleting field 'User.provider' |
|
27 |
db.delete_column('im_user', 'provider') |
|
28 |
|
|
29 |
# Deleting field 'User.openidurl' |
|
30 |
db.delete_column('im_user', 'openidurl') |
|
31 |
|
|
32 |
# Deleting field 'Invitation.is_consumed' |
|
33 |
db.delete_column('im_invitation', 'is_consumed') |
|
34 |
|
|
35 |
# Deleting field 'Invitation.consumed' |
|
36 |
db.delete_column('im_invitation', 'consumed') |
|
37 |
|
|
38 |
|
|
39 |
models = { |
|
40 |
'im.invitation': { |
|
41 |
'Meta': {'object_name': 'Invitation'}, |
|
42 |
'accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
43 |
'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), |
|
44 |
'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
|
45 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
46 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
47 |
'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.User']"}), |
|
48 |
'is_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
49 |
'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
50 |
'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
|
51 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
|
52 |
}, |
|
53 |
'im.user': { |
|
54 |
'Meta': {'object_name': 'User'}, |
|
55 |
'affiliation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
56 |
'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
|
57 |
'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
58 |
'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), |
|
59 |
'created': ('django.db.models.fields.DateTimeField', [], {}), |
|
60 |
'email': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
61 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
62 |
'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
63 |
'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
64 |
'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
65 |
'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}), |
|
66 |
'openidurl': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
67 |
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
68 |
'provider': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
69 |
'realname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), |
|
70 |
'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '16'}), |
|
71 |
'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), |
|
72 |
'updated': ('django.db.models.fields.DateTimeField', [], {}) |
|
73 |
} |
|
74 |
} |
|
75 |
|
|
76 |
complete_apps = ['im'] |
b/astakos/im/models.py | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or |
|
4 |
# without modification, are permitted provided that the following |
|
5 |
# conditions are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above |
|
8 |
# copyright notice, this list of conditions and the following |
|
9 |
# disclaimer. |
|
10 |
# |
|
11 |
# 2. Redistributions in binary form must reproduce the above |
|
12 |
# copyright notice, this list of conditions and the following |
|
13 |
# disclaimer in the documentation and/or other materials |
|
14 |
# provided with the distribution. |
|
15 |
# |
|
16 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
17 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
18 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
19 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
20 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
21 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
22 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
23 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
25 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
26 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
27 |
# POSSIBILITY OF SUCH DAMAGE. |
|
28 |
# |
|
29 |
# The views and conclusions contained in the software and |
|
30 |
# documentation are those of the authors and should not be |
|
31 |
# interpreted as representing official policies, either expressed |
|
32 |
# or implied, of GRNET S.A. |
|
33 |
|
|
34 |
import logging |
|
35 |
import hashlib |
|
36 |
|
|
37 |
from time import asctime |
|
38 |
from datetime import datetime, timedelta |
|
39 |
from base64 import b64encode |
|
40 |
|
|
41 |
from django.conf import settings |
|
42 |
from django.db import models |
|
43 |
|
|
44 |
from astakos.im.interface import get_quota, set_quota |
|
45 |
|
|
46 |
from hashlib import new as newhasher |
|
47 |
|
|
48 |
class User(models.Model): |
|
49 |
ACCOUNT_STATE = ( |
|
50 |
('ACTIVE', 'Active'), |
|
51 |
('DELETED', 'Deleted'), |
|
52 |
('SUSPENDED', 'Suspended'), |
|
53 |
('UNVERIFIED', 'Unverified'), |
|
54 |
('PENDING', 'Pending') |
|
55 |
) |
|
56 |
|
|
57 |
uniq = models.CharField('Unique ID', max_length=255, null=True) |
|
58 |
|
|
59 |
realname = models.CharField('Real Name', max_length=255, default='') |
|
60 |
email = models.CharField('Email', max_length=255, default='') |
|
61 |
affiliation = models.CharField('Affiliation', max_length=255, default='') |
|
62 |
provider = models.CharField('Provider', max_length=255, default='') |
|
63 |
state = models.CharField('Account state', choices=ACCOUNT_STATE, |
|
64 |
max_length=16, default='PENDING') |
|
65 |
|
|
66 |
#for invitations |
|
67 |
level = models.IntegerField('Inviter level', default=4) |
|
68 |
invitations = models.IntegerField('Invitations left', default=0) |
|
69 |
|
|
70 |
#for local |
|
71 |
password = models.CharField('Password', max_length=255, default='') |
|
72 |
|
|
73 |
is_admin = models.BooleanField('Admin?', default=False) |
|
74 |
|
|
75 |
auth_token = models.CharField('Authentication Token', max_length=32, |
|
76 |
null=True, blank=True) |
|
77 |
auth_token_created = models.DateTimeField('Token creation date', |
|
78 |
null=True) |
|
79 |
auth_token_expires = models.DateTimeField('Token expiration date', |
|
80 |
null=True) |
|
81 |
|
|
82 |
created = models.DateTimeField('Creation date') |
|
83 |
updated = models.DateTimeField('Update date') |
|
84 |
|
|
85 |
is_verified = models.BooleanField('Verified?', default=False) |
|
86 |
|
|
87 |
openidurl = models.CharField('OpenID url', max_length=255, default='') |
|
88 |
|
|
89 |
@property |
|
90 |
def quota(self): |
|
91 |
return get_quota(self.uniq) |
|
92 |
|
|
93 |
@quota.setter |
|
94 |
def quota(self, value): |
|
95 |
set_quota(self.uniq, value) |
|
96 |
|
|
97 |
@property |
|
98 |
def invitation(self): |
|
99 |
try: |
|
100 |
return Invitation.objects.get(uniq=self.uniq) |
|
101 |
except Invitation.DoesNotExist: |
|
102 |
return None |
|
103 |
|
|
104 |
def save(self, update_timestamps=True, **kwargs): |
|
105 |
if update_timestamps: |
|
106 |
if not self.id: |
|
107 |
self.created = datetime.now() |
|
108 |
self.updated = datetime.now() |
|
109 |
|
|
110 |
super(User, self).save(**kwargs) |
|
111 |
|
|
112 |
#invitation consume |
|
113 |
if self.invitation and not self.invitation.is_consumed: |
|
114 |
self.invitation.consume() |
|
115 |
|
|
116 |
def renew_token(self): |
|
117 |
md5 = hashlib.md5() |
|
118 |
md5.update(self.uniq) |
|
119 |
md5.update(self.realname.encode('ascii', 'ignore')) |
|
120 |
md5.update(asctime()) |
|
121 |
|
|
122 |
self.auth_token = b64encode(md5.digest()) |
|
123 |
self.auth_token_created = datetime.now() |
|
124 |
self.auth_token_expires = self.auth_token_created + \ |
|
125 |
timedelta(hours=settings.AUTH_TOKEN_DURATION) |
|
126 |
|
|
127 |
def __unicode__(self): |
|
128 |
return self.uniq |
|
129 |
|
|
130 |
class Invitation(models.Model): |
|
131 |
inviter = models.ForeignKey(User, related_name='invitations_sent', |
|
132 |
null=True) |
|
133 |
realname = models.CharField('Real name', max_length=255) |
|
134 |
uniq = models.CharField('Unique ID', max_length=255) |
|
135 |
code = models.BigIntegerField('Invitation code', db_index=True) |
|
136 |
#obsolete: we keep it just for transfering the data |
|
137 |
is_accepted = models.BooleanField('Accepted?', default=False) |
|
138 |
is_consumed = models.BooleanField('Consumed?', default=False) |
|
139 |
created = models.DateTimeField('Creation date', auto_now_add=True) |
|
140 |
#obsolete: we keep it just for transfering the data |
|
141 |
accepted = models.DateTimeField('Acceptance date', null=True, blank=True) |
|
142 |
consumed = models.DateTimeField('Consumption date', null=True, blank=True) |
|
143 |
|
|
144 |
def consume(self): |
|
145 |
self.is_consumed = True |
|
146 |
self.consumed = datetime.now() |
|
147 |
self.save() |
|
148 |
|
|
149 |
def __unicode__(self): |
|
150 |
return '%s -> %s [%d]' % (self.inviter, self.uniq, self.code) |
b/astakos/im/static/bootstrap.css | ||
---|---|---|
1 |
/*! |
|
2 |
* Bootstrap v1.3.0 |
|
3 |
* |
|
4 |
* Copyright 2011 Twitter, Inc |
|
5 |
* Licensed under the Apache License v2.0 |
|
6 |
* http://www.apache.org/licenses/LICENSE-2.0 |
|
7 |
* |
|
8 |
* Designed and built with all the love in the world @twitter by @mdo and @fat. |
|
9 |
* Date: Thu Sep 22 12:52:42 PDT 2011 |
|
10 |
*/ |
|
11 |
/* Reset.less |
|
12 |
* Props to Eric Meyer (meyerweb.com) for his CSS reset file. We're using an adapted version here that cuts out some of the reset HTML elements we will never need here (i.e., dfn, samp, etc). |
|
13 |
* ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ |
|
14 |
html, body { |
|
15 |
margin: 0; |
|
16 |
padding: 0; |
|
17 |
} |
|
18 |
h1, |
|
19 |
h2, |
|
20 |
h3, |
|
21 |
h4, |
|
22 |
h5, |
|
23 |
h6, |
|
24 |
p, |
|
25 |
blockquote, |
|
26 |
pre, |
|
27 |
a, |
|
28 |
abbr, |
|
29 |
acronym, |
|
30 |
address, |
|
31 |
cite, |
|
32 |
code, |
|
33 |
del, |
|
34 |
dfn, |
|
35 |
em, |
|
36 |
img, |
|
37 |
q, |
|
38 |
s, |
|
39 |
samp, |
|
40 |
small, |
|
41 |
strike, |
|
42 |
strong, |
|
43 |
sub, |
|
44 |
sup, |
|
45 |
tt, |
|
46 |
var, |
|
47 |
dd, |
|
48 |
dl, |
|
49 |
dt, |
|
50 |
li, |
|
51 |
ol, |
|
52 |
ul, |
|
53 |
fieldset, |
|
54 |
form, |
|
55 |
label, |
|
56 |
legend, |
|
57 |
button, |
|
58 |
table, |
|
59 |
caption, |
|
60 |
tbody, |
|
61 |
tfoot, |
|
62 |
thead, |
|
63 |
tr, |
|
64 |
th, |
|
65 |
td { |
|
66 |
margin: 0; |
|
67 |
padding: 0; |
|
68 |
border: 0; |
|
69 |
font-weight: normal; |
|
70 |
font-style: normal; |
|
71 |
font-size: 100%; |
|
72 |
line-height: 1; |
|
73 |
font-family: inherit; |
|
74 |
} |
|
75 |
table { |
|
76 |
border-collapse: collapse; |
|
77 |
border-spacing: 0; |
|
78 |
} |
|
79 |
ol, ul { |
|
80 |
list-style: none; |
|
81 |
} |
|
82 |
q:before, |
|
83 |
q:after, |
|
84 |
blockquote:before, |
|
85 |
blockquote:after { |
|
86 |
content: ""; |
|
87 |
} |
|
88 |
html { |
|
89 |
overflow-y: scroll; |
|
90 |
font-size: 100%; |
|
91 |
-webkit-text-size-adjust: 100%; |
|
92 |
-ms-text-size-adjust: 100%; |
|
93 |
} |
|
94 |
a:focus { |
|
95 |
outline: thin dotted; |
|
96 |
} |
|
97 |
a:hover, a:active { |
|
98 |
outline: 0; |
|
99 |
} |
|
100 |
article, |
|
101 |
aside, |
|
102 |
details, |
|
103 |
figcaption, |
|
104 |
figure, |
|
105 |
footer, |
|
106 |
header, |
|
107 |
hgroup, |
|
108 |
nav, |
|
109 |
section { |
|
110 |
display: block; |
|
111 |
} |
|
112 |
audio, canvas, video { |
|
113 |
display: inline-block; |
|
114 |
*display: inline; |
|
115 |
*zoom: 1; |
|
116 |
} |
|
117 |
audio:not([controls]) { |
|
118 |
display: none; |
|
119 |
} |
|
120 |
sub, sup { |
|
121 |
font-size: 75%; |
|
122 |
line-height: 0; |
|
123 |
position: relative; |
|
124 |
vertical-align: baseline; |
|
125 |
} |
|
126 |
sup { |
|
127 |
top: -0.5em; |
|
128 |
} |
|
129 |
sub { |
|
130 |
bottom: -0.25em; |
|
131 |
} |
|
132 |
img { |
|
133 |
border: 0; |
|
134 |
-ms-interpolation-mode: bicubic; |
|
135 |
} |
|
136 |
button, |
|
137 |
input, |
|
138 |
select, |
|
139 |
textarea { |
|
140 |
font-size: 100%; |
|
141 |
margin: 0; |
|
142 |
vertical-align: baseline; |
|
143 |
*vertical-align: middle; |
|
144 |
} |
|
145 |
button, input { |
|
146 |
line-height: normal; |
|
147 |
*overflow: visible; |
|
148 |
} |
|
149 |
button::-moz-focus-inner, input::-moz-focus-inner { |
|
150 |
border: 0; |
|
151 |
padding: 0; |
|
152 |
} |
|
153 |
button, |
|
154 |
input[type="button"], |
|
155 |
input[type="reset"], |
|
156 |
input[type="submit"] { |
|
157 |
cursor: pointer; |
|
158 |
-webkit-appearance: button; |
|
159 |
} |
|
160 |
input[type="search"] { |
|
161 |
-webkit-appearance: textfield; |
|
162 |
-webkit-box-sizing: content-box; |
|
163 |
-moz-box-sizing: content-box; |
|
164 |
box-sizing: content-box; |
|
165 |
} |
|
166 |
input[type="search"]::-webkit-search-decoration { |
|
167 |
-webkit-appearance: none; |
|
168 |
} |
|
169 |
textarea { |
|
170 |
overflow: auto; |
|
171 |
vertical-align: top; |
|
172 |
} |
|
173 |
/* Variables.less |
|
174 |
* Variables to customize the look and feel of Bootstrap |
|
175 |
* ----------------------------------------------------- */ |
|
176 |
/* Variables.less |
|
177 |
* Snippets of reusable CSS to develop faster and keep code readable |
|
178 |
* ----------------------------------------------------------------- */ |
|
179 |
/* |
|
180 |
* Scaffolding |
|
181 |
* Basic and global styles for generating a grid system, structural layout, and page templates |
|
182 |
* ------------------------------------------------------------------------------------------- */ |
|
183 |
html, body { |
|
184 |
background-color: #ffffff; |
|
185 |
} |
|
186 |
body { |
|
187 |
margin: 0; |
|
188 |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
|
189 |
font-size: 13px; |
|
190 |
font-weight: normal; |
|
191 |
line-height: 18px; |
|
192 |
color: #404040; |
|
193 |
} |
|
194 |
.container { |
|
195 |
width: 940px; |
|
196 |
margin-left: auto; |
|
197 |
margin-right: auto; |
|
198 |
zoom: 1; |
|
199 |
} |
|
200 |
.container:before, .container:after { |
|
201 |
display: table; |
|
202 |
content: ""; |
|
203 |
zoom: 1; |
|
204 |
*display: inline; |
|
205 |
} |
|
206 |
.container:after { |
|
207 |
clear: both; |
|
208 |
} |
|
209 |
.container-fluid { |
|
210 |
position: relative; |
|
211 |
min-width: 940px; |
|
212 |
padding-left: 20px; |
|
213 |
padding-right: 20px; |
|
214 |
zoom: 1; |
|
215 |
} |
|
216 |
.container-fluid:before, .container-fluid:after { |
|
217 |
display: table; |
|
218 |
content: ""; |
|
219 |
zoom: 1; |
|
220 |
*display: inline; |
|
221 |
} |
|
222 |
.container-fluid:after { |
|
223 |
clear: both; |
|
224 |
} |
|
225 |
.container-fluid > .sidebar { |
|
226 |
float: left; |
|
227 |
width: 220px; |
|
228 |
} |
|
229 |
.container-fluid > .content { |
|
230 |
margin-left: 240px; |
|
231 |
} |
|
232 |
a { |
|
233 |
color: #0069d6; |
|
234 |
text-decoration: none; |
|
235 |
line-height: inherit; |
|
236 |
font-weight: inherit; |
|
237 |
} |
|
238 |
a:hover { |
|
239 |
color: #00438a; |
|
240 |
text-decoration: underline; |
|
241 |
} |
|
242 |
.pull-right { |
|
243 |
float: right; |
|
244 |
} |
|
245 |
.pull-left { |
|
246 |
float: left; |
|
247 |
} |
|
248 |
.hide { |
|
249 |
display: none; |
|
250 |
} |
|
251 |
.show { |
|
252 |
display: block; |
|
253 |
} |
|
254 |
.row { |
|
255 |
zoom: 1; |
|
256 |
margin-left: -20px; |
|
257 |
} |
|
258 |
.row:before, .row:after { |
|
259 |
display: table; |
|
260 |
content: ""; |
|
261 |
zoom: 1; |
|
262 |
*display: inline; |
|
263 |
} |
|
264 |
.row:after { |
Also available in: Unified diff