ASTAKOS_MODERATION_ENABLED True If False and invitations are not enabled newly created user will be automatically accepted
ASTAKOS_BASEURL \http://pithos.dev.grnet.gr Astakos baseurl
ASTAKOS_SITENAME GRNET Cloud Service name that appears in emails
-ASTAKOS_CLOUD_SERVICES ({'icon': 'home-icon.png', 'id': 'cloud', 'name': 'grnet cloud', 'url': '/'}, Cloud services appear in the horizontal bar
- {'id': 'okeanos', 'name': 'cyclades', 'url': '/okeanos.html'},
- {'id': 'pithos', 'name': 'pithos+', 'url': '/ui/'})
ASTAKOS_RECAPTCHA_ENABLED True Enable recaptcha
ASTAKOS_RECAPTCHA_PUBLIC_KEY Recaptcha public key obtained after registration here: http://recaptcha.net
ASTAKOS_RECAPTCHA_PRIVATE_KEY Recaptcha private key obtained after registration here: http://recaptcha.net
--- /dev/null
+# Copyright 2011-2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+from django.http import HttpResponse
+from django.utils import simplejson as json
+
+from astakos.im.models import AstakosUser
+from astakos.im.faults import ItemNotFound
+
+format = ('%a, %d %b %Y %H:%M:%S GMT')
+
+def _get_user_by_username(user_id):
+ try:
+ user = AstakosUser.objects.get(username = user_id)
+ except AstakosUser.DoesNotExist, e:
+ raise ItemNotFound('Invalid userid')
+ else:
+ response = HttpResponse()
+ response.status=200
+ user_info = {'id':user.id,
+ 'username':user.username,
+ 'email':[user.email],
+ 'name':user.realname,
+ 'auth_token_created':user.auth_token_created.strftime(format),
+ 'auth_token_expires':user.auth_token_expires.strftime(format),
+ 'has_credits':user.has_credits,
+ 'enabled':user.is_active,
+ 'groups':[g.name for g in user.groups.all()]}
+ response.content = json.dumps(user_info)
+ response['Content-Type'] = 'application/json; charset=UTF-8'
+ response['Content-Length'] = len(response.content)
+ return response
+
+def _get_user_by_email(email):
+ if not email:
+ raise BadRequest('Email missing')
+ try:
+ user = AstakosUser.objects.get(email = email)
+ except AstakosUser.DoesNotExist, e:
+ raise ItemNotFound('Invalid email')
+
+ if not user.is_active:
+ raise ItemNotFound('Inactive user')
+ else:
+ response = HttpResponse()
+ response.status=200
+ user_info = {'id':user.id,
+ 'username':user.username,
+ 'email':[user.email],
+ 'enabled':user.is_active,
+ 'name':user.realname,
+ 'auth_token_created':user.auth_token_created.strftime(format),
+ 'auth_token_expires':user.auth_token_expires.strftime(format),
+ 'has_credits':user.has_credits,
+ 'groups':[g.name for g in user.groups.all()],
+ 'user_permissions':[p.codename for p in user.user_permissions.all()]}
+ response.content = json.dumps(user_info)
+ response['Content-Type'] = 'application/json; charset=UTF-8'
+ response['Content-Length'] = len(response.content)
+ return response
\ No newline at end of file
from time import time, mktime
from urllib import quote
from urlparse import urlparse
+from collections import defaultdict
from django.conf import settings
from django.http import HttpResponse
from django.utils import simplejson as json
from django.core.urlresolvers import reverse
-from astakos.im.faults import BadRequest, Unauthorized, InternalServerError, \
-Fault, ItemNotFound, Forbidden
-from astakos.im.models import AstakosUser
-from astakos.im.settings import CLOUD_SERVICES, INVITATIONS_ENABLED, COOKIE_NAME, \
-EMAILCHANGE_ENABLED
+from astakos.im.api.faults import *
+from astakos.im.models import AstakosUser, Service
+from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
from astakos.im.util import epoch
+from astakos.im.api import _get_user_by_email, _get_user_by_username
logger = logging.getLogger(__name__)
format = ('%a, %d %b %Y %H:%M:%S GMT')
raise Unauthorized('Access denied')
try:
user = AstakosUser.objects.get(auth_token=x_auth_token)
+ ## Check if the token has expired.
+ #if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
+ # raise Unauthorized('Authentication expired')
if not user.has_perms(perms):
raise Forbidden('Unauthorized request')
except AstakosUser.DoesNotExist, e:
@api_method(http_method='GET')
def get_services(request):
callback = request.GET.get('callback', None)
- data = json.dumps(CLOUD_SERVICES)
+ services = Service.objects.all()
+ data = tuple({'name':s.name, 'url':s.url, 'icon':s.icon} for s in services)
+ data = json.dumps(data)
mimetype = 'application/json'
if callback:
return HttpResponse(content=data, mimetype=mimetype)
-@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
+@api_method(http_method='GET', token_required=True)
def get_user_by_email(request, user=None):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# forbidden (403)
# itemNotFound (404)
email = request.GET.get('name')
- if not email:
- raise BadRequest('Email missing')
- try:
- user = AstakosUser.objects.get(email = email)
- except AstakosUser.DoesNotExist, e:
- raise ItemNotFound('Invalid email')
-
- if not user.is_active:
- raise ItemNotFound('Inactive user')
- else:
- response = HttpResponse()
- response.status=200
- user_info = {'id':user.id,
- 'username':user.username,
- 'email':[user.email],
- 'enabled':user.is_active,
- 'name':user.realname,
- 'auth_token_created':user.auth_token_created.strftime(format),
- 'auth_token_expires':user.auth_token_expires.strftime(format),
- 'has_credits':user.has_credits,
- 'groups':[g.name for g in user.groups.all()],
- 'user_permissions':[p.codename for p in user.user_permissions.all()],
- 'group_permissions': list(user.get_group_permissions())}
- response.content = json.dumps(user_info)
- response['Content-Type'] = 'application/json; charset=UTF-8'
- response['Content-Length'] = len(response.content)
- return response
+ return _get_user_by_email(email)
-@api_method(http_method='GET', token_required=True, perms=['can_access_userinfo'])
+@api_method(http_method='GET', token_required=True)
def get_user_by_username(request, user_id, user=None):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# unauthorised (401)
# forbidden (403)
# itemNotFound (404)
- try:
- user = AstakosUser.objects.get(username = user_id)
- except AstakosUser.DoesNotExist, e:
- raise ItemNotFound('Invalid userid')
- else:
- response = HttpResponse()
- response.status=200
- user_info = {'id':user.id,
- 'username':user.username,
- 'email':[user.email],
- 'name':user.realname,
- 'auth_token_created':user.auth_token_created.strftime(format),
- 'auth_token_expires':user.auth_token_expires.strftime(format),
- 'has_credits':user.has_credits,
- 'enabled':user.is_active,
- 'groups':[g.name for g in user.groups.all()]}
- response.content = json.dumps(user_info)
- response['Content-Type'] = 'application/json; charset=UTF-8'
- response['Content-Length'] = len(response.content)
- return response
\ No newline at end of file
+ return _get_user_by_username(user_id)
\ No newline at end of file
--- /dev/null
+# Copyright 2011-2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+import logging
+import urllib
+
+from functools import wraps
+from traceback import format_exc
+from time import time, mktime
+from urllib import quote
+from urlparse import urlparse
+from collections import defaultdict
+
+from django.conf import settings
+from django.http import HttpResponse
+from django.core.urlresolvers import reverse
+from django.views.decorators.csrf import csrf_exempt
+
+from astakos.im.api.faults import *
+from astakos.im.models import AstakosUser, Service
+from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
+from astakos.im.util import epoch
+from astakos.im.forms import FeedbackForm
+from astakos.im.functions import send_feedback as send_feedback_func, SendMailError
+
+logger = logging.getLogger(__name__)
+
+def render_fault(request, fault):
+ if isinstance(fault, InternalServerError) and settings.DEBUG:
+ fault.details = format_exc(fault)
+
+ request.serialization = 'text'
+ data = fault.message + '\n'
+ if fault.details:
+ data += '\n' + fault.details
+ response = HttpResponse(data, status=fault.code)
+ response['Content-Length'] = len(response.content)
+ return response
+
+def api_method(http_method=None, token_required=False):
+ """Decorator function for views that implement an API method."""
+ def decorator(func):
+ @wraps(func)
+ def wrapper(request, *args, **kwargs):
+ try:
+ if http_method and request.method != http_method:
+ raise BadRequest('Method not allowed.')
+ x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
+ if token_required:
+ if not x_auth_token:
+ raise Unauthorized('Access denied')
+ try:
+ service = Service.objects.get(auth_token=x_auth_token)
+
+ # Check if the token has expired.
+ if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
+ raise Unauthorized('Authentication expired')
+ except Service.DoesNotExist, e:
+ raise Unauthorized('Invalid X-Auth-Token')
+ response = func(request, *args, **kwargs)
+ return response
+ except Fault, fault:
+ return render_fault(request, fault)
+ except BaseException, e:
+ logger.exception('Unexpected error: %s' % e)
+ fault = InternalServerError('Unexpected error')
+ return render_fault(request, fault)
+ return wrapper
+ return decorator
+
+@api_method(http_method='GET', token_required=True)
+def get_user_by_email(request, user=None):
+ # Normal Response Codes: 200
+ # Error Response Codes: internalServerError (500)
+ # badRequest (400)
+ # unauthorised (401)
+ # forbidden (403)
+ # itemNotFound (404)
+ email = request.GET.get('name')
+ return _get_user_by_email(email)
+
+@api_method(http_method='GET', token_required=True)
+def get_user_by_username(request, user_id, user=None):
+ # Normal Response Codes: 200
+ # Error Response Codes: internalServerError (500)
+ # badRequest (400)
+ # unauthorised (401)
+ # forbidden (403)
+ # itemNotFound (404)
+ return _get_user_by_username(user_id)
+
+@csrf_exempt
+@api_method(http_method='POST', token_required=True)
+def send_feedback(request, email_template_name='im/feedback_mail.txt'):
+ # Normal Response Codes: 200
+ # Error Response Codes: internalServerError (500)
+ # badRequest (400)
+ # unauthorised (401)
+ auth_token = request.POST.get('auth', '')
+ if not auth_token:
+ raise BadRequest('Missing user authentication')
+
+ user = None
+ try:
+ user = AstakosUser.objects.get(auth_token=auth_token)
+ except:
+ pass
+
+ if not user:
+ raise BadRequest('Invalid user authentication')
+
+ form = FeedbackForm(request.POST)
+ if not form.is_valid():
+ raise BadRequest('Invalid data')
+
+ msg = form.cleaned_data['feedback_msg']
+ data = form.cleaned_data['feedback_data']
+ send_feedback_func(msg, data, user, email_template_name)
+ response = HttpResponse(status=200)
+ response['Content-Length'] = len(response.content)
+ return response
\ No newline at end of file
from astakos.im.settings import IM_MODULES, INVITATIONS_ENABLED, IM_STATIC_URL, \
COOKIE_NAME, LOGIN_MESSAGES, PROFILE_EXTRA_LINKS
-from astakos.im.api import get_menu
+from astakos.im.api.admin import get_menu
from astakos.im.util import get_query
from django.conf import settings
--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+from optparse import make_option
+
+from django.core.management.base import BaseCommand, CommandError
+
+from astakos.im.models import Service
+
+class Command(BaseCommand):
+ help = "List g"
+
+ option_list = BaseCommand.option_list + (
+ make_option('-c',
+ action='store_true',
+ dest='csv',
+ default=False,
+ help="Use pipes to separate values"),
+ )
+
+ def handle(self, *args, **options):
+ if args:
+ raise CommandError("Command doesn't accept any arguments")
+
+ services = Service.objects.all()
+
+ labels = ('id', 'name', 'url', 'icon')
+ columns = (3, 12, 40, 40)
+
+ if not options['csv']:
+ line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
+ self.stdout.write(line + '\n')
+ sep = '-' * len(line)
+ self.stdout.write(sep + '\n')
+
+ for service in services:
+ fields = (str(service.id), service.name, service.url, service.icon)
+
+ if options['csv']:
+ line = '|'.join(fields)
+ else:
+ line = ' '.join(f.rjust(w) for f, w in zip(fields, columns))
+
+ self.stdout.write(line.encode('utf8') + '\n')
--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+from django.core.management.base import BaseCommand, CommandError
+
+from astakos.im.models import Service
+
+class Command(BaseCommand):
+ args = "<name> <url> [<icon>]"
+ help = "Register a service"
+
+ def handle(self, *args, **options):
+ if len(args) < 2:
+ raise CommandError("Invalid number of arguments")
+
+ service = Service(name=args[0], url=args[1])
+ if len(args) == 3:
+ service.icon = args[2]
+ try:
+ service.save()
+ self.stdout.write('Service created with token: %s\n' % service.auth_token)
+ except Exception, e:
+ raise CommandError(e)
\ No newline at end of file
--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+from django.core.management.base import BaseCommand, CommandError
+
+from astakos.im.models import Service
+
+class Command(BaseCommand):
+ args = "<name>"
+ help = "Renew service token"
+
+ def handle(self, *args, **options):
+ if len(args) != 1:
+ raise CommandError("Invalid number of arguments")
+
+ try:
+ service = Service.objects.get(name=args[0])
+ service.renew_token()
+ service.save()
+ self.stdout.write('New service token: %s\n' % service.auth_token)
+ except Service.DoesNotExist:
+ raise CommandError("Invalid service name")
+ except Exception, e:
+ raise CommandError(e)
\ No newline at end of file
--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+from django.core.management.base import BaseCommand, CommandError
+
+from astakos.im.models import Service
+
+class Command(BaseCommand):
+ args = "<name>"
+ help = "Unregister a service"
+
+ def handle(self, *args, **options):
+ if len(args) < 1:
+ raise CommandError("Invalid number of arguments")
+
+ try:
+ service = Service.objects.get(name=args[0])
+ service.delete()
+ except Service.DoesNotExist, e:
+ raise CommandError(e)
\ No newline at end of file
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'Service'
+ db.create_table('im_service', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
+ ('url', self.gf('django.db.models.fields.URLField')(max_length=200)),
+ ('icon', self.gf('django.db.models.fields.FilePathField')(max_length=100)),
+ ('auth_token', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)),
+ ('auth_token_created', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+ ('auth_token_expires', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+ ))
+ db.send_create_signal('im', ['Service'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'Service'
+ db.delete_table('im_service')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'im.approvalterms': {
+ 'Meta': {'object_name': 'ApprovalTerms'},
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 5, 21, 15, 21, 13, 352838)', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'im.astakosuser': {
+ 'Meta': {'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
+ 'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
+ 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'im.emailchange': {
+ 'Meta': {'object_name': 'EmailChange'},
+ 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'requested_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 5, 21, 15, 21, 13, 354390)'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchange_user'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
+ },
+ 'im.invitation': {
+ 'Meta': {'object_name': 'Invitation'},
+ 'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
+ 'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'im.service': {
+ 'Meta': {'object_name': 'Service'},
+ 'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'icon': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ }
+ }
+
+ complete_apps = ['im']
expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
return self.requested_at + expiration_date < datetime.now()
+class Service(models.Model):
+ name = models.CharField('Name', max_length=255, unique=True)
+ url = models.FilePathField()
+ icon = models.FilePathField(blank=True)
+ auth_token = models.CharField('Authentication Token', max_length=32,
+ null=True, blank=True)
+ auth_token_created = models.DateTimeField('Token creation date', null=True)
+ auth_token_expires = models.DateTimeField('Token expiration date', null=True)
+
+ def save(self, **kwargs):
+ if not self.id:
+ self.renew_token()
+ self.full_clean()
+ super(Service, self).save(**kwargs)
+
+ def renew_token(self):
+ md5 = hashlib.md5()
+ md5.update(self.name.encode('ascii', 'ignore'))
+ md5.update(self.url.encode('ascii', 'ignore'))
+ md5.update(asctime())
+
+ self.auth_token = b64encode(md5.digest())
+ self.auth_token_created = datetime.now()
+ self.auth_token_expires = self.auth_token_created + \
+ timedelta(hours=AUTH_TOKEN_DURATION)
+
def create_astakos_user(u):
try:
AstakosUser.objects.get(user_ptr=u.pk)
# Set service name
SITENAME = getattr(settings, 'ASTAKOS_SITENAME', 'GRNET Cloud')
-# Set cloud services appear in the horizontal bar
-CLOUD_SERVICES = getattr(settings, 'ASTAKOS_CLOUD_SERVICES', (
- { 'url':'/', 'name':'grnet cloud', 'id':'cloud', 'icon':'home-icon.png' },
- { 'url':'/okeanos.html', 'name':'cyclades', 'id':'okeanos' },
- { 'url':'/ui/', 'name':'pithos+', 'id':'pithos' }))
-
# Set recaptcha keys
RECAPTCHA_PUBLIC_KEY = getattr(settings, 'ASTAKOS_RECAPTCHA_PUBLIC_KEY', '')
RECAPTCHA_PRIVATE_KEY = getattr(settings, 'ASTAKOS_RECAPTCHA_PRIVATE_KEY', '')
url(r'^login/twitter/authenticated/?$', 'twitter.authenticated')
)
-urlpatterns += patterns('astakos.im.api',
+urlpatterns += patterns('astakos.im.api.admin',
url(r'^authenticate/?$', 'authenticate_old'),
- url(r'^authenticate/v2/?$', 'authenticate'),
+ #url(r'^authenticate/v2/?$', 'authenticate'),
url(r'^get_services/?$', 'get_services'),
url(r'^get_menu/?$', 'get_menu'),
- url(r'^v2.0/users/?$', 'get_user_by_email'),
- url(r'^v2.0/users/(?P<user_id>.+?)/?$', 'get_user_by_username'),
+ url(r'^admin/api/v2.0/users/?$', 'get_user_by_email'),
+ url(r'^admin/api/v2.0/users/(?P<user_id>.+?)/?$', 'get_user_by_username'),
)
+
+urlpatterns += patterns('astakos.im.api.service',
+ #url(r'^service/api/v2.0/tokens/(?P<token_id>.+?)/?$', 'validate_token'),
+ url(r'^service/api/v2.0/feedback/?$', 'send_feedback'),
+ url(r'^service/api/v2.0/users/?$', 'get_user_by_email'),
+ url(r'^service/api/v2.0/users/(?P<user_id>.+?)/?$', 'get_user_by_username'),
+)
\ No newline at end of file
# Set service name
#ASTAKOS_SITENAME = 'GRNET Cloud'
-# Set cloud services appear in the horizontal bar
-#ASTAKOS_CLOUD_SERVICES = (
-# { 'url':'/', 'name':'grnet cloud', 'id':'cloud', 'icon':'home-icon.png' },
-# { 'url':'/okeanos.html', 'name':'~okeanos', 'id':'okeanos' },
-# { 'url':'/ui/', 'name':'pithos+', 'id':'pithos' })
-#
-
# Set recaptcha keys
# http://www.google.com/recaptcha/whyrecaptcha
#ASTAKOS_RECAPTCHA_PUBLIC_KEY = ''