Revision 244c552b
b/runtests-venvs.sh | ||
---|---|---|
59 | 59 |
rm bin/vncauthproxy.py |
60 | 60 |
echo "running django tests..." >&2 |
61 | 61 |
export SYNNEFO_SETTINGS_DIR=/etc/lala |
62 |
snf-manage test aai admin api db helpdesk invitations logic userdata --settings=synnefo.settings.test
|
|
62 |
snf-manage test admin api db logic userdata --settings=synnefo.settings.test
|
|
63 | 63 |
cd .. |
64 | 64 |
deactivate |
65 | 65 |
|
b/runtests.sh | ||
---|---|---|
38 | 38 |
set -e |
39 | 39 |
|
40 | 40 |
echo "Running snf-app tests..." >&2 |
41 |
snf-manage test aai admin api db helpdesk logic userdata --settings=synnefo.settings.test
|
|
41 |
snf-manage test admin api db logic userdata --settings=synnefo.settings.test
|
|
42 | 42 |
|
43 | 43 |
echo "Running snf-ganeti-tools tests..." >&2 |
44 | 44 |
./snf-ganeti-tools/test/synnefo.ganeti_unittest.py |
/dev/null | ||
---|---|---|
1 |
[ |
|
2 |
{ |
|
3 |
"model": "db.SynnefoUser", |
|
4 |
"pk": 1, |
|
5 |
"fields": { |
|
6 |
"name": "testdbuser", |
|
7 |
"realname" :"test db user", |
|
8 |
"uniq" :"test@synnefo.gr", |
|
9 |
"auth_token": "46e427d657b20defe352804f0eb6f8a2", |
|
10 |
"auth_token_created": "2011-04-07 09:17:14", |
|
11 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
12 |
"type": "STUDENT", |
|
13 |
"created": "2011-02-06" |
|
14 |
} |
|
15 |
} |
|
16 |
] |
/dev/null | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or without |
|
4 |
# modification, are permitted provided that the following conditions |
|
5 |
# are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above copyright |
|
8 |
# notice, 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 |
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|
15 |
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
16 |
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
17 |
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|
18 |
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
19 |
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
20 |
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
21 |
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
22 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
23 |
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
24 |
# SUCH DAMAGE. |
|
25 |
# |
|
26 |
# The views and conclusions contained in the software and documentation are |
|
27 |
# those of the authors and should not be interpreted as representing official |
|
28 |
# policies, either expressed or implied, of GRNET S.A. |
|
29 |
|
|
30 |
from django.conf import settings |
|
31 |
from django.http import HttpResponse, HttpResponseRedirect |
|
32 |
from django.utils.cache import patch_vary_headers |
|
33 |
from synnefo.db.models import SynnefoUser |
|
34 |
from synnefo.aai.shibboleth import Tokens, register_shibboleth_user |
|
35 |
import time |
|
36 |
|
|
37 |
DONT_CHECK = getattr(settings, "AAI_SKIP_AUTH_URLS", ['/api']) |
|
38 |
|
|
39 |
class SynnefoAuthMiddleware(object): |
|
40 |
|
|
41 |
def process_request(self, request): |
|
42 |
|
|
43 |
for path in DONT_CHECK: |
|
44 |
if request.path.startswith(path): |
|
45 |
return |
|
46 |
|
|
47 |
# Special case for testing purposes, delivers the cookie for the |
|
48 |
# test user on first access |
|
49 |
if settings.BYPASS_AUTHENTICATION and \ |
|
50 |
request.GET.get('test') is not None: |
|
51 |
try: |
|
52 |
u = SynnefoUser.objects.get( |
|
53 |
auth_token=settings.BYPASS_AUTHENTICATION_SECRET_TOKEN) |
|
54 |
except SynnefoUser.DoesNotExist: |
|
55 |
raise Exception("No user found with token matching " |
|
56 |
"BYPASS_AUTHENTICATION_SECRET_TOKEN.") |
|
57 |
return self._redirect_shib_auth_user(user = u) |
|
58 |
|
|
59 |
token = None |
|
60 |
|
|
61 |
# Try to find token in a cookie |
|
62 |
token = request.COOKIES.get('X-Auth-Token', None) |
|
63 |
|
|
64 |
# Try to find token in request header |
|
65 |
if not token: |
|
66 |
token = request.META.get('HTTP_X_AUTH_TOKEN', None) |
|
67 |
|
|
68 |
if token: |
|
69 |
# token was found, retrieve user from backing store |
|
70 |
try: |
|
71 |
user = SynnefoUser.objects.get(auth_token=token) |
|
72 |
|
|
73 |
except SynnefoUser.DoesNotExist: |
|
74 |
return HttpResponseRedirect(settings.LOGIN_URL) |
|
75 |
# check user's auth token validity |
|
76 |
if (time.time() - |
|
77 |
time.mktime(user.auth_token_expires.timetuple())) > 0: |
|
78 |
# the user's token has expired, prompt to re-login |
|
79 |
return HttpResponseRedirect(settings.LOGIN_URL) |
|
80 |
|
|
81 |
request.user = user |
|
82 |
return |
|
83 |
|
|
84 |
# token was not found but user authenticated by Shibboleth |
|
85 |
if Tokens.SHIB_EPPN in request.META and \ |
|
86 |
Tokens.SHIB_SESSION_ID in request.META: |
|
87 |
try: |
|
88 |
user = SynnefoUser.objects.get(uniq=request.META[Tokens.SHIB_EPPN]) |
|
89 |
return self._redirect_shib_auth_user(user) |
|
90 |
except SynnefoUser.DoesNotExist: |
|
91 |
if register_shibboleth_user(request.META): |
|
92 |
user = SynnefoUser.objects.get(uniq=request.META[Tokens.SHIB_EPPN]) |
|
93 |
return self._redirect_shib_auth_user(user) |
|
94 |
else: |
|
95 |
return HttpResponseRedirect(settings.LOGIN_URL) |
|
96 |
|
|
97 |
if settings.TEST and 'TEST-AAI' in request.META: |
|
98 |
return HttpResponseRedirect(settings.LOGIN_URL) |
|
99 |
|
|
100 |
if request.path.endswith(settings.LOGIN_URL): |
|
101 |
# avoid redirect loops |
|
102 |
return |
|
103 |
else: |
|
104 |
# no authentication info found in headers, redirect back |
|
105 |
return HttpResponseRedirect(settings.LOGIN_URL) |
|
106 |
|
|
107 |
def process_response(self, request, response): |
|
108 |
# Tell proxies and other interested parties that the request varies |
|
109 |
# based on X-Auth-Token, to avoid caching of results |
|
110 |
patch_vary_headers(response, ('X-Auth-Token',)) |
|
111 |
return response |
|
112 |
|
|
113 |
def _redirect_shib_auth_user(self, user): |
|
114 |
expire_fmt = user.auth_token_expires.strftime('%a, %d-%b-%Y %H:%M:%S %Z') |
|
115 |
|
|
116 |
response = HttpResponse() |
|
117 |
response.set_cookie('X-Auth-Token', value=user.auth_token, |
|
118 |
expires=expire_fmt, path='/') |
|
119 |
response['X-Auth-Token'] = user.auth_token |
|
120 |
response['Location'] = settings.APP_INSTALL_URL |
|
121 |
response.status_code = 302 |
|
122 |
return response |
|
123 |
|
|
124 |
|
|
125 |
def add_url_exception(url): |
|
126 |
if not url in DONT_CHECK: |
|
127 |
DONT_CHECK.append(url) |
/dev/null | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or without |
|
4 |
# modification, are permitted provided that the following conditions |
|
5 |
# are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above copyright |
|
8 |
# notice, 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 |
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|
15 |
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
16 |
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
17 |
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|
18 |
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
19 |
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
20 |
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
21 |
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
22 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
23 |
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
24 |
# SUCH DAMAGE. |
|
25 |
|
|
26 |
# |
|
27 |
# The views and conclusions contained in the software and documentation are |
|
28 |
# those of the authors and should not be interpreted as representing official |
|
29 |
# policies, either expressed or implied, of GRNET S.A. |
|
30 |
|
|
31 |
# Business Logic for working with sibbolleth users |
|
32 |
|
|
33 |
from synnefo.logic import users |
|
34 |
|
|
35 |
class Tokens: |
|
36 |
# these are mapped by the Shibboleth SP software |
|
37 |
SHIB_EPPN = "eppn" # eduPersonPrincipalName |
|
38 |
SHIB_NAME = "Shib-InetOrgPerson-givenName" |
|
39 |
SHIB_SURNAME = "Shib-Person-surname" |
|
40 |
SHIB_CN = "Shib-Person-commonName" |
|
41 |
SHIB_DISPLAYNAME = "Shib-InetOrgPerson-displayName" |
|
42 |
SHIB_EP_AFFILIATION = "Shib-EP-Affiliation" |
|
43 |
SHIB_SESSION_ID = "Shib-Session-ID" |
|
44 |
|
|
45 |
|
|
46 |
class NoUniqueToken(BaseException): |
|
47 |
def __init__(self, msg): |
|
48 |
self.msg = msg |
|
49 |
|
|
50 |
|
|
51 |
class NoRealName(BaseException): |
|
52 |
def __init__(self, msg): |
|
53 |
self.msg = msg |
|
54 |
|
|
55 |
|
|
56 |
def register_shibboleth_user(tokens): |
|
57 |
"""Registers a Shibboleth user using the input hash as a source for data.""" |
|
58 |
|
|
59 |
if Tokens.SHIB_DISPLAYNAME in tokens: |
|
60 |
realname = tokens[Tokens.SHIB_DISPLAYNAME] |
|
61 |
elif Tokens.SHIB_CN in tokens: |
|
62 |
realname = tokens[Tokens.SHIB_CN] |
|
63 |
elif Tokens.SHIB_NAME in tokens and Tokens.SHIB_SURNAME in tokens: |
|
64 |
realname = tokens[Tokens.SHIB_NAME] + ' ' + tokens[Tokens.SHIB_SURNAME] |
|
65 |
else: |
|
66 |
raise NoRealName("Authentication does not return the user's name") |
|
67 |
|
|
68 |
try: |
|
69 |
affiliation = tokens[Tokens.SHIB_EP_AFFILIATION] |
|
70 |
except KeyError: |
|
71 |
affiliation = 'member' |
|
72 |
|
|
73 |
try: |
|
74 |
eppn = tokens[Tokens.SHIB_EPPN] |
|
75 |
except KeyError: |
|
76 |
raise NoUniqueToken("Authentication does not return a unique token") |
|
77 |
|
|
78 |
if affiliation == 'student': |
|
79 |
users.register_student(realname, '' , eppn) |
|
80 |
else: |
|
81 |
# this includes faculty but also staff, alumni, member, other, ... |
|
82 |
users.register_professor(realname, '' , eppn) |
|
83 |
|
|
84 |
return True |
/dev/null | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or without |
|
4 |
# modification, are permitted provided that the following conditions |
|
5 |
# are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above copyright |
|
8 |
# notice, 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 |
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|
15 |
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
16 |
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
17 |
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|
18 |
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
19 |
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
20 |
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
21 |
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
22 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
23 |
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
24 |
# SUCH DAMAGE. |
|
25 |
# |
|
26 |
# The views and conclusions contained in the software and documentation are |
|
27 |
# those of the authors and should not be interpreted as representing official |
|
28 |
# policies, either expressed or implied, of GRNET S.A. |
|
29 |
|
|
30 |
# Provides automated tests for aai module. The tests |
|
31 |
|
|
32 |
from django.test import TestCase |
|
33 |
from django.test.client import Client |
|
34 |
from django.conf import settings |
|
35 |
|
|
36 |
from synnefo.db.models import SynnefoUser |
|
37 |
|
|
38 |
from datetime import datetime, timedelta |
|
39 |
|
|
40 |
from synnefo.aai.shibboleth import Tokens |
|
41 |
|
|
42 |
|
|
43 |
class AaiTestCase(TestCase): |
|
44 |
fixtures = ['users', 'api_test_data', 'auth_test_data'] |
|
45 |
apibase = '/api/v1.1' |
|
46 |
|
|
47 |
def setUp(self): |
|
48 |
self.client = Client() |
|
49 |
|
|
50 |
def test_shibboleth_correct_request(self): |
|
51 |
"""test request that should succeed and register a user |
|
52 |
""" |
|
53 |
response = self.client.get('/index.html', {}, |
|
54 |
**{Tokens.SHIB_NAME: 'Jimmy', |
|
55 |
Tokens.SHIB_EPPN: 'jh@gmail.com', |
|
56 |
Tokens.SHIB_CN: 'Jimmy Hendrix', |
|
57 |
Tokens.SHIB_SESSION_ID: '123321', |
|
58 |
'TEST-AAI' : 'true'}) |
|
59 |
user = None |
|
60 |
try: |
|
61 |
user = SynnefoUser.objects.get(uniq = "jh@gmail.com") |
|
62 |
except SynnefoUser.DoesNotExist: |
|
63 |
self.assertNotEqual(user, None) |
|
64 |
self.assertNotEqual(user, None) |
|
65 |
self.assertEquals(response.status_code, 302) |
|
66 |
self.assertEquals(response['Location'], settings.APP_INSTALL_URL) |
|
67 |
self.assertTrue('X-Auth-Token' in response) |
|
68 |
self.assertEquals(response['X-Auth-Token'], user.auth_token) |
|
69 |
#self.assertNotEquals(response.cookies['X-Auth-Token'].find(user.auth_token), -1) |
|
70 |
|
|
71 |
def test_shibboleth_no_uniq_request(self): |
|
72 |
"""test a request with no unique field |
|
73 |
""" |
|
74 |
response = self.client.get('/index.html', {}, |
|
75 |
**{Tokens.SHIB_NAME: 'Jimmy', |
|
76 |
Tokens.SHIB_CN: 'Jimmy Hendrix', |
|
77 |
'TEST-AAI': 'true'}) |
|
78 |
self._test_redirect(response) |
|
79 |
|
|
80 |
def test_shibboleth_expired_token(self): |
|
81 |
""" test request from expired token |
|
82 |
""" |
|
83 |
user = SynnefoUser.objects.get(uniq="test@synnefo.gr") |
|
84 |
self.assertNotEqual(user.auth_token_expires, None) |
|
85 |
user.auth_token_expires = datetime.now() |
|
86 |
user.save() |
|
87 |
response = self.client.get('/index.html', {}, |
|
88 |
**{'X-Auth-Token': user.auth_token, |
|
89 |
'TEST-AAI': 'true'}) |
|
90 |
self._test_redirect(response) |
|
91 |
|
|
92 |
def test_shibboleth_redirect(self): |
|
93 |
""" test redirect to Sibboleth page |
|
94 |
""" |
|
95 |
response = self.client.get('/index.html', {}, **{'TEST-AAI': 'true'}) |
|
96 |
self._test_redirect(response) |
|
97 |
|
|
98 |
def test_shibboleth_auth(self): |
|
99 |
""" test authentication with X-Auth-Token |
|
100 |
""" |
|
101 |
user = SynnefoUser.objects.get(uniq="test@synnefo.gr") |
|
102 |
response = self.client.get('/index.html', {}, |
|
103 |
**{'X-Auth-Token': user.auth_token, |
|
104 |
'TEST-AAI': 'true'}) |
|
105 |
self.assertTrue(response.status_code, 200) |
|
106 |
self.assertTrue('Vary' in response) |
|
107 |
self.assertTrue('X-Auth-Token' in response['Vary']) |
|
108 |
|
|
109 |
def test_auth_cookie(self): |
|
110 |
user = SynnefoUser.objects.get(uniq = "test@synnefo.gr") |
|
111 |
self.client.cookies['X-Auth-Token'] = user.auth_token |
|
112 |
response = self.client.get('/', {}, |
|
113 |
**{'X-Auth-Token': user.auth_token, |
|
114 |
'TEST-AAI' : 'true'}) |
|
115 |
self.assertTrue(response.status_code, 200) |
|
116 |
self.assertTrue('Vary' in response) |
|
117 |
self.assertTrue('X-Auth-Token' in response['Vary']) |
|
118 |
|
|
119 |
def _test_redirect(self, response): |
|
120 |
self.assertEquals(response.status_code, 302) |
|
121 |
self.assertTrue('Location' in response) |
|
122 |
self.assertTrue(response['Location'].startswith(settings.LOGIN_URL)) |
|
123 |
|
b/snf-app/synnefo/admin/api.py | ||
---|---|---|
37 | 37 |
from django.http import HttpResponse |
38 | 38 |
|
39 | 39 |
from synnefo.admin.views import requires_admin |
40 |
from synnefo.db import models
|
|
40 |
from synnefo.db.models import VirtualMachine
|
|
41 | 41 |
|
42 | 42 |
|
43 | 43 |
@requires_admin |
44 | 44 |
def servers_info(request, server_id): |
45 |
server = models.VirtualMachine.objects.get(id=server_id)
|
|
45 |
server = VirtualMachine.objects.get(id=server_id) |
|
46 | 46 |
reply = { |
47 | 47 |
'name': server.name, |
48 | 48 |
'ref': '#'} |
49 | 49 |
return HttpResponse(json.dumps(reply), content_type='application/json') |
50 |
|
|
51 |
|
|
52 |
@requires_admin |
|
53 |
def users_info(request, user_id): |
|
54 |
user = models.SynnefoUser.objects.get(id=user_id) |
|
55 |
reply = { |
|
56 |
'name': user.name, |
|
57 |
'ref': reverse('synnefo.admin.views.users_info', args=(user_id,))} |
|
58 |
return HttpResponse(json.dumps(reply), content_type='application/json') |
b/snf-app/synnefo/admin/static/main.js | ||
---|---|---|
21 | 21 |
append_item('append-server', '/admin/api/servers/'); |
22 | 22 |
} |
23 | 23 |
|
24 |
function append_user() { |
|
25 |
append_item('append-user', '/admin/api/users/'); |
|
26 |
} |
|
27 |
|
|
28 |
|
|
29 | 24 |
$(function() { |
30 | 25 |
$('table.id-sorted').tablesorter({ sortList: [[0, 0]] }); |
31 | 26 |
|
32 | 27 |
$('tr.row-template').hide(); |
33 | 28 |
$('div.alert-message').hide(); |
34 | 29 |
|
35 |
append_user(); |
|
36 |
$('.append-user').change(function() { |
|
37 |
append_user(); |
|
38 |
}); |
|
39 |
|
|
40 | 30 |
append_server(); |
41 | 31 |
$('.append-server').change(function() { |
42 | 32 |
append_server(); |
b/snf-app/synnefo/admin/templates/images_info.html | ||
---|---|---|
33 | 33 |
<div class="clearfix"> |
34 | 34 |
<label for="image-owner">Owner ID</label> |
35 | 35 |
<div class="input input-append"> |
36 |
<input class="small append-user" id="image-owner" name="owner" value="{{ image.owner.id }}" type="text" />
|
|
36 |
<input class="small" id="image-owner" name="owner" value="{{ image.userid }}" type="text" />
|
|
37 | 37 |
<label class="add-on"><a href=""></a></label> |
38 | 38 |
</div> |
39 | 39 |
</div> |
b/snf-app/synnefo/admin/templates/images_list.html | ||
---|---|---|
19 | 19 |
<tr> |
20 | 20 |
<td><a href="{% url synnefo.admin.views.images_info image.id %}">{{ image.id }}</a></td> |
21 | 21 |
<td><a href="{% url synnefo.admin.views.images_info image.id %}">{{ image.name }}</a></td> |
22 |
<td>{% if image.owner %}<a href="{% url synnefo.admin.views.users_info image.owner.id %}">{{ image.owner.name }}</a>{% endif %} |
|
23 |
</td> |
|
22 |
<td>{{ image.userid|default:"" }}</td> |
|
24 | 23 |
<td>{{ image.state }}</td> |
25 | 24 |
<td>{{ image.backend_id }}</td> |
26 | 25 |
<td>{{ image.format }}</td> |
b/snf-app/synnefo/admin/templates/images_register.html | ||
---|---|---|
13 | 13 |
<div class="clearfix"> |
14 | 14 |
<label for="image-owner">Owner ID</label> |
15 | 15 |
<div class="input input-append"> |
16 |
<input class="small append-user" id="image-owner" name="owner" value="{{ image.owner.id }}" type="text" />
|
|
16 |
<input class="small" id="image-owner" name="owner" value="{{ image.userid }}" type="text" />
|
|
17 | 17 |
<label class="add-on"><a href=""></a></label> |
18 | 18 |
</div> |
19 | 19 |
</div> |
b/snf-app/synnefo/admin/templates/servers_list.html | ||
---|---|---|
19 | 19 |
<tr> |
20 | 20 |
<td>{{ server.id }}</td> |
21 | 21 |
<td>{{ server.name }}</td> |
22 |
<td><a href="{% url synnefo.admin.views.users_info server.owner.id %}">{{ server.owner.name }}</a></td>
|
|
22 |
<td>{{ server.userid }}</td>
|
|
23 | 23 |
<td>{{ server.operstate }}</td> |
24 | 24 |
<td><a href="{% url synnefo.admin.views.flavors_info server.flavor.id %}">{{ server.flavor.name }}</a></td> |
25 | 25 |
<td><a href="{% url synnefo.admin.views.images_info server.imageid %}">{{ server.imageid }}</a></td> |
/dev/null | ||
---|---|---|
1 |
{% extends "base.html" %} |
|
2 |
|
|
3 |
{% block body %} |
|
4 |
|
|
5 |
<form action="{% url synnefo.admin.views.users_modify user.id %}" method="post"> |
|
6 |
<div class="clearfix"> |
|
7 |
<label for="user-id">ID</label> |
|
8 |
<div class="input"> |
|
9 |
<span class="uneditable-input" id="user-id">{{ user.id }}</span> |
|
10 |
</div> |
|
11 |
</div> |
|
12 |
|
|
13 |
<div class="clearfix"> |
|
14 |
<label for="user-name">Name</label> |
|
15 |
<div class="input"> |
|
16 |
<input class="medium" id="user-name" name="name" value="{{ user.name }}" type="text" /> |
|
17 |
</div> |
|
18 |
</div> |
|
19 |
|
|
20 |
<div class="clearfix"> |
|
21 |
<label for="user-realname">Real Name</label> |
|
22 |
<div class="input"> |
|
23 |
<input class="large" id="user-realname" name="realname" value="{{ user.realname }}" type="text" /> |
|
24 |
</div> |
|
25 |
</div> |
|
26 |
|
|
27 |
<div class="clearfix"> |
|
28 |
<label for="user-uniq">Uniq</label> |
|
29 |
<div class="input"> |
|
30 |
<input class="large" id="user-uniq" name="uniq" value="{{ user.uniq }}" type="text" /> |
|
31 |
</div> |
|
32 |
</div> |
|
33 |
|
|
34 |
<div class="clearfix"> |
|
35 |
<label for="user-type">Type</label> |
|
36 |
<div class="input"> |
|
37 |
<select class="medium" id="user-type" name="type"> |
|
38 |
{% for type in types %} |
|
39 |
<option{% ifequal type user.type %} selected{% endifequal %}>{{ type }}</option> |
|
40 |
{% endfor %} |
|
41 |
</select> |
|
42 |
</div> |
|
43 |
</div> |
|
44 |
|
|
45 |
<div class="clearfix"> |
|
46 |
<label for="user-state">State</label> |
|
47 |
<div class="input"> |
|
48 |
<select class="medium" id="user-state" name="state"> |
|
49 |
{% for state in states %} |
|
50 |
<option{% ifequal state user.state %} selected{% endifequal %}>{{ state }}</option> |
|
51 |
{% endfor %} |
|
52 |
</select> |
|
53 |
</div> |
|
54 |
</div> |
|
55 |
|
|
56 |
<div class="clearfix"> |
|
57 |
<label for="user-created">Created</label> |
|
58 |
<div class="input"> |
|
59 |
<span class="uneditable-input" id="user-created">{{ user.created }}</span> |
|
60 |
</div> |
|
61 |
</div> |
|
62 |
|
|
63 |
<div class="clearfix"> |
|
64 |
<label for="user-updated">Updated</label> |
|
65 |
<div class="input"> |
|
66 |
<span class="uneditable-input" id="user-updated">{{ user.updated }}</span> |
|
67 |
</div> |
|
68 |
</div> |
|
69 |
|
|
70 |
<div class="actions"> |
|
71 |
<button type="submit" class="btn primary">Save Changes</button> |
|
72 |
<button type="reset" class="btn">Reset</button> |
|
73 |
</div> |
|
74 |
</form> |
|
75 |
{% endblock body %} |
/dev/null | ||
---|---|---|
1 |
{% extends "base.html" %} |
|
2 |
|
|
3 |
{% block body %} |
|
4 |
<table class="zebra-striped id-sorted"> |
|
5 |
<thead> |
|
6 |
<tr> |
|
7 |
<th>ID</th> |
|
8 |
<th>Name</th> |
|
9 |
<th>Real Name</th> |
|
10 |
<th>Uniq</th> |
|
11 |
<th>Type</th> |
|
12 |
<th>State</th> |
|
13 |
<th>Updated</th> |
|
14 |
</tr> |
|
15 |
</thead> |
|
16 |
<tbody> |
|
17 |
{% for user in users %} |
|
18 |
<tr> |
|
19 |
<td><a href="{% url synnefo.admin.views.users_info user.id %}">{{ user.id }}</a></td> |
|
20 |
<td><a href="{% url synnefo.admin.views.users_info user.id %}">{{ user.name }}</a></td> |
|
21 |
<td>{{ user.realname }}</td> |
|
22 |
<td>{{ user.uniq }}</td> |
|
23 |
<td>{{ user.type }}</td> |
|
24 |
<td>{{ user.state }}</td> |
|
25 |
<td>{{ user.updated }}</td> |
|
26 |
</tr> |
|
27 |
{% endfor %} |
|
28 |
</tbody> |
|
29 |
</table> |
|
30 |
{% endblock body %} |
b/snf-app/synnefo/admin/urls.py | ||
---|---|---|
50 | 50 |
(r'^/images/(\d+)/modify/?$', 'images_modify'), |
51 | 51 |
|
52 | 52 |
(r'^/servers/?$', 'servers_list'), |
53 |
|
|
54 |
(r'^/users/?$', 'users_list'), |
|
55 |
(r'^/users/(\d+)/?$', 'users_info'), |
|
56 |
(r'^/users/(\d+)/modify/?$', 'users_modify'), |
|
57 |
(r'^/users/(\d+)/delete/?$', 'users_delete'), |
|
58 | 53 |
) |
59 | 54 |
|
60 | 55 |
urlpatterns += patterns('synnefo.admin.api', |
b/snf-app/synnefo/admin/views.py | ||
---|---|---|
77 | 77 |
@requires_admin |
78 | 78 |
def index(request): |
79 | 79 |
stats = {} |
80 |
stats['users'] = models.SynnefoUser.objects.count() |
|
81 | 80 |
stats['images'] = models.Image.objects.exclude(state='DELETED').count() |
82 | 81 |
stats['flavors'] = models.Flavor.objects.count() |
83 | 82 |
stats['vms'] = models.VirtualMachine.objects.filter(deleted=False).count() |
... | ... | |
187 | 186 |
image = models.Image() |
188 | 187 |
image.state = 'ACTIVE' |
189 | 188 |
image.name = request.POST.get('name') |
190 |
owner_id = request.POST.get('owner') or None |
|
191 |
image.owner = owner_id and models.SynnefoUser.objects.get(id=owner_id) |
|
189 |
image.userid = request.POST.get('owner') |
|
192 | 190 |
image.backend_id = request.POST.get('backend') |
193 | 191 |
image.format = request.POST.get('format') |
194 | 192 |
image.public = True if request.POST.get('public') else False |
... | ... | |
221 | 219 |
image = models.Image.objects.get(id=image_id) |
222 | 220 |
image.name = request.POST.get('name') |
223 | 221 |
image.state = request.POST.get('state') |
224 |
owner_id = request.POST.get('owner') or None |
|
225 |
image.owner = owner_id and models.SynnefoUser.objects.get(id=owner_id) |
|
226 |
vm_id = request.POST.get('sourcevm') or None |
|
222 |
image.userid = request.POST.get('owner') |
|
223 |
vm_id = request.POST.get('sourcevm') |
|
227 | 224 |
image.sourcevm = vm_id and models.VirtualMachine.objects.get(id=vm_id) |
228 | 225 |
image.backend_id = request.POST.get('backend') |
229 | 226 |
image.format = request.POST.get('format') |
... | ... | |
258 | 255 |
all_states=sorted(all_states), |
259 | 256 |
filters=filters) |
260 | 257 |
return HttpResponse(html) |
261 |
|
|
262 |
|
|
263 |
@requires_admin |
|
264 |
def users_list(request): |
|
265 |
all_states = set(x[0] for x in models.SynnefoUser.ACCOUNT_STATE) |
|
266 |
default = all_states - set(['DELETED']) |
|
267 |
filters = get_filters(request, 'users_filters', all_states, default) |
|
268 |
|
|
269 |
users = models.SynnefoUser.objects.all() |
|
270 |
for state in all_states - filters: |
|
271 |
users = users.exclude(state=state) |
|
272 |
|
|
273 |
html = render('users_list.html', 'users', |
|
274 |
users=users.order_by('id'), |
|
275 |
all_states=sorted(all_states), |
|
276 |
filters=filters) |
|
277 |
return HttpResponse(html) |
|
278 |
|
|
279 |
|
|
280 |
@requires_admin |
|
281 |
def users_info(request, user_id): |
|
282 |
user = models.SynnefoUser.objects.get(id=user_id) |
|
283 |
types = [x[0] for x in models.SynnefoUser.ACCOUNT_TYPE] |
|
284 |
if not user.type: |
|
285 |
types = [''] + types |
|
286 |
states = [x[0] for x in models.SynnefoUser.ACCOUNT_STATE] |
|
287 |
html = render('users_info.html', 'users', |
|
288 |
user=user, types=types, states=states) |
|
289 |
return HttpResponse(html) |
|
290 |
|
|
291 |
|
|
292 |
@requires_admin |
|
293 |
def users_modify(request, user_id): |
|
294 |
user = models.SynnefoUser.objects.get(id=user_id) |
|
295 |
user.name = request.POST.get('name') |
|
296 |
user.realname = request.POST.get('realname') |
|
297 |
user.uniq = request.POST.get('uniq') |
|
298 |
user.type = request.POST.get('type') |
|
299 |
user.state = request.POST.get('state') |
|
300 |
user.save() |
|
301 |
log.info('User %s modified User %s', request.user.name, user.name) |
|
302 |
return redirect(users_info, user.id) |
|
303 |
|
|
304 |
|
|
305 |
@requires_admin |
|
306 |
def users_delete(request, user_id): |
|
307 |
user = models.SynnefoUser.objects.get(id=user_id) |
|
308 |
user.delete() |
|
309 |
log.info('User %s deleted User %s', request.user.name, user.name) |
|
310 |
return redirect(users_list) |
b/snf-app/synnefo/api/fixtures/api_test_data.json | ||
---|---|---|
247 | 247 |
"pk": 1, |
248 | 248 |
"fields": { |
249 | 249 |
"name": "Debian Squeeze", |
250 |
"owner": 1,
|
|
250 |
"userid": "test",
|
|
251 | 251 |
"created": "2011-02-06 00:00:00", |
252 | 252 |
"updated": "2011-02-06 00:00:00", |
253 | 253 |
"state": "ACTIVE" |
... | ... | |
257 | 257 |
"model": "db.VirtualMachine", |
258 | 258 |
"pk": 1001, |
259 | 259 |
"fields": { |
260 |
"owner": 1,
|
|
260 |
"userid": "test",
|
|
261 | 261 |
"name": "snf-1001", |
262 | 262 |
"created": "2011-02-06 00:00:00", |
263 | 263 |
"updated": "2011-02-06 00:00:00", |
... | ... | |
272 | 272 |
"model": "db.VirtualMachine", |
273 | 273 |
"pk": 1002, |
274 | 274 |
"fields": { |
275 |
"owner": 1,
|
|
275 |
"userid": "test",
|
|
276 | 276 |
"name": "snf-1002", |
277 | 277 |
"created": "2011-02-10 00:00:00", |
278 | 278 |
"updated": "2011-02-10 00:00:00", |
... | ... | |
287 | 287 |
"model": "db.VirtualMachine", |
288 | 288 |
"pk": 1003, |
289 | 289 |
"fields": { |
290 |
"owner": 1,
|
|
290 |
"userid": "test",
|
|
291 | 291 |
"name": "snf-1003", |
292 | 292 |
"created": "2011-02-10 00:00:00", |
293 | 293 |
"updated": "2011-02-10 00:00:00", |
... | ... | |
302 | 302 |
"model": "db.VirtualMachine", |
303 | 303 |
"pk": 1004, |
304 | 304 |
"fields": { |
305 |
"owner": 1,
|
|
305 |
"userid": "test",
|
|
306 | 306 |
"name": "snf-1004", |
307 | 307 |
"created": "2011-02-10 00:00:00", |
308 | 308 |
"updated": "2011-02-10 00:00:00", |
... | ... | |
321 | 321 |
"created": "2011-02-10 00:00:00", |
322 | 322 |
"updated": "2011-02-10 00:00:00", |
323 | 323 |
"state": "ACTIVE", |
324 |
"owner" : 1,
|
|
324 |
"userid" : "test",
|
|
325 | 325 |
"sourcevm": 1001 |
326 | 326 |
} |
327 | 327 |
} |
b/snf-app/synnefo/api/middleware.py | ||
---|---|---|
1 |
from django.http import HttpResponse |
|
2 |
from synnefo.db.models import SynnefoUser |
|
3 |
from django.utils.cache import patch_vary_headers |
|
4 |
import time |
|
1 |
# Copyright 2012 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. |
|
5 | 33 |
|
6 |
class ApiAuthMiddleware(object):
|
|
34 |
from django.utils.cache import patch_vary_headers
|
|
7 | 35 |
|
8 |
auth_token = "X-Auth-Token" |
|
9 |
auth_user = "X-Auth-User" |
|
10 |
auth_key = "X-Auth-Key" |
|
11 | 36 |
|
37 |
class ApiAuthMiddleware(object): |
|
12 | 38 |
def process_request(self, request): |
13 |
if not request.path.startswith('/api/') : |
|
14 |
return |
|
15 |
|
|
16 |
token = None |
|
17 |
|
|
18 |
# Try to find token in a cookie |
|
19 |
token = request.COOKIES.get('X-Auth-Token', None) |
|
20 |
|
|
21 |
# Try to find token in request header |
|
22 |
if not token: |
|
23 |
token = request.META.get('HTTP_X_AUTH_TOKEN', None) |
|
24 |
|
|
25 |
if token: |
|
26 |
user = None |
|
27 |
# Retrieve user from DB or other caching mechanism |
|
28 |
try: |
|
29 |
user = SynnefoUser.objects.get(auth_token=token) |
|
30 |
except SynnefoUser.DoesNotExist: |
|
31 |
user = None |
|
32 |
|
|
33 |
# Check user's auth token |
|
34 |
if user and (time.time() - |
|
35 |
time.mktime(user.auth_token_expires.timetuple())) > 0: |
|
36 |
# The user's token has expired, re-login |
|
37 |
user = None |
|
38 |
|
|
39 |
request.user = user |
|
40 |
return |
|
41 |
|
|
42 |
# A Rackspace API authentication request |
|
43 |
if self.auth_user in request.META and \ |
|
44 |
self.auth_key in request.META and \ |
|
45 |
'GET' == request.method: |
|
46 |
# This is here merely for compatibility with the Openstack API. |
|
47 |
# All normal users should authenticate through Shibboleth. Admin |
|
48 |
# users or other selected users could use this as a bypass |
|
49 |
# mechanism |
|
50 |
user = SynnefoUser.objects\ |
|
51 |
.filter(name = request.META[self.auth_user]) \ |
|
52 |
.filter(uniq = request.META[self.auth_key]) |
|
53 |
|
|
54 |
response = HttpResponse() |
|
55 |
if user.count() <= 0: |
|
56 |
response.status_code = 401 |
|
57 |
else: |
|
58 |
response.status_code = 204 |
|
59 |
response['X-Auth-Token'] = user[0].auth_token |
|
60 |
# TODO: set the following fields when we do have this info |
|
61 |
response['X-Server-Management-Url'] = "" |
|
62 |
response['X-Storage-Url'] = "" |
|
63 |
response['X-CDN-Management-Url'] = "" |
|
64 |
return response |
|
65 |
|
|
66 | 39 |
request.user = None |
67 | 40 |
|
68 | 41 |
def process_response(self, request, response): |
... | ... | |
70 | 43 |
# based on X-Auth-Token, to avoid caching of results |
71 | 44 |
patch_vary_headers(response, ('X-Auth-Token',)) |
72 | 45 |
return response |
73 |
|
b/snf-app/synnefo/api/tests.py | ||
---|---|---|
49 | 49 |
|
50 | 50 |
class AaiClient(Client): |
51 | 51 |
def request(self, **request): |
52 |
request['HTTP_X_AUTH_TOKEN'] = \ |
|
53 |
settings.BYPASS_AUTHENTICATION_SECRET_TOKEN |
|
52 |
request['HTTP_X_AUTH_TOKEN'] = '0000' |
|
54 | 53 |
return super(AaiClient, self).request(**request) |
55 | 54 |
|
56 | 55 |
|
... | ... | |
160 | 159 |
request = { |
161 | 160 |
"server": { |
162 | 161 |
"name": "new-server-test", |
163 |
"owner": 1,
|
|
162 |
"userid": "test",
|
|
164 | 163 |
"imageRef": 1, |
165 | 164 |
"flavorRef": 1, |
166 | 165 |
"metadata": { |
... | ... | |
342 | 341 |
self.assertEqual(response.status_code, 201) |
343 | 342 |
|
344 | 343 |
|
345 |
def create_users(n=1): |
|
346 |
for i in range(n): |
|
347 |
SynnefoUser.objects.create(name='User %d' % i) |
|
348 |
|
|
349 |
|
|
350 | 344 |
def create_flavors(n=1): |
351 | 345 |
for i in range(n): |
352 | 346 |
Flavor.objects.create( |
... | ... | |
356 | 350 |
|
357 | 351 |
|
358 | 352 |
def create_images(n=1): |
359 |
owner = SynnefoUser.objects.all()[0] |
|
360 | 353 |
for i in range(n): |
361 | 354 |
Image.objects.create( |
362 | 355 |
name='Image %d' % (i + 1), |
363 | 356 |
state='ACTIVE', |
364 |
owner=owner)
|
|
357 |
owner='test')
|
|
365 | 358 |
|
366 | 359 |
|
367 | 360 |
def create_image_metadata(n=1): |
... | ... | |
374 | 367 |
|
375 | 368 |
|
376 | 369 |
def create_servers(n=1): |
377 |
owner = SynnefoUser.objects.all()[0] |
|
378 | 370 |
flavors = Flavor.objects.all() |
379 | 371 |
images = Image.objects.all() |
380 | 372 |
for i in range(n): |
381 | 373 |
VirtualMachine.objects.create( |
382 | 374 |
name='Server %d' % (i + 1), |
383 |
owner=owner,
|
|
375 |
owner='test',
|
|
384 | 376 |
imageid=choice(images).id, |
385 | 377 |
hostid=str(i), |
386 | 378 |
flavor=choice(flavors)) |
... | ... | |
410 | 402 |
|
411 | 403 |
|
412 | 404 |
class BaseTestCase(TestCase): |
413 |
USERS = 0 |
|
414 | 405 |
FLAVORS = 1 |
415 | 406 |
IMAGES = 1 |
416 | 407 |
SERVERS = 1 |
... | ... | |
420 | 411 |
|
421 | 412 |
def setUp(self): |
422 | 413 |
self.client = AaiClient() |
423 |
create_users(self.USERS) |
|
424 | 414 |
create_flavors(self.FLAVORS) |
425 | 415 |
create_images(self.IMAGES) |
426 | 416 |
create_image_metadata(self.IMAGE_METADATA) |
b/snf-app/synnefo/api/util.py | ||
---|---|---|
177 | 177 |
try: |
178 | 178 |
image_id = int(image_id) |
179 | 179 |
image = Image.objects.get(id=image_id) |
180 |
if not image.public and image.owner != owner:
|
|
180 |
if not image.public and image.userid != owner:
|
|
181 | 181 |
raise ItemNotFound('Image not found.') |
182 | 182 |
return image |
183 | 183 |
except ValueError: |
b/snf-app/synnefo/app_settings/__init__.py | ||
---|---|---|
1 | 1 |
synnefo_web_apps = [ |
2 |
'synnefo.aai', |
|
3 | 2 |
'synnefo.admin', |
4 | 3 |
'synnefo.api', |
5 | 4 |
'synnefo.ui', |
6 | 5 |
'synnefo.db', |
7 | 6 |
'synnefo.logic', |
8 |
'synnefo.helpdesk', |
|
9 | 7 |
'synnefo.plankton', |
10 | 8 |
'synnefo.ui.userdata', |
11 | 9 |
] |
12 | 10 |
|
13 | 11 |
synnefo_web_middleware = [ |
14 | 12 |
{'after': 'django.middleware.locale.LocaleMiddleware', 'insert': [ |
15 |
'synnefo.aai.middleware.SynnefoAuthMiddleware', |
|
16 |
'synnefo.api.middleware.ApiAuthMiddleware', |
|
17 |
'synnefo.helpdesk.middleware.HelpdeskMiddleware' |
|
13 |
'synnefo.api.middleware.ApiAuthMiddleware' |
|
18 | 14 |
] |
19 | 15 |
} |
20 | 16 |
] |
b/snf-app/synnefo/app_settings/default/__init__.py | ||
---|---|---|
38 | 38 |
from synnefo.app_settings.default.plankton import * |
39 | 39 |
from synnefo.app_settings.default.ui import * |
40 | 40 |
from synnefo.app_settings.default.userdata import * |
41 |
from synnefo.app_settings.default.aai import * |
|
42 | 41 |
from synnefo.app_settings.default.reconciliation import * |
43 |
from synnefo.app_settings.default.helpdesk import * |
|
44 | 42 |
from synnefo.app_settings.default.tests import * |
45 | 43 |
|
/dev/null | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from synnefo.util.entry_points import extend_list_from_entry_point |
|
3 |
# |
|
4 |
# AAI configuration |
|
5 |
##################### |
|
6 |
|
|
7 |
# Unauthenticated HTTP requests to the UI get redirected to this URL |
|
8 |
LOGIN_URL = "/login" |
|
9 |
|
|
10 |
# Set the expiration time of newly created auth tokens |
|
11 |
# to be this many hours after their creation time. |
|
12 |
AUTH_TOKEN_DURATION = 30 * 24 |
|
13 |
|
|
14 |
# Enable receiving a temporary auth token (using the ?test URL parameter) that |
|
15 |
# bypasses the authentication mechanism. |
|
16 |
# |
|
17 |
# Make sure there is an actual user in the db whose token matches |
|
18 |
# BYPASS_AUTHENTICATION_SECRET_TOKEN. |
|
19 |
# |
|
20 |
# WARNING, ACHTUNG, README, etc: DO NOT ENABLE THIS ON DEPLOYED VERSIONS! |
|
21 |
# |
|
22 |
BYPASS_AUTHENTICATION = False |
|
23 |
BYPASS_AUTHENTICATION_SECRET_TOKEN = '5e41595e9e884543fa048e07c1094d74' |
|
24 |
|
|
25 |
# Urls that bypass Shibboleth authentication |
|
26 |
AAI_SKIP_AUTH_URLS = ['/api', '/plankton'] |
|
27 |
AAI_SKIP_AUTH_URLS = extend_list_from_entry_point(AAI_SKIP_AUTH_URLS, \ |
|
28 |
'synnefo', 'web_skip_urls') |
/dev/null | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# |
|
3 |
# Helpdesk application |
|
4 |
# |
|
5 |
|
|
6 |
# Duration for temporary auth tokens, created for impersonating a registered |
|
7 |
# user by helpdesk staff. |
|
8 |
HELPDESK_TOKEN_DURATION_MIN = 30 |
|
9 |
|
|
10 |
# IP addresses of the machines allowed to connect as help desk |
|
11 |
HELPDESK_ALLOWED_IPS = ("127.0.0.1",) |
|
12 |
|
b/snf-app/synnefo/app_settings/urls.py | ||
---|---|---|
37 | 37 |
(r'^ui/', include('synnefo.ui.urls')), |
38 | 38 |
(r'^admin/', include('synnefo.admin.urls')), |
39 | 39 |
(r'^api/', include('synnefo.api.urls')), |
40 |
(r'^helpdesk/?', include('synnefo.helpdesk.urls')), |
|
41 | 40 |
(r'^plankton/', include('synnefo.plankton.urls')), |
42 | 41 |
) |
43 | 42 |
|
b/snf-app/synnefo/db/fixtures/db_test_data.json | ||
---|---|---|
1 | 1 |
[ |
2 | 2 |
{ |
3 |
"model": "db.SynnefoUser", |
|
4 |
"pk": 30000, |
|
5 |
"fields": { |
|
6 |
"name": "testdbuser", |
|
7 |
"realname" :"test db user", |
|
8 |
"uniq" :"test@synnefo.gr", |
|
9 |
"auth_token": "5e41595e9e884543fa048e07c1094d74", |
|
10 |
"auth_token_created": "2009-04-07 09:17:14", |
|
11 |
"auth_token_expires": "2015-04-07 09:17:14", |
|
12 |
"type": "STUDENT", |
|
13 |
"created": "2011-02-06" |
|
14 |
} |
|
15 |
}, |
|
16 |
{ |
|
17 | 3 |
"model": "db.Flavor", |
18 | 4 |
"pk": 30000, |
19 | 5 |
"fields": { |
... | ... | |
39 | 25 |
"updated": "2011-02-06 00:00:00", |
40 | 26 |
"created": "2011-02-06 00:00:00", |
41 | 27 |
"state": "ACTIVE", |
42 |
"owner" : 30000
|
|
28 |
"userid" : "test"
|
|
43 | 29 |
} |
44 | 30 |
}, |
45 | 31 |
{ |
... | ... | |
64 | 50 |
"model": "db.VirtualMachine", |
65 | 51 |
"pk": 30000, |
66 | 52 |
"fields": { |
67 |
"owner": 30000,
|
|
53 |
"userid": "test",
|
|
68 | 54 |
"name": "snf-1001", |
69 | 55 |
"created": "2009-02-06 00:00:00", |
70 | 56 |
"charged": "2010-02-06 00:00:00", |
... | ... | |
78 | 64 |
"model": "db.VirtualMachine", |
79 | 65 |
"pk": 30001, |
80 | 66 |
"fields": { |
81 |
"owner": 30000,
|
|
67 |
"userid": "test",
|
|
82 | 68 |
"name": "snf-1002", |
83 | 69 |
"created": "2009-02-06 00:00:00", |
84 | 70 |
"charged": "2010-02-06 00:00:00", |
... | ... | |
89 | 75 |
} |
90 | 76 |
}, |
91 | 77 |
{ |
92 |
"model": "db.Disk", |
|
93 |
"pk": 30000, |
|
94 |
"fields": { |
|
95 |
"name": "My_Music", |
|
96 |
"created": "2011-02-10 00:00:00", |
|
97 |
"size" : 20, |
|
98 |
"vm" : 30000, |
|
99 |
"owner" : 30000 |
|
100 |
} |
|
101 |
}, |
|
102 |
{ |
|
103 | 78 |
"model": "db.VirtualMachine", |
104 | 79 |
"pk": 30002, |
105 | 80 |
"fields": { |
106 |
"owner": 30000,
|
|
81 |
"userid": "test",
|
|
107 | 82 |
"name": "snf-test-building", |
108 | 83 |
"created": "2009-02-06 00:00:00", |
109 | 84 |
"charged": "2010-02-06 00:00:00", |
/dev/null | ||
---|---|---|
1 |
[ |
|
2 |
{ |
|
3 |
"model": "db.Disk", |
|
4 |
"pk": 1, |
|
5 |
"fields": { |
|
6 |
"name": "My_Music", |
|
7 |
"created": "2011-02-10 00:00:00", |
|
8 |
"updated": "2011-02-10 00:00:00", |
|
9 |
"size" : "20", |
|
10 |
"vm" : "1001", |
|
11 |
"owner" : "1" |
|
12 |
} |
|
13 |
}, |
|
14 |
{ |
|
15 |
"model": "db.Disk", |
|
16 |
"pk": 2, |
|
17 |
"fields": { |
|
18 |
"name": "My_Videos", |
|
19 |
"created": "2011-02-10 00:00:00", |
|
20 |
"updated": "2011-02-10 00:00:00", |
|
21 |
"size" : "300", |
|
22 |
"vm" : "1001", |
|
23 |
"owner" : "1" |
|
24 |
} |
|
25 |
} |
|
26 |
] |
b/snf-app/synnefo/db/fixtures/images.json | ||
---|---|---|
4 | 4 |
"pk": 1, |
5 | 5 |
"fields": { |
6 | 6 |
"name": "Debian Base", |
7 |
"owner": 1,
|
|
7 |
"userid": "test",
|
|
8 | 8 |
"created": "2011-02-06 00:00:00", |
9 | 9 |
"updated": "2011-06-29 13:14:00", |
10 | 10 |
"state": "ACTIVE", |
... | ... | |
63 | 63 |
"pk": 2, |
64 | 64 |
"fields": { |
65 | 65 |
"name": "Debian Desktop", |
66 |
"owner": 1,
|
|
66 |
"userid": "test",
|
|
67 | 67 |
"created": "2011-06-29 19:33:00", |
68 | 68 |
"updated": "2011-02-06 19:33:00", |
69 | 69 |
"state": "ACTIVE", |
... | ... | |
122 | 122 |
"pk": 3, |
123 | 123 |
"fields": { |
124 | 124 |
"name": "Ubuntu", |
125 |
"owner": 1,
|
|
125 |
"userid": "test",
|
|
126 | 126 |
"created": "2011-06-30 16:45:00", |
127 | 127 |
"updated": "2011-06-30 16:45:00", |
128 | 128 |
"state": "ACTIVE", |
... | ... | |
181 | 181 |
"pk": 4, |
182 | 182 |
"fields": { |
183 | 183 |
"name": "Kubuntu", |
184 |
"owner": 1,
|
|
184 |
"userid": "test",
|
|
185 | 185 |
"created": "2011-06-30 16:45:00", |
186 | 186 |
"updated": "2011-06-30 16:45:00", |
187 | 187 |
"state": "ACTIVE", |
... | ... | |
240 | 240 |
"pk": 5, |
241 | 241 |
"fields": { |
242 | 242 |
"name": "Fedora Desktop", |
243 |
"owner": 1,
|
|
243 |
"userid": "test",
|
|
244 | 244 |
"created": "2011-06-30 18:54:00", |
245 | 245 |
"updated": "2011-06-30 18:54:00", |
246 | 246 |
"state": "ACTIVE", |
... | ... | |
299 | 299 |
"pk": 6, |
300 | 300 |
"fields": { |
301 | 301 |
"name": "CentOS", |
302 |
"owner": 1,
|
|
302 |
"userid": "test",
|
|
303 | 303 |
"created": "2011-06-09 00:00:00", |
304 | 304 |
"updated": "2011-06-09 00:00:00", |
305 | 305 |
"state": "ACTIVE", |
... | ... | |
358 | 358 |
"pk": 7, |
359 | 359 |
"fields": { |
360 | 360 |
"name": "Windows", |
361 |
"owner": 1,
|
|
361 |
"userid": "test",
|
|
362 | 362 |
"created": "2011-06-09 00:00:00", |
363 | 363 |
"updated": "2011-06-09 00:00:00", |
364 | 364 |
"state": "ACTIVE", |
/dev/null | ||
---|---|---|
1 |
[ |
|
2 |
{ |
|
3 |
"model": "db.SynnefoUser", |
|
4 |
"pk": 1, |
|
5 |
"fields": { |
|
6 |
"name": "testdbuser", |
|
7 |
"realname" :"test db user", |
|
8 |
"uniq" :"test@synnefo.gr", |
|
9 |
"auth_token": "5e41595e9e884543fa048e07c1094d74", |
|
10 |
"auth_token_created": "2011-05-10", |
|
11 |
"auth_token_expires": "2015-05-10", |
|
12 |
"type": "ADMIN", |
|
13 |
"created": "2011-05-10" |
|
14 |
} |
|
15 |
}, |
|
16 |
{ |
|
17 |
"model": "db.SynnefoUser", |
|
18 |
"pk": 2, |
|
19 |
"fields": { |
|
20 |
"name": "helpdesk", |
|
21 |
"realname" :"HelpDesk user", |
|
22 |
"uniq" :"helpdesk@synnefo.grnet.gr", |
|
23 |
"auth_token": "cd7c3c159507515ac495c54bee3ff8d1", |
|
24 |
"auth_token_created": "2011-05-10", |
|
25 |
"auth_token_expires": "2015-05-10", |
|
26 |
"type": "HELPDESK", |
|
27 |
"created": "2011-05-10" |
|
28 |
} |
|
29 |
} |
|
30 |
] |
b/snf-app/synnefo/db/fixtures/vms.json | ||
---|---|---|
13 | 13 |
"model": "db.VirtualMachine", |
14 | 14 |
"pk": 1001, |
15 | 15 |
"fields": { |
16 |
"owner": 1,
|
|
16 |
"userid": "test",
|
|
17 | 17 |
"name": "snf-1001", |
18 | 18 |
"created": "2011-02-06 00:00:00", |
19 | 19 |
"updated": "2011-02-06 00:00:00", |
... | ... | |
28 | 28 |
"model": "db.VirtualMachine", |
29 | 29 |
"pk": 1002, |
30 | 30 |
"fields": { |
31 |
"owner": 1,
|
|
31 |
"userid": "test",
|
|
32 | 32 |
"name": "snf-1002", |
33 | 33 |
"created": "2011-02-10 00:00:00", |
34 | 34 |
"updated": "2011-02-10 00:00:00", |
... | ... | |
43 | 43 |
"model": "db.VirtualMachine", |
44 | 44 |
"pk": 1003, |
45 | 45 |
"fields": { |
46 |
"owner": 1,
|
|
46 |
"userid": "test",
|
|
47 | 47 |
"name": "snf-1003", |
48 | 48 |
"created": "2011-02-10 00:00:00", |
49 | 49 |
"updated": "2011-02-10 00:00:00", |
... | ... | |
57 | 57 |
{ |
58 | 58 |
"model": "db.VirtualMachine", |
59 | 59 |
"pk": 1004, |
60 |
"owner": 1,
|
|
60 |
"userid": "test",
|
|
61 | 61 |
"fields": { |
62 |
"owner": 1,
|
|
62 |
"userid": "test",
|
|
63 | 63 |
"name": "snf-1004", |
64 | 64 |
"created": "2011-02-10 00:00:00", |
65 | 65 |
"updated": "2011-02-10 00:00:00", |
... | ... | |
87 | 87 |
"created": "2011-02-10 00:00:00", |
88 | 88 |
"updated": "2011-02-10 00:00:00", |
89 | 89 |
"state": "ACTIVE", |
90 |
"owner": 1,
|
|
90 |
"userid": "test",
|
|
91 | 91 |
"sourcevm": 1001 |
92 | 92 |
} |
93 | 93 |
} |
b/snf-app/synnefo/db/migrations/0026_auto__del_legacy_fields.py | ||
---|---|---|
23 | 23 |
# Deleting model 'Limit' |
24 | 24 |
db.delete_table('db_limit') |
25 | 25 |
|
26 |
# Deleting model 'Debit' |
|
27 |
db.delete_table('db_debit') |
|
28 |
|
|
26 | 29 |
# Deleting field 'SynnefoUser.credit' |
27 | 30 |
db.delete_column('db_synnefouser', 'credit') |
28 | 31 |
|
... | ... | |
81 | 84 |
)) |
82 | 85 |
db.send_create_signal('db', ['Limit']) |
83 | 86 |
|
87 |
# Adding model 'Debit' |
|
88 |
db.create_table('db_debit', ( |
|
89 |
('description', self.gf('django.db.models.fields.TextField')()), |
|
90 |
('when', self.gf('django.db.models.fields.DateTimeField')()), |
|
91 |
('vm', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['db.VirtualMachine'])), |
|
92 |
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['db.SynnefoUser'])), |
|
93 |
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
94 |
)) |
|
95 |
db.send_create_signal('db', ['Debit']) |
|
96 |
|
|
84 | 97 |
# Adding field 'SynnefoUser.credit' |
85 | 98 |
db.add_column('db_synnefouser', 'credit', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False) |
86 | 99 |
|
... | ... | |
89 | 102 |
|
90 | 103 |
|
91 | 104 |
models = { |
92 |
'db.debit': { |
|
93 |
'Meta': {'object_name': 'Debit'}, |
|
94 |
'description': ('django.db.models.fields.TextField', [], {}), |
|
95 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
96 |
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}), |
|
97 |
'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']"}), |
|
98 |
'when': ('django.db.models.fields.DateTimeField', [], {}) |
|
99 |
}, |
|
100 | 105 |
'db.disk': { |
101 | 106 |
'Meta': {'object_name': 'Disk'}, |
102 | 107 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
... | ... | |
193 | 198 |
'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}), |
194 | 199 |
'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
195 | 200 |
'buildpercentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
196 |
'charged': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 1, 23, 13, 34, 33, 344061)'}),
|
|
201 |
'charged': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 1, 23, 14, 29, 44, 160884)'}),
|
|
197 | 202 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
198 | 203 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
199 | 204 |
'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}), |
b/snf-app/synnefo/db/migrations/0027_auto__del_disk__add_field_userid.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 |
# Deleting model 'Disk' |
|
12 |
db.delete_table('db_disk') |
|
13 |
|
|
14 |
# Adding field 'VirtualMachine.userid' |
|
15 |
db.add_column('db_virtualmachine', 'userid', self.gf('django.db.models.fields.CharField')(default='', max_length=100), keep_default=False) |
|
16 |
|
|
17 |
# Adding field 'Image.userid' |
|
18 |
db.add_column('db_image', 'userid', self.gf('django.db.models.fields.CharField')(default='', max_length=100), keep_default=False) |
|
19 |
|
|
20 |
# Adding field 'Network.userid' |
|
21 |
db.add_column('db_network', 'userid', self.gf('django.db.models.fields.CharField')(default='', max_length=100), keep_default=False) |
|
22 |
|
|
23 |
|
|
24 |
def backwards(self, orm): |
|
25 |
|
|
26 |
# Adding model 'Disk' |
|
27 |
db.create_table('db_disk', ( |
|
28 |
('vm', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['db.VirtualMachine'], null=True, blank=True)), |
|
29 |
('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)), |
|
30 |
('name', self.gf('django.db.models.fields.CharField')(max_length=255)), |
|
31 |
('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), |
|
32 |
('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['db.SynnefoUser'], null=True, blank=True)), |
|
33 |
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
|
34 |
('size', self.gf('django.db.models.fields.PositiveIntegerField')()), |
|
35 |
)) |
|
36 |
db.send_create_signal('db', ['Disk']) |
|
37 |
|
|
38 |
# Deleting field 'VirtualMachine.userid' |
|
39 |
db.delete_column('db_virtualmachine', 'userid') |
|
40 |
|
|
41 |
# Deleting field 'Image.userid' |
|
42 |
db.delete_column('db_image', 'userid') |
|
43 |
|
|
44 |
# Deleting field 'Network.userid' |
|
45 |
db.delete_column('db_network', 'userid') |
|
46 |
|
|
47 |
|
|
48 |
models = { |
|
49 |
'db.flavor': { |
|
50 |
'Meta': {'unique_together': "(('cpu', 'ram', 'disk'),)", 'object_name': 'Flavor'}, |
|
51 |
'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
52 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
53 |
'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
54 |
'disk_template': ('django.db.models.fields.CharField', [], {'default': "'drbd'", 'max_length': '32'}), |
|
55 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
56 |
'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'}) |
|
57 |
}, |
|
58 |
'db.image': { |
|
59 |
'Meta': {'object_name': 'Image'}, |
|
60 |
'backend_id': ('django.db.models.fields.CharField', [], {'default': "'debian_base'", 'max_length': '50'}), |
|
61 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
62 |
'format': ('django.db.models.fields.CharField', [], {'default': "'dump'", 'max_length': '30'}), |
|
63 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
64 |
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
|
65 |
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True', 'blank': 'True'}), |
|
66 |
'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
|
67 |
'sourcevm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']", 'null': 'True'}), |
|
68 |
'state': ('django.db.models.fields.CharField', [], {'max_length': '30'}), |
Also available in: Unified diff