Commit 7bb68cb2 authored by Sofia Papagiannaki's avatar Sofia Papagiannaki
Browse files

Provide api calls for permitting helpdesk users to access user information by...

Provide api calls for permitting helpdesk users to access user information by user email and by username

Refs: #2414
parent 9ab29315
......@@ -45,13 +45,15 @@ 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
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:
......@@ -65,8 +67,10 @@ def render_fault(request, fault):
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)
......@@ -81,7 +85,7 @@ def api_method(http_method=None, token_required=False, perms=[]):
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
......@@ -221,46 +225,68 @@ def get_menu(request, with_extra_links=False, with_signout=True):
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
......@@ -49,3 +49,9 @@ class Unauthorized(Fault):
class InternalServerError(Fault):
code = 500
class Forbidden(Fault):
code = 403
class ItemNotFound(Fault):
code = 404
\ No newline at end of file
......@@ -19,5 +19,12 @@
"fields": {
"name": "shibboleth"
}
},
{
"model": "auth.group",
"pk": 4,
"fields": {
"name": "helpdesk"
}
}
]
......@@ -34,9 +34,12 @@
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:
......@@ -59,3 +62,59 @@ def format_date(d):
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
......@@ -39,15 +39,16 @@ from time import time
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')
......@@ -58,6 +59,16 @@ class Command(BaseCommand):
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
# 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
......@@ -41,10 +41,14 @@ from uuid import uuid4
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"
......@@ -63,7 +67,13 @@ class Command(BaseCommand):
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):
......@@ -108,3 +118,25 @@ class Command(BaseCommand):
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
......@@ -58,8 +58,8 @@ class Command(BaseCommand):
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))
......@@ -68,7 +68,8 @@ class Command(BaseCommand):
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)
......
......@@ -64,8 +64,8 @@ class Command(BaseCommand):
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))
......@@ -77,8 +77,8 @@ class Command(BaseCommand):
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)
......
......@@ -34,10 +34,12 @@
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>"
......@@ -87,6 +89,12 @@ class Command(BaseCommand):
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):
......@@ -121,7 +129,7 @@ class Command(BaseCommand):
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:
......@@ -129,7 +137,33 @@ class Command(BaseCommand):
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:
......
# 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
......@@ -67,7 +67,7 @@ class Command(BaseCommand):
'last login': format_date(user.last_login),
'date joined': format_date(user.date_joined),
'last update': format_date(user