from django.utils import simplejson as json
from django.core.urlresolvers import reverse
-from astakos.im.faults import BadRequest, Unauthorized, InternalServerError, Fault
+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.util import epoch
logger = logging.getLogger(__name__)
+format = ('%a, %d %b %Y %H:%M:%S GMT')
def render_fault(request, fault):
if isinstance(fault, InternalServerError) and settings.DEBUG:
response['Content-Length'] = len(response.content)
return response
-def api_method(http_method=None, token_required=False, perms=[]):
+def api_method(http_method=None, token_required=False, perms=None):
"""Decorator function for views that implement an API method."""
+ if not perms:
+ perms = []
def decorator(func):
@wraps(func)
try:
user = AstakosUser.objects.get(auth_token=x_auth_token)
if not user.has_perms(perms):
- raise Unauthorized('Unauthorized request')
+ raise Forbidden('Unauthorized request')
except AstakosUser.DoesNotExist, e:
raise Unauthorized('Invalid X-Auth-Token')
kwargs['user'] = user
return HttpResponse(content=data, mimetype=mimetype)
-@api_method(http_method='GET', token_required=True, perms=['astakos.im.can_find_userid'])
-def find_userid(request):
- # Normal Response Codes: 204
+@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
+def get_user_by_email(request, user=None):
+ # Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# badRequest (400)
# unauthorised (401)
- email = request.GET.get('email')
+ # forbidden (403)
+ # itemNotFound (404)
+ email = request.GET.get('name')
if not email:
raise BadRequest('Email missing')
try:
- user = AstakosUser.objects.get(email = email, is_active=True)
+ user = AstakosUser.objects.get(email = email)
except AstakosUser.DoesNotExist, e:
- raise BadRequest('Invalid email')
+ raise ItemNotFound('Invalid email')
+
+ if not user.is_active:
+ raise ItemNotFound('Inactive user')
else:
response = HttpResponse()
- response.status=204
- user_info = {'userid':user.username}
+ 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
-@api_method(http_method='GET', token_required=True, perms=['astakos.im.can_find_email'])
-def find_email(request):
- # Normal Response Codes: 204
+@api_method(http_method='GET', token_required=True, perms=['can_access_userinfo'])
+def get_user_by_username(request, user_id, user=None):
+ # Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# badRequest (400)
# unauthorised (401)
- userid = request.GET.get('userid')
- if not userid:
- raise BadRequest('Userid missing')
+ # forbidden (403)
+ # itemNotFound (404)
try:
- user = AstakosUser.objects.get(username = userid)
+ user = AstakosUser.objects.get(username = user_id)
except AstakosUser.DoesNotExist, e:
- raise BadRequest('Invalid userid')
+ raise ItemNotFound('Invalid userid')
else:
response = HttpResponse()
- response.status=204
- user_info = {'userid':user.email}
+ 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
+ return response
\ No newline at end of file
class InternalServerError(Fault):
code = 500
+
+class Forbidden(Fault):
+ code = 403
+
+class ItemNotFound(Fault):
+ code = 404
\ No newline at end of file
"fields": {
"name": "shibboleth"
}
+ },
+ {
+ "model": "auth.group",
+ "pk": 4,
+ "fields": {
+ "name": "helpdesk"
+ }
}
]
from datetime import datetime
from django.utils.timesince import timesince, timeuntil
+from django.contrib.auth.models import Permission
+from django.contrib.contenttypes.models import ContentType
from astakos.im.models import AstakosUser
+content_type = None
def get_user(email_or_id, **kwargs):
try:
return timesince(d) + ' ago'
else:
return 'in ' + timeuntil(d)
+
+def get_astakosuser_content_type():
+ if content_type:
+ return content_type
+
+ try:
+ return ContentType.objects.get(app_label='im',
+ model='astakosuser')
+ except:
+ return content_type
+
+def add_user_permission(user, pname):
+ content_type = get_astakosuser_content_type()
+ if user.has_perm(pname):
+ return 0, None
+ p, created = Permission.objects.get_or_create(codename=pname,
+ name=pname.capitalize(),
+ content_type=content_type)
+ user.user_permissions.add(p)
+ return 1, created
+
+def add_group_permission(group, pname):
+ content_type = get_astakosuser_content_type()
+ if pname in [p.codename for p in group.permissions.all()]:
+ return 0, None
+ content_type = ContentType.objects.get(app_label='im',
+ model='astakosuser')
+ p, created = Permission.objects.get_or_create(codename=pname,
+ name=pname.capitalize(),
+ content_type=content_type)
+ group.permissions.add(p)
+ return 1, created
+
+def remove_user_permission(user, pname):
+ content_type = get_astakosuser_content_type()
+ if user.has_perm(pname):
+ return 0
+ try:
+ p = Permission.objects.get(codename=pname,
+ content_type=content_type)
+ user.user_permissions.remove(p)
+ return 1
+ except Permission.DoesNotExist, e:
+ return -1
+
+def remove_group_permission(group, pname):
+ content_type = get_astakosuser_content_type()
+ if pname not in [p.codename for p in group.permissions.all()]:
+ return 0
+ try:
+ p = Permission.objects.get(codename=pname,
+ content_type=content_type)
+ group.permissions.remove(p)
+ return 1
+ except Permission.DoesNotExist, e:
+ return -1
\ No newline at end of file
from os.path import abspath
from django.core.management.base import BaseCommand, CommandError
-
from django.contrib.auth.models import Group
+from ._common import add_group_permission
+
class Command(BaseCommand):
- args = "<name>"
+ args = "<groupname> [<permission> ...]"
help = "Insert group"
def handle(self, *args, **options):
- if len(args) != 1:
+ if len(args) < 1:
raise CommandError("Invalid number of arguments")
name = args[0].decode('utf8')
except Group.DoesNotExist, e:
group = Group(name=name)
group.save()
-
- msg = "Created group id %d" % (group.id,)
- self.stdout.write(msg + '\n')
+ msg = "Created group id %d" % (group.id,)
+ self.stdout.write(msg + '\n')
+ try:
+ for pname in args[1:]:
+ r, created = add_group_permission(group, pname)
+ if created:
+ self.stdout.write('Permission: %s created successfully\n' % pname)
+ if r == 0:
+ self.stdout.write('Group has already permission: %s\n' % pname)
+ else:
+ self.stdout.write('Permission: %s added successfully\n' % pname)
+ 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 optparse import make_option
+
+from django.core.management.base import BaseCommand, CommandError
+from django.contrib.auth.models import Group, Permission
+from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import ValidationError
+
+from astakos.im.models import AstakosUser
+from ._common import add_group_permission
+
+class Command(BaseCommand):
+ args = "<groupname> <permission> [<permissions> ...]"
+ help = "Add group permissions"
+
+ def handle(self, *args, **options):
+ if len(args) < 2:
+ raise CommandError("Please provide a group name and at least one permission")
+
+ group = None
+ try:
+ if args[0].isdigit():
+ group = Group.objects.get(id=args[0])
+ else:
+ group = Group.objects.get(name=args[0])
+ except Group.DoesNotExist, e:
+ raise CommandError("Invalid group")
+
+ try:
+ content_type = ContentType.objects.get(app_label='im',
+ model='astakosuser')
+ for pname in args[1:]:
+ r, created = add_group_permission(group, pname)
+ if created:
+ self.stdout.write('Permission: %s created successfully\n' % pname)
+ if r == 0:
+ self.stdout.write('Group has already permission: %s\n' % pname)
+ else:
+ self.stdout.write('Permission: %s added successfully\n' % pname)
+ except Exception, e:
+ raise CommandError(e)
\ No newline at end of file
from django.core.management.base import BaseCommand, CommandError
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
+from django.contrib.auth.models import Group, Permission
+from django.contrib.contenttypes.models import ContentType
from astakos.im.models import AstakosUser
from astakos.im.util import reserved_email
+from ._common import add_user_permission
+
class Command(BaseCommand):
args = "<email> <first name> <last name> <affiliation>"
help = "Create a user"
make_option('--password',
dest='password',
metavar='PASSWORD',
- help="Set user's password")
+ help="Set user's password"),
+ make_option('--add-group',
+ dest='add-group',
+ help="Add user group"),
+ make_option('--add-permission',
+ dest='add-permission',
+ help="Add user permission")
)
def handle(self, *args, **options):
if options['password'] is None:
msg += " with password '%s'" % (password,)
self.stdout.write(msg + '\n')
+
+ groupname = options.get('add-group')
+ if groupname is not None:
+ try:
+ group = Group.objects.get(name=groupname)
+ user.groups.add(group)
+ self.stdout.write('Group: %s added successfully\n' % groupname)
+ except Group.DoesNotExist, e:
+ self.stdout.write('Group named %s does not exist\n' % groupname)
+
+ pname = options.get('add-permission')
+ if pname is not None:
+ try:
+ r, created = add_user_permission(user, pname)
+ if created:
+ self.stdout.write('Permission: %s created successfully\n' % pname)
+ if r > 0:
+ self.stdout.write('Permission: %s added successfully\n' % pname)
+ elif r==0:
+ self.stdout.write('User has already permission: %s\n' % pname)
+ except Exception, e:
+ raise CommandError(e)
\ No newline at end of file
groups = Group.objects.all()
- labels = ('id', 'name')
- columns = (1, 2)
+ labels = ('id', 'name', 'permissions')
+ columns = (3, 12, 50)
if not options['csv']:
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
self.stdout.write(sep + '\n')
for group in groups:
- fields = (str(group.id), group.name)
+ fields = (str(group.id), group.name,
+ ','.join(p.codename for p in group.permissions.all()))
if options['csv']:
line = '|'.join(fields)
if options['pending']:
users = users.filter(is_active=False)
- labels = ('id', 'email', 'real name', 'affiliation', 'active', 'admin', 'provider')
- columns = (3, 24, 24, 12, 6, 5, 12)
+ labels = ('id', 'email', 'real name', 'active', 'admin', 'provider', 'groups')
+ columns = (3, 24, 24, 6, 5, 12, 24)
if not options['csv']:
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
id = str(user.id)
active = format_bool(user.is_active)
admin = format_bool(user.is_superuser)
- fields = (id, user.email, user.realname, user.affiliation, active,
- admin, user.provider)
+ fields = (id, user.email, user.realname, active, admin, user.provider,
+ ','.join([g.name for g in user.groups.all()]))
if options['csv']:
line = '|'.join(fields)
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
-from django.contrib.auth.models import Group
+from django.contrib.auth.models import Group, Permission
+from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from astakos.im.models import AstakosUser
+from ._common import remove_user_permission, add_user_permission
class Command(BaseCommand):
args = "<user ID>"
make_option('--delete-group',
dest='delete-group',
help="Delete user group"),
+ make_option('--add-permission',
+ dest='add-permission',
+ help="Add user permission"),
+ make_option('--delete-permission',
+ dest='delete-permission',
+ help="Delete user permission"),
)
def handle(self, *args, **options):
group = Group.objects.get(name=groupname)
user.groups.add(group)
except Group.DoesNotExist, e:
- raise CommandError("Group named %s does not exist." % groupname)
+ self.stdout.write("Group named %s does not exist\n" % groupname)
groupname = options.get('delete-group')
if groupname is not None:
group = Group.objects.get(name=groupname)
user.groups.remove(group)
except Group.DoesNotExist, e:
- raise CommandError("Group named %s does not exist." % groupname)
+ self.stdout.write("Group named %s does not exist\n" % groupname)
+
+ pname = options.get('add-permission')
+ if pname is not None:
+ try:
+ r, created = add_user_permission(user, pname)
+ if created:
+ self.stdout.write('Permission: %s created successfully\n' % pname)
+ if r > 0:
+ self.stdout.write('Permission: %s added successfully\n' % pname)
+ elif r==0:
+ self.stdout.write('User has already permission: %s\n' % pname)
+ except Exception, e:
+ raise CommandError(e)
+
+ pname = options.get('delete-permission')
+ if pname is not None and not user.has_perm(pname):
+ try:
+ r = remove_user_permission(user, pname)
+ if r < 0:
+ self.stdout.write('Invalid permission codename: %s\n' % pname)
+ elif r == 0:
+ self.stdout.write('User has not permission: %s\n' % pname)
+ elif r > 0:
+ self.stdout.write('Permission: %s removed successfully\n' % pname)
+ except Exception, e:
+ raise CommandError(e)
level = options.get('level')
if level is not None:
--- /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 django.contrib.auth.models import Group
+from django.core.exceptions import ValidationError
+
+from astakos.im.models import AstakosUser
+from ._common import remove_group_permission
+
+class Command(BaseCommand):
+ args = "<groupname> <permission> [<permissions> ...]"
+ help = "Remove group permissions"
+
+ def handle(self, *args, **options):
+ if len(args) < 2:
+ raise CommandError("Please provide a group name and at least one permission")
+
+ group = None
+ try:
+ if args[0].isdigit():
+ group = Group.objects.get(id=args[0])
+ else:
+ group = Group.objects.get(name=args[0])
+ except Group.DoesNotExist, e:
+ raise CommandError("Invalid group")
+
+ try:
+ for pname in args[1:]:
+ r = remove_group_permission(group, pname)
+ if r < 0:
+ self.stdout.write('Invalid permission codename: %s\n' % pname)
+ elif r == 0:
+ self.stdout.write('Group has not permission: %s\n' % pname)
+ elif r > 0:
+ self.stdout.write('Permission: %s removed successfully\n' % pname)
+ except Exception, e:
+ raise CommandError(e)
\ No newline at end of file
'last login': format_date(user.last_login),
'date joined': format_date(user.date_joined),
'last update': format_date(user.updated),
- 'token': user.auth_token,
+ #'token': user.auth_token,
'token expiration': format_date(user.auth_token_expires),
'invitations': user.invitations,
'invitation level': user.level,
'verified': format_bool(user.is_verified),
'has_credits': format_bool(user.has_credits),
'groups': [elem.name for elem in user.groups.all()],
+ 'permissions': [elem.codename for elem in user.user_permissions.all()],
+ 'group_permissions': user.get_group_permissions(),
'third_party_identifier': user.third_party_identifier,
- 'email_verified': format_bool(user.email_verified)
+ 'email_verified': format_bool(user.email_verified),
+ 'username': user.username
}
if get_latest_terms():
has_signed_terms = user.signed_terms()
url(r'^authenticate/v2/?$', 'authenticate'),
url(r'^get_services/?$', 'get_services'),
url(r'^get_menu/?$', 'get_menu'),
- url(r'^find_userid/?$', 'find_userid'),
- url(r'^find_email/?$', 'find_email'),
+ url(r'^v2.0/users/?$', 'get_user_by_email'),
+ url(r'^v2.0/users/(?P<user_id>.+?)/?$', 'get_user_by_username'),
)