Needs more testing, but permission delegation seems to work right now.

This commit is contained in:
Adolfo Gómez García 2015-03-05 15:20:46 +01:00
parent 6387629e7e
commit 2435f589b9
24 changed files with 663 additions and 539 deletions

View File

@ -37,11 +37,13 @@ from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _, activate from django.utils.translation import ugettext as _, activate
from django.conf import settings from django.conf import settings
from uds.REST.handlers import Handler, HandlerError, AccessDenied, NotFound, RequestError, ResponseError from uds.REST.handlers import Handler, HandlerError, AccessDenied, NotFound, RequestError, ResponseError, NotSupportedError
import time import time
import logging import logging
import six
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
__all__ = [str(v) for v in ['Handler', 'Dispatcher']] __all__ = [str(v) for v in ['Handler', 'Dispatcher']]
@ -142,18 +144,20 @@ class Dispatcher(View):
response[k] = val response[k] = val
return response return response
except RequestError as e: except RequestError as e:
return http.HttpResponseBadRequest(unicode(e)) return http.HttpResponseBadRequest(six.text_type(e))
except ResponseError as e: except ResponseError as e:
return http.HttpResponseServerError(unicode(e)) return http.HttpResponseServerError(six.text_type(e))
except NotSupportedError as e:
return http.HttpResponseBadRequest(six.text_type(e))
except AccessDenied as e: except AccessDenied as e:
return http.HttpResponseForbidden(unicode(e)) return http.HttpResponseForbidden(six.text_type(e))
except NotFound as e: except NotFound as e:
return http.HttpResponseNotFound(unicode(e)) return http.HttpResponseNotFound(six.text_type(e))
except HandlerError as e: except HandlerError as e:
return http.HttpResponseBadRequest(unicode(e)) return http.HttpResponseBadRequest(six.text_type(e))
except Exception as e: except Exception as e:
logger.exception('Error processing request') logger.exception('Error processing request')
return http.HttpResponseServerError(unicode(e)) return http.HttpResponseServerError(six.text_type(e))
@staticmethod @staticmethod
def registerSubclasses(classes): def registerSubclasses(classes):

View File

@ -82,6 +82,13 @@ class ResponseError(HandlerError):
pass pass
class NotSupportedError(HandlerError):
'''
Some elements do not support some operations (as searching over an authenticator that does not supports it)
'''
pass
class Handler(object): class Handler(object):
''' '''
REST requests handler base class REST requests handler base class
@ -110,6 +117,7 @@ class Handler(object):
self._kwargs = kwargs self._kwargs = kwargs
self._headers = {} self._headers = {}
self._authToken = None self._authToken = None
self._user = None
if self.authenticated: # Only retrieve auth related data on authenticated handlers if self.authenticated: # Only retrieve auth related data on authenticated handlers
try: try:
self._authToken = self._request.META.get(AUTH_TOKEN_HEADER, '') self._authToken = self._request.META.get(AUTH_TOKEN_HEADER, '')
@ -129,6 +137,8 @@ class Handler(object):
if self.needs_staff and not self.getValue('staff_member'): if self.needs_staff and not self.getValue('staff_member'):
raise AccessDenied() raise AccessDenied()
self._user = self.getUser()
def headers(self): def headers(self):
''' '''
Returns the headers of the REST request (all) Returns the headers of the REST request (all)
@ -253,6 +263,7 @@ class Handler(object):
''' '''
If user is staff member, returns his Associated user on auth If user is staff member, returns his Associated user on auth
''' '''
logger.debug('REST : {}'.format(self._session))
authId = self.getValue('auth') authId = self.getValue('auth')
username = self.getValue('username') username = self.getValue('username')
# Maybe it's root user?? # Maybe it's root user??

View File

@ -39,6 +39,7 @@ from uds.core import auths
from users_groups import Users, Groups from users_groups import Users, Groups
from uds.REST import NotFound from uds.REST import NotFound
from uds.REST.model import ModelHandler from uds.REST.model import ModelHandler
from uds.core.util import permissions
import logging import logging
@ -93,10 +94,12 @@ class Authenticators(ModelHandler):
'small_name': auth.small_name, 'small_name': auth.small_name,
'users_count': auth.users.count(), 'users_count': auth.users.count(),
'type': type_.type(), 'type': type_.type(),
'permission': permissions.getEffectivePermission(self._user, auth)
} }
# Custom "search" method # Custom "search" method
def search(self, item): def search(self, item):
self.ensureAccess(item, permissions.PERMISSION_READ)
try: try:
type_ = self._params['type'] type_ = self._params['type']
if type_ not in ('user', 'group'): if type_ not in ('user', 'group'):
@ -108,7 +111,7 @@ class Authenticators(ModelHandler):
canDoSearch = type_ == 'user' and (auth.searchUsers != auths.Authenticator.searchUsers) or (auth.searchGroups != auths.Authenticator.searchGroups) canDoSearch = type_ == 'user' and (auth.searchUsers != auths.Authenticator.searchUsers) or (auth.searchGroups != auths.Authenticator.searchGroups)
if canDoSearch is False: if canDoSearch is False:
self.invalidRequestException() self.notSupported()
if type_ == 'user': if type_ == 'user':
return auth.searchUsers(term) return auth.searchUsers(term)
@ -121,6 +124,8 @@ class Authenticators(ModelHandler):
from uds.core.Environment import Environment from uds.core.Environment import Environment
authType = auths.factory().lookup(type_) authType = auths.factory().lookup(type_)
self.ensureAccess(authType, permissions.PERMISSION_MANAGEMENT, root=True)
dct = self._params.copy() dct = self._params.copy()
dct['_request'] = self._request dct['_request'] = self._request
res = authType.test(Environment.getTempEnv(), dct) res = authType.test(Environment.getTempEnv(), dct)

View File

@ -35,6 +35,7 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _, ugettext from django.utils.translation import ugettext_lazy as _, ugettext
from uds.models import Network from uds.models import Network
from uds.core.util import net from uds.core.util import net
from uds.core.util import permissions
from uds.core.ui.UserInterface import gui from uds.core.ui.UserInterface import gui
from uds.REST.model import ModelHandler, SaveException from uds.REST.model import ModelHandler, SaveException
@ -89,4 +90,5 @@ class Networks(ModelHandler):
'name': item.name, 'name': item.name,
'net_string': item.net_string, 'net_string': item.net_string,
'networks_count': item.transports.count(), 'networks_count': item.transports.count(),
'permission': permissions.getEffectivePermission(self._user, item)
} }

View File

@ -33,8 +33,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext, ugettext_lazy as _
from django.conf import settings
from uds.models import OSManager from uds.models import OSManager
from uds.core.util import permissions
from uds.REST import NotFound, RequestError from uds.REST import NotFound, RequestError
from uds.core.osmanagers import factory from uds.core.osmanagers import factory
@ -59,8 +59,7 @@ class OsManagers(ModelHandler):
{'deployed_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}} {'deployed_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
] ]
@staticmethod def osmToDict(self, osm):
def osmToDict(osm):
type_ = osm.getType() type_ = osm.getType()
return { return {
'id': osm.uuid, 'id': osm.uuid,
@ -68,10 +67,11 @@ class OsManagers(ModelHandler):
'deployed_count': osm.deployedServices.count(), 'deployed_count': osm.deployedServices.count(),
'type': type_.type(), 'type': type_.type(),
'comments': osm.comments, 'comments': osm.comments,
'permission': permissions.getEffectivePermission(self._user, osm)
} }
def item_as_dict(self, item): def item_as_dict(self, item):
return OsManagers.osmToDict(item) return self.osmToDict(item)
def checkDelete(self, item): def checkDelete(self, item):
if item.deployedServices.count() > 0: if item.deployedServices.count() > 0:

View File

@ -38,7 +38,7 @@ from uds.REST import Handler
from uds.REST import RequestError from uds.REST import RequestError
from uds.core.util import permissions from uds.core.util import permissions
from uds.models import Provider, Service, Authenticator, OSManager, Transport, Network, ServicesPool from uds.models import Provider, Service, Authenticator, OSManager, Transport, Network, ServicePool
from uds.models import User, Group from uds.models import User, Group
import six import six
@ -64,7 +64,7 @@ class Permissions(Handler):
'osmanagers': OSManager, 'osmanagers': OSManager,
'transports': Transport, 'transports': Transport,
'networks': Network, 'networks': Network,
'servicespools': ServicesPool 'servicespools': ServicePool
}.get(arg, None) }.get(arg, None)
if cls is None: if cls is None:
@ -84,16 +84,17 @@ class Permissions(Handler):
entity = perm.user entity = perm.user
res.append({ res.append({
'id': perm.uuid,
'type': kind, 'type': kind,
'auth': entity.manager.uuid, 'auth': entity.manager.uuid,
'auth_name': entity.manager.name, 'auth_name': entity.manager.name,
'id': entity.uuid, 'entity_id': entity.uuid,
'name': entity.name, 'entity_name': entity.name,
'perm': perm.permission, 'perm': perm.permission,
'perm_name': perm.permission_as_string 'perm_name': perm.permission_as_string
}) })
return sorted(res, key=lambda v: v['auth_name'] + v['name']) return sorted(res, key=lambda v: v['auth_name'] + v['entity_name'])
def get(self): def get(self):
''' '''
@ -115,15 +116,16 @@ class Permissions(Handler):
''' '''
Processes post requests Processes post requests
''' '''
if len(self._args) != 4:
raise RequestError('Invalid request')
logger.debug('Put args: {}'.format(self._args)) logger.debug('Put args: {}'.format(self._args))
la = len(self._args)
if la == 5 and self._args[3] == 'add':
perm = { perm = {
'0': permissions.PERMISSION_NONE, '0': permissions.PERMISSION_NONE,
'1': permissions.PERMISSION_READ, '1': permissions.PERMISSION_READ,
'2': permissions.PERMISSION_ALL '2': permissions.PERMISSION_MANAGEMENT,
'3': permissions.PERMISSION_ALL
}.get(self._params.get('perm', '0'), permissions.PERMISSION_NONE) }.get(self._params.get('perm', '0'), permissions.PERMISSION_NONE)
cls = Permissions.getClass(self._args[0]) cls = Permissions.getClass(self._args[0])
@ -131,12 +133,19 @@ class Permissions(Handler):
obj = cls.objects.get(uuid=self._args[1]) obj = cls.objects.get(uuid=self._args[1])
if self._args[2] == 'users': if self._args[2] == 'users':
user = User.objects.get(uuid=self._args[3]) user = User.objects.get(uuid=self._args[4])
permissions.addUserPermission(user, obj, perm) permissions.addUserPermission(user, obj, perm)
elif self._args[2] == 'groups': elif self._args[2] == 'groups':
group = Group.objects.get(uuid=self._args[3]) group = Group.objects.get(uuid=self._args[4])
permissions.addGroupPermission(group, obj, perm) permissions.addGroupPermission(group, obj, perm)
else: else:
raise RequestError('Ivalid request') raise RequestError('Ivalid request')
return Permissions.permsToDict(permissions.getPermissions(obj)) return Permissions.permsToDict(permissions.getPermissions(obj))
elif la == 1 and self._args[0] == 'revoke':
items = self._params.get('items', [])
for permId in items:
permissions.revokePermissionById(permId)
return {}
else:
raise RequestError('Invalid request')

View File

@ -36,6 +36,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from uds.models import Provider, Service, UserService from uds.models import Provider, Service, UserService
from uds.REST.methods.services import Services as DetailServices from uds.REST.methods.services import Services as DetailServices
from uds.core import services from uds.core import services
from uds.core.util import permissions
from uds.REST import NotFound, RequestError from uds.REST import NotFound, RequestError
from uds.REST.model import ModelHandler from uds.REST.model import ModelHandler
@ -87,6 +88,7 @@ class Providers(ModelHandler):
'offers': offers, 'offers': offers,
'type': type_.type(), 'type': type_.type(),
'comments': provider.comments, 'comments': provider.comments,
'permission': permissions.getEffectivePermission(self._user, provider)
} }
def checkDelete(self, item): def checkDelete(self, item):
@ -110,7 +112,9 @@ class Providers(ModelHandler):
''' '''
for s in Service.objects.all(): for s in Service.objects.all():
try: try:
yield DetailServices.serviceToDict(s, True) perm = permissions.getEffectivePermission(self._user, s)
if perm >= permissions.PERMISSION_READ:
yield DetailServices.serviceToDict(s, perm, True)
except Exception: except Exception:
logger.exception('Passed service cause type is unknown') logger.exception('Passed service cause type is unknown')
@ -119,7 +123,9 @@ class Providers(ModelHandler):
Custom method that returns a service by its uuid, no matter who's his daddy Custom method that returns a service by its uuid, no matter who's his daddy
''' '''
try: try:
return DetailServices.serviceToDict(Service.objects.get(uuid=self._args[1]), True) service = Service.objects.get(uuid=self._args[1])
perm = self.ensureAccess(service, permissions.PERMISSION_READ) # Ensures that we can read this item
return DetailServices.serviceToDict(service, perm, True)
except Exception: except Exception:
raise RequestError(ugettext('Service not found')) raise RequestError(ugettext('Service not found'))
@ -128,6 +134,7 @@ class Providers(ModelHandler):
Custom method that swaps maintenance mode state for a provider Custom method that swaps maintenance mode state for a provider
:param item: :param item:
''' '''
self.ensureAccess(item, permissions.PERMISSION_MANAGEMENT)
item.maintenance_mode = not item.maintenance_mode item.maintenance_mode = not item.maintenance_mode
item.save() item.save()
return self.item_as_dict(item) return self.item_as_dict(item)
@ -137,6 +144,9 @@ class Providers(ModelHandler):
logger.debug('Type: {}'.format(type_)) logger.debug('Type: {}'.format(type_))
spType = services.factory().lookup(type_) spType = services.factory().lookup(type_)
self.ensureAccess(spType, permissions.PERMISSION_MANAGEMENT, root=True)
logger.debug('spType: {}'.format(spType)) logger.debug('spType: {}'.format(spType))
res = spType.test(Environment.getTempEnv(), self._params) res = spType.test(Environment.getTempEnv(), self._params)
if res[0]: if res[0]:

View File

@ -39,6 +39,7 @@ from uds.models import Service, UserService
from uds.core.services import Service as coreService from uds.core.services import Service as coreService
from uds.core.util import log from uds.core.util import log
from uds.core.util import permissions
from uds.core.Environment import Environment from uds.core.Environment import Environment
from uds.REST.model import DetailHandler from uds.REST.model import DetailHandler
from uds.REST import NotFound, ResponseError, RequestError from uds.REST import NotFound, ResponseError, RequestError
@ -55,7 +56,22 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
''' '''
@staticmethod @staticmethod
def serviceToDict(item, full=False): def serviceInfo(item):
info = item.getType()
return {
'icon': info.icon().replace('\n', ''),
'needs_publication': info.publicationType is not None,
'max_deployed': info.maxDeployed,
'uses_cache': info.usesCache,
'uses_cache_l2': info.usesCache_L2,
'cache_tooltip': _(info.cacheTooltip),
'cache_tooltip_l2': _(info.cacheTooltip_L2),
'needs_manager': info.needsManager,
'must_assign_manually': info.mustAssignManually,
}
@staticmethod
def serviceToDict(item, perm, full=False):
''' '''
Convert a service db item to a dict for a rest response Convert a service db item to a dict for a rest response
:param item: Service item (db) :param item: Service item (db)
@ -70,31 +86,22 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
'deployed_services_count': item.deployedServices.count(), 'deployed_services_count': item.deployedServices.count(),
'user_services_count': UserService.objects.filter(deployed_service__service=item).count(), 'user_services_count': UserService.objects.filter(deployed_service__service=item).count(),
'maintenance_mode': item.provider.maintenance_mode, 'maintenance_mode': item.provider.maintenance_mode,
'permission': perm
} }
if full: if full:
info = item.getType() retVal['info'] = Services.serviceInfo(item)
retVal['info'] = {
'icon': info.icon().replace('\n', ''),
'needs_publication': info.publicationType is not None,
'max_deployed': info.maxDeployed,
'uses_cache': info.usesCache,
'uses_cache_l2': info.usesCache_L2,
'cache_tooltip': _(info.cacheTooltip),
'cache_tooltip_l2': _(info.cacheTooltip_L2),
'needs_manager': info.needsManager,
'must_assign_manually': info.mustAssignManually,
}
return retVal return retVal
def getItems(self, parent, item): def getItems(self, parent, item):
# Extract provider # Check what kind of access do we have to parent provider
perm = permissions.getEffectivePermission(self._user, parent)
try: try:
if item is None: if item is None:
return [Services.serviceToDict(k) for k in parent.services.all()] return [Services.serviceToDict(k, perm) for k in parent.services.all()]
else: else:
k = parent.services.get(uuid=item) k = parent.services.get(uuid=item)
val = Services.serviceToDict(k) val = Services.serviceToDict(k, perm, full=True)
return self.fillIntanceFields(k, val) return self.fillIntanceFields(k, val)
except Exception: except Exception:
logger.exception('itemId {}'.format(item)) logger.exception('itemId {}'.format(item))

View File

@ -37,10 +37,12 @@ from uds.models import DeployedService, OSManager, Service, Image
from uds.core.ui.images import DEFAULT_THUMB_BASE64 from uds.core.ui.images import DEFAULT_THUMB_BASE64
from uds.core.util.State import State from uds.core.util.State import State
from uds.core.util import log from uds.core.util import log
from uds.core.util import permissions
from uds.REST.model import ModelHandler from uds.REST.model import ModelHandler
from uds.REST import RequestError, ResponseError from uds.REST import RequestError, ResponseError
from uds.core.ui.UserInterface import gui from uds.core.ui.UserInterface import gui
from uds.REST.methods.user_services import AssignedService, CachedService, Groups, Transports, Publications from .user_services import AssignedService, CachedService, Groups, Transports, Publications
from .services import Services
import logging import logging
@ -80,6 +82,7 @@ class ServicesPools(ModelHandler):
val = { val = {
'id': item.uuid, 'id': item.uuid,
'name': item.name, 'name': item.name,
'parent': item.service.name,
'comments': item.comments, 'comments': item.comments,
'state': item.state if item.service.provider.maintenance_mode is False else State.MAINTENANCE, 'state': item.state if item.service.provider.maintenance_mode is False else State.MAINTENANCE,
'thumb': item.image.thumb64 if item.image is not None else DEFAULT_THUMB_BASE64, 'thumb': item.image.thumb64 if item.image is not None else DEFAULT_THUMB_BASE64,
@ -93,6 +96,8 @@ class ServicesPools(ModelHandler):
'user_services_count': item.userServices.count(), 'user_services_count': item.userServices.count(),
'restrained': item.isRestrained(), 'restrained': item.isRestrained(),
'show_transports': item.show_transports, 'show_transports': item.show_transports,
'permission': permissions.getEffectivePermission(self._user, item),
'info': Services.serviceInfo(item.service)
} }
if item.osmanager is not None: if item.osmanager is not None:

View File

@ -35,6 +35,7 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _, ugettext from django.utils.translation import ugettext_lazy as _, ugettext
from uds.models import Transport, Network from uds.models import Transport, Network
from uds.core.transports import factory from uds.core.transports import factory
from uds.core.util import permissions
from uds.REST.model import ModelHandler from uds.REST.model import ModelHandler
@ -93,6 +94,7 @@ class Transports(ModelHandler):
'networks': [{'id': n.id} for n in item.networks.all()], 'networks': [{'id': n.id} for n in item.networks.all()],
'deployed_count': item.deployedServices.count(), 'deployed_count': item.deployedServices.count(),
'type': type_.type(), 'type': type_.type(),
'permission': permissions.getEffectivePermission(self._user, item)
} }
def afterSave(self, item): def afterSave(self, item):

View File

@ -33,13 +33,14 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from uds.REST.handlers import NotFound, RequestError, ResponseError from uds.REST.handlers import NotFound, RequestError, ResponseError, AccessDenied, NotSupportedError
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db import IntegrityError from django.db import IntegrityError
from uds.core.ui.UserInterface import gui as uiGui from uds.core.ui.UserInterface import gui as uiGui
from uds.REST.handlers import Handler, HandlerError from uds.REST.handlers import Handler, HandlerError
from uds.core.util import log from uds.core.util import log
from uds.core.util import permissions
import fnmatch import fnmatch
import re import re
@ -50,7 +51,7 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
__updated__ = '2015-02-16' __updated__ = '2015-03-05'
# a few constants # a few constants
@ -150,6 +151,12 @@ class BaseModelHandler(Handler):
return gui return gui
def ensureAccess(self, obj, permission, root=False):
perm = permissions.getEffectivePermission(self._user, obj, root)
if perm < permission:
self.accessDenied()
return perm
def typeInfo(self, type_): # pylint: disable=no-self-use def typeInfo(self, type_): # pylint: disable=no-self-use
''' '''
Returns info about the type Returns info about the type
@ -233,6 +240,12 @@ class BaseModelHandler(Handler):
message = _('Item not found') if message is None else None message = _('Item not found') if message is None else None
raise NotFound('{} {}: {}'.format(message, self.__class__, self._args)) raise NotFound('{} {}: {}'.format(message, self.__class__, self._args))
def accessDenied(self, message=None):
raise AccessDenied(message or _('Access denied'))
def notSupported(self, message=None):
raise NotSupportedError(message or _('Operation not supported'))
# Success methods # Success methods
def success(self): def success(self):
''' '''
@ -284,6 +297,7 @@ class DetailHandler(BaseModelHandler): # pylint: disable=abstract-class-not-use
self._params = params self._params = params
self._args = args self._args = args
self._kwargs = kwargs self._kwargs = kwargs
self._user = kwargs.get('user', None)
def __checkCustom(self, check, parent, arg=None): def __checkCustom(self, check, parent, arg=None):
''' '''
@ -312,6 +326,7 @@ class DetailHandler(BaseModelHandler): # pylint: disable=abstract-class-not-use
nArgs = len(self._args) nArgs = len(self._args)
parent = self._kwargs['parent'] parent = self._kwargs['parent']
if nArgs == 0: if nArgs == 0:
return self.getItems(parent, None) return self.getItems(parent, None)
@ -578,6 +593,7 @@ class ModelHandler(BaseModelHandler):
# log related # log related
def getLogs(self, item): def getLogs(self, item):
self.ensureAccess(item, permissions.PERMISSION_READ)
logger.debug('Default getLogs invoked') logger.debug('Default getLogs invoked')
return log.getLogs(item) return log.getLogs(item)
@ -656,14 +672,26 @@ class ModelHandler(BaseModelHandler):
return data return data
# Helper to process detail # Helper to process detail
# Details can be managed (writen) by any user that has MANAGEMENT permission over parent
def processDetail(self): def processDetail(self):
logger.debug('Processing detail {0}'.format(self._path)) logger.debug('Processing detail {} for user {}'.format(self._path, self._user))
try: try:
item = self.model.objects.filter(uuid=self._args[0])[0] item = self.model.objects.filter(uuid=self._args[0])[0]
# If we do not have access to parent to, at least, read...
if self._operation in ('put', 'post', 'delete'):
requiredPermission = permissions.PERMISSION_MANAGEMENT
else:
requiredPermission = permissions.PERMISSION_READ
if permissions.checkPermissions(self._user, item, requiredPermission) is False:
logger.debug('Permission for user {} does not comply with {}'.format(self._user, requiredPermission))
self.accessDenied()
detailCls = self.detail[self._args[1]] detailCls = self.detail[self._args[1]]
args = list(self._args[2:]) args = list(self._args[2:])
path = self._path + '/'.join(args[:2]) path = self._path + '/'.join(args[:2])
detail = detailCls(self, path, self._params, *args, parent=item) detail = detailCls(self, path, self._params, *args, parent=item, user=self._user)
method = getattr(detail, self._operation) method = getattr(detail, self._operation)
except KeyError: except KeyError:
self.invalidMethodException() self.invalidMethodException()
@ -672,10 +700,17 @@ class ModelHandler(BaseModelHandler):
return method() return method()
def getItems(self, *args, **kwargs): def getItems(self, overview=True, *args, **kwargs):
for item in self.model.objects.filter(*args, **kwargs): for item in self.model.objects.filter(*args, **kwargs):
try: try:
if permissions.checkPermissions(self._user, item, permissions.PERMISSION_READ) is False:
continue
if overview:
yield self.item_as_dict_overview(item) yield self.item_as_dict_overview(item)
else:
res = self.item_as_dict(item)
self.fillIntanceFields(item, res)
yield res
except Exception: # maybe an exception is thrown to skip an item except Exception: # maybe an exception is thrown to skip an item
# logger.exception('Exception getting item from {0}'.format(self.model)) # logger.exception('Exception getting item from {0}'.format(self.model))
pass pass
@ -693,15 +728,7 @@ class ModelHandler(BaseModelHandler):
nArgs = len(self._args) nArgs = len(self._args)
if nArgs == 0: if nArgs == 0:
result = [] return list(self.getItems(overview=False))
for val in self.model.objects.all():
try:
res = self.item_as_dict(val)
self.fillIntanceFields(val, res)
result.append(res)
except Exception: # maybe an exception is thrown to skip an item
pass
return result
# if has custom methods, look for if this request matches any of them # if has custom methods, look for if this request matches any of them
for cm in self.custom_methods: for cm in self.custom_methods:
@ -735,6 +762,9 @@ class ModelHandler(BaseModelHandler):
# get item ID # get item ID
try: try:
val = self.model.objects.get(uuid=self._args[0].lower()) val = self.model.objects.get(uuid=self._args[0].lower())
self.ensureAccess(val, permissions.PERMISSION_READ)
res = self.item_as_dict(val) res = self.item_as_dict(val)
self.fillIntanceFields(val, res) self.fillIntanceFields(val, res)
return res return res
@ -793,6 +823,9 @@ class ModelHandler(BaseModelHandler):
if len(self._args) > 1: # Detail? if len(self._args) > 1: # Detail?
return self.processDetail() return self.processDetail()
self.ensureAccess(self.model(), permissions.PERMISSION_ALL, root=True) # Must have write permissions to create, modify, etc..
try: try:
# Extract fields # Extract fields
args = self.readFieldsFromParams(self.save_fields) args = self.readFieldsFromParams(self.save_fields)
@ -854,6 +887,9 @@ class ModelHandler(BaseModelHandler):
if len(self._args) != 1: if len(self._args) != 1:
raise RequestError('Delete need one and only one argument') raise RequestError('Delete need one and only one argument')
self.ensureAccess(self.model(), permissions.PERMISSION_ALL, root=True) # Must have write permissions to delete
try: try:
item = self.model.objects.get(uuid=self._args[0].lower()) item = self.model.objects.get(uuid=self._args[0].lower())
self.checkDelete(item) self.checkDelete(item)

View File

@ -32,7 +32,7 @@
''' '''
from __future__ import unicode_literals from __future__ import unicode_literals
__updated__ = '2015-03-01' __updated__ = '2015-03-05'
from uds.models import Provider, Service, OSManager, Transport, Network, ServicePool, UserService, Authenticator, User, Group, StatsCounters, StatsEvents from uds.models import Provider, Service, OSManager, Transport, Network, ServicePool, UserService, Authenticator, User, Group, StatsCounters, StatsEvents
import logging import logging

View File

@ -32,7 +32,7 @@
''' '''
from __future__ import unicode_literals from __future__ import unicode_literals
__updated__ = '2015-03-04' __updated__ = '2015-03-05'
from uds.models import Permissions from uds.models import Permissions
from uds.core.util import ot from uds.core.util import ot
@ -43,6 +43,7 @@ logger = logging.getLogger(__name__)
PERMISSION_ALL = Permissions.PERMISSION_ALL PERMISSION_ALL = Permissions.PERMISSION_ALL
PERMISSION_READ = Permissions.PERMISSION_READ PERMISSION_READ = Permissions.PERMISSION_READ
PERMISSION_MANAGEMENT = Permissions.PERMISSION_MANAGEMENT
PERMISSION_NONE = Permissions.PERMISSION_NONE PERMISSION_NONE = Permissions.PERMISSION_NONE
@ -54,6 +55,19 @@ def getPermissions(obj):
return list(Permissions.enumeratePermissions(object_type=ot.getObjectType(obj), object_id=obj.pk)) return list(Permissions.enumeratePermissions(object_type=ot.getObjectType(obj), object_id=obj.pk))
def getEffectivePermission(user, obj, root=False):
if user.is_admin is True:
return PERMISSION_ALL
if user.staff_member is False:
return PERMISSION_NONE
if root is False:
return Permissions.getPermissions(user=user, groups=user.groups.all(), object_type=ot.getObjectType(obj), object_id=obj.pk)
else:
return Permissions.getPermissions(user=user, groups=user.groups.all(), object_type=ot.getObjectType(obj))
def addUserPermission(user, obj, permission=PERMISSION_READ): def addUserPermission(user, obj, permission=PERMISSION_READ):
# Some permissions added to some object types needs at least READ_PERMISSION on parent # Some permissions added to some object types needs at least READ_PERMISSION on parent
Permissions.addPermission(user=user, object_type=ot.getObjectType(obj), object_id=obj.pk, permission=permission) Permissions.addPermission(user=user, object_type=ot.getObjectType(obj), object_id=obj.pk, permission=permission)
@ -63,15 +77,16 @@ def addGroupPermission(group, obj, permission=PERMISSION_READ):
Permissions.addPermission(group=group, object_type=ot.getObjectType(obj), object_id=obj.pk, permission=permission) Permissions.addPermission(group=group, object_type=ot.getObjectType(obj), object_id=obj.pk, permission=permission)
def checkPermissions(user, obj, permission=PERMISSION_ALL): def checkPermissions(user, obj, permission=PERMISSION_ALL, root=False):
if user.is_admin is True: return getEffectivePermission(user, obj, root) >= permission
return True
if user.is_staff is False:
return False
return Permissions.getPermissions(user=user, groups=user.groups.all(), object_type=ot.getObjectType(obj), object_id=obj.pk) >= permission
def getPermissionName(perm): def getPermissionName(perm):
return Permissions.permissionAsString(perm) return Permissions.permissionAsString(perm)
def revokePermissionById(permId):
try:
return Permissions.objects.get(uuid=permId).delete()
except Exception:
return None

View File

@ -33,7 +33,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
__updated__ = '2015-03-04' __updated__ = '2015-03-05'
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -58,8 +58,9 @@ class Permissions(UUIDModel):
# pylint: disable=model-missing-unicode # pylint: disable=model-missing-unicode
# Allowed permissions # Allowed permissions
PERMISSION_NONE = 0 PERMISSION_NONE = 0
PERMISSION_READ = 16 PERMISSION_READ = 32
PERMISSION_ALL = 32 PERMISSION_MANAGEMENT = 64
PERMISSION_ALL = 96
created = models.DateTimeField(db_index=True) created = models.DateTimeField(db_index=True)
ends = models.DateTimeField(db_index=True, null=True, blank=True, default=None) # Future "permisions ends at this moment", not assigned right now ends = models.DateTimeField(db_index=True, null=True, blank=True, default=None) # Future "permisions ends at this moment", not assigned right now
@ -77,6 +78,7 @@ class Permissions(UUIDModel):
return { return {
Permissions.PERMISSION_NONE: _('None'), Permissions.PERMISSION_NONE: _('None'),
Permissions.PERMISSION_READ: _('Read'), Permissions.PERMISSION_READ: _('Read'),
Permissions.PERMISSION_MANAGEMENT: _('Management'),
Permissions.PERMISSION_ALL: _('All') Permissions.PERMISSION_ALL: _('All')
}.get(perm, _('None')) }.get(perm, _('None'))

View File

@ -6,6 +6,13 @@ api = @api
api.debug = on api.debug = on
api.permissions = {
NONE: 0
READ: 32
MANAGEMENT: 64
ALL: 96
}
api.doLog = (args...) -> api.doLog = (args...) ->
if api.debug if api.debug
try try
@ -267,6 +274,12 @@ class BasicModelRest
return return
permission: () ->
if api.config.admin is true
return api.permissions.ALL
return api.permissions.NONE
getPermissions: (id, success_fnc, fail_fnc) -> getPermissions: (id, success_fnc, fail_fnc) ->
path = "permissions/" + @path + '/' + id path = "permissions/" + @path + '/' + id
@_requestPath path, @_requestPath path,
@ -275,17 +288,17 @@ class BasicModelRest
fail: fail_fnc fail: fail_fnc
addPermission: (id, type, itemId, perm, success_fnc, fail_fnc) -> addPermission: (id, type, itemId, perm, success_fnc, fail_fnc) ->
path = "permissions/" + @path + '/' + id + '/' + type + '/' + itemId path = "permissions/" + @path + '/' + id + '/' + type + '/add/' + itemId
data = data =
perm: perm perm: perm
api.putJson path, data, api.putJson path, data,
success: success_fnc success: success_fnc
fail: fail_fnc fail: fail_fnc
revokePermissions: (id, type, itemIds, success_fnc, fail_fnc)-> revokePermissions: (itemIds, success_fnc, fail_fnc)->
path = "permissions/revoke/" + @path + '/' + id + '/' + type path = "permissions/revoke"
data = data =
ids: itemIds items: itemIds
api.putJson path, data, api.putJson path, data,
success: success_fnc success: success_fnc
fail: fail_fnc fail: fail_fnc
@ -338,12 +351,14 @@ class DetailModelRestApi extends BasicModelRest
].join("/") ].join("/")
@moptions = options @moptions = options
permission: () ->
if @moptions.permission? then @moptions.permission else api.permissions.ALL
create: (data, success_fnc, fail_fnc) -> create: (data, success_fnc, fail_fnc) ->
@put data, @put data,
success: success_fnc success: success_fnc
fail: fail_fnc fail: fail_fnc
save: (data, success_fnc, fail_fnc) -> save: (data, success_fnc, fail_fnc) ->
@put data, @put data,
id: data.id id: data.id

View File

@ -125,6 +125,7 @@ gui.authenticators.link = (event) ->
"edit" "edit"
"delete" "delete"
"xls" "xls"
"permissions"
] ]
onRowDeselect: -> onRowDeselect: ->
clearDetails() clearDetails()
@ -141,8 +142,8 @@ gui.authenticators.link = (event) ->
id = selected[0].id id = selected[0].id
type = gui.authenticators.types[selected[0].type] type = gui.authenticators.types[selected[0].type]
gui.doLog "Type", type gui.doLog "Type", type
user = new GuiElement(api.authenticators.detail(id, "users"), "users") user = new GuiElement(api.authenticators.detail(id, "users", { permission: selected[0].permission }), "users")
group = new GuiElement(api.authenticators.detail(id, "groups"), "groups") group = new GuiElement(api.authenticators.detail(id, "groups", { permission: selected[0].permission }), "groups")
grpTable = group.table( grpTable = group.table(
container: "groups-placeholder" container: "groups-placeholder"
rowSelect: "single" rowSelect: "single"

View File

@ -19,6 +19,7 @@ gui.connectivity.link = (event) ->
"edit" "edit"
"delete" "delete"
"xls" "xls"
"permissions"
] ]
onNew: gui.methods.typedNew(gui.connectivity.transports, gettext("New transport"), gettext("Transport creation error")) onNew: gui.methods.typedNew(gui.connectivity.transports, gettext("New transport"), gettext("Transport creation error"))
onEdit: gui.methods.typedEdit(gui.connectivity.transports, gettext("Edit transport"), gettext("Transport saving error")) onEdit: gui.methods.typedEdit(gui.connectivity.transports, gettext("Edit transport"), gettext("Transport saving error"))
@ -32,6 +33,7 @@ gui.connectivity.link = (event) ->
"edit" "edit"
"delete" "delete"
"xls" "xls"
"permissions"
] ]
onNew: gui.methods.typedNew(gui.connectivity.networks, gettext("New network"), gettext("Network creation error")) onNew: gui.methods.typedNew(gui.connectivity.networks, gettext("New network"), gettext("Network creation error"))
onEdit: gui.methods.typedEdit(gui.connectivity.networks, gettext("Edit network"), gettext("Network saving error")) onEdit: gui.methods.typedEdit(gui.connectivity.networks, gettext("Edit network"), gettext("Network saving error"))

View File

@ -17,6 +17,7 @@ gui.osmanagers.link = (event) ->
"edit" "edit"
"delete" "delete"
"xls" "xls"
"permissions"
] ]
onNew: gui.methods.typedNew(gui.osmanagers, gettext("New OSManager"), gettext("OSManager creation error")) onNew: gui.methods.typedNew(gui.osmanagers, gettext("New OSManager"), gettext("OSManager creation error"))
onEdit: gui.methods.typedEdit(gui.osmanagers, gettext("Edit OSManager"), gettext("OSManager saving error")) onEdit: gui.methods.typedEdit(gui.osmanagers, gettext("Edit OSManager"), gettext("OSManager saving error"))

View File

@ -54,6 +54,8 @@ gui.providers.link = (event) ->
return return
tableId = gui.providers.table( tableId = gui.providers.table(
getPermission: (selected) ->
gui.doLog "Selected", selected
container: "providers-placeholder" container: "providers-placeholder"
rowSelect: "single" rowSelect: "single"
onCheck: (check, items) -> # Check if item can be deleted onCheck: (check, items) -> # Check if item can be deleted
@ -86,7 +88,7 @@ gui.providers.link = (event) ->
id = selected[0].id id = selected[0].id
# Giving the name compossed with type, will ensure that only styles will be reattached once # Giving the name compossed with type, will ensure that only styles will be reattached once
services = new GuiElement(api.providers.detail(id, "services"), "services-" + selected[0].type) services = new GuiElement(api.providers.detail(id, "services", { permission: selected[0].permission }), "services-" + selected[0].type)
tmpLogTable = undefined tmpLogTable = undefined
servicesTable = services.table( servicesTable = services.table(
container: "services-placeholder" container: "services-placeholder"
@ -120,7 +122,6 @@ gui.providers.link = (event) ->
"edit" "edit"
"delete" "delete"
"xls" "xls"
"permissions"
] ]
onEdit: gui.methods.typedEdit(services, gettext("Edit service"), gettext("Service creation error")) onEdit: gui.methods.typedEdit(services, gettext("Edit service"), gettext("Service creation error"))
onNew: gui.methods.typedNew(services, gettext("New service"), gettext("Service saving error")) onNew: gui.methods.typedNew(services, gettext("New service"), gettext("Service saving error"))
@ -141,6 +142,7 @@ gui.providers.link = (event) ->
"new" "new"
"edit" "edit"
{ {
permission: api.permissions.MANAGEMENT
text: gettext("Maintenance") text: gettext("Maintenance")
css: "disabled" css: "disabled"
click: (val, value, btn, tbl, refreshFnc) -> click: (val, value, btn, tbl, refreshFnc) ->

View File

@ -79,14 +79,6 @@ gui.servicesPools.link = (event) ->
return return
return return
# Fills up the list of available services
api.providers.allServices (services) ->
availableServices = {}
$.each services, (undefined_, service) ->
availableServices[service.id] = service
return
gui.doLog "Available services", availableServices
api.templates.get "services_pool", (tmpl) -> api.templates.get "services_pool", (tmpl) ->
gui.appendToWorkspace api.templates.evaluate(tmpl, gui.appendToWorkspace api.templates.evaluate(tmpl,
deployed_services: "deployed-services-placeholder" deployed_services: "deployed-services-placeholder"
@ -120,6 +112,7 @@ gui.servicesPools.link = (event) ->
"edit" "edit"
"delete" "delete"
"xls" "xls"
"permissions"
] ]
onRowDeselect: -> onRowDeselect: ->
clearDetails() clearDetails()
@ -131,16 +124,13 @@ gui.servicesPools.link = (event) ->
clearDetails() clearDetails()
service = null service = null
try try
service = availableServices[servPool.service_id] info = servPool.info
catch e catch e
gui.doLog "Exception on rowSelect", e gui.doLog "Exception on rowSelect", e
gui.notify "Service pool " + gettext("error"), "danger" gui.notify "Service pool " + gettext("error"), "danger"
return return
if service?
$("#detail-placeholder").removeClass "hidden" $("#detail-placeholder").removeClass "hidden"
else
$("#detail-placeholder").addClass "hidden"
return
# #
# * Cache Part # * Cache Part
@ -149,9 +139,9 @@ gui.servicesPools.link = (event) ->
# If service does not supports cache, do not show it # If service does not supports cache, do not show it
# Shows/hides cache # Shows/hides cache
if service.info.uses_cache or service.info.uses_cache_l2 if info.uses_cache or info.uses_cache_l2
$("#cache-placeholder_tab").removeClass "hidden" $("#cache-placeholder_tab").removeClass "hidden"
cachedItems = new GuiElement(api.servicesPools.detail(servPool.id, "cache"), "cache") cachedItems = new GuiElement(api.servicesPools.detail(servPool.id, "cache", { permission: servPool.permission }), "cache")
# Cached items table # Cached items table
prevCacheLogTbl = null prevCacheLogTbl = null
@ -190,9 +180,9 @@ gui.servicesPools.link = (event) ->
groups = null groups = null
# Shows/hides groups # Shows/hides groups
if service.info.must_assign_manually is false if info.must_assign_manually is false
$("#groups-placeholder_tab").removeClass "hidden" $("#groups-placeholder_tab").removeClass "hidden"
groups = new GuiElement(api.servicesPools.detail(servPool.id, "groups"), "groups") groups = new GuiElement(api.servicesPools.detail(servPool.id, "groups", { permission: servPool.permission }), "groups")
# Groups items table # Groups items table
groupsTable = groups.table( groupsTable = groups.table(
@ -268,11 +258,11 @@ gui.servicesPools.link = (event) ->
# * Assigned services part # * Assigned services part
# #
prevAssignedLogTbl = null prevAssignedLogTbl = null
assignedServices = new GuiElement(api.servicesPools.detail(servPool.id, "services"), "services") assignedServices = new GuiElement(api.servicesPools.detail(servPool.id, "services", { permission: servPool.permission }), "services")
assignedServicesTable = assignedServices.table( assignedServicesTable = assignedServices.table(
container: "assigned-services-placeholder_tbl" container: "assigned-services-placeholder_tbl"
rowSelect: "single" rowSelect: "single"
buttons: (if service.info.must_assign_manually then [ buttons: (if info.must_assign_manually then [
"new" "new"
"delete" "delete"
"xls" "xls"
@ -292,12 +282,12 @@ gui.servicesPools.link = (event) ->
return return
onRowSelect: (selected) -> onRowSelect: (selected) ->
service = selected[0] svr = selected[0]
if prevAssignedLogTbl if prevAssignedLogTbl
$tbl = $(prevAssignedLogTbl).dataTable() $tbl = $(prevAssignedLogTbl).dataTable()
$tbl.fnClearTable() $tbl.fnClearTable()
$tbl.fnDestroy() $tbl.fnDestroy()
prevAssignedLogTbl = assignedServices.logTable(service.id, prevAssignedLogTbl = assignedServices.logTable(svr.id,
container: "assigned-services-placeholder_log" container: "assigned-services-placeholder_log"
) )
return return
@ -311,7 +301,7 @@ gui.servicesPools.link = (event) ->
# #
# * Transports part # * Transports part
# #
transports = new GuiElement(api.servicesPools.detail(servPool.id, "transports"), "transports") transports = new GuiElement(api.servicesPools.detail(servPool.id, "transports", { permission: servPool.permission }), "transports")
# Transports items table # Transports items table
transportsTable = transports.table( transportsTable = transports.table(
@ -367,10 +357,10 @@ gui.servicesPools.link = (event) ->
# * Publications part # * Publications part
# #
publications = null publications = null
if service.info.needs_publication if info.needs_publication
$("#publications-placeholder_tab").removeClass "hidden" $("#publications-placeholder_tab").removeClass "hidden"
pubApi = api.servicesPools.detail(servPool.id, "publications") pubApi = api.servicesPools.detail(servPool.id, "publications")
publications = new GuiElement(pubApi, "publications") publications = new GuiElement(pubApi, "publications", { permission: servPool.permission })
# Publications table # Publications table
publicationsTable = publications.table( publicationsTable = publications.table(
@ -446,20 +436,14 @@ gui.servicesPools.link = (event) ->
$.each data, (index, value) -> $.each data, (index, value) ->
gui.doLog value.thumb gui.doLog value.thumb
try try
service = availableServices[value.service_id]
if not service?
value.parent = gettext("undefined")
return
style = "display:inline-block; background: url(data:image/png;base64," + value.thumb + "); background-size: 16px 16px; background-repeat: no-repeat; width: 16px; height: 16px; vertical-align: middle;" style = "display:inline-block; background: url(data:image/png;base64," + value.thumb + "); background-size: 16px 16px; background-repeat: no-repeat; width: 16px; height: 16px; vertical-align: middle;"
gui.doLog style gui.doLog style
if value.restrained if value.restrained
value.name = "<span class=\"fa fa-exclamation text-danger\"></span> " + value.name value.name = "<span class=\"fa fa-exclamation text-danger\"></span> " + value.name
value.state = gettext("Restrained") value.state = gettext("Restrained")
value.name = "<span style=\"" + style + "\"></span> " + value.name value.name = "<span style=\"" + style + "\"></span> " + value.name
value.parent = service.name
catch e catch e
value.name = "<span class=\"fa fa-asterisk text-alert\"></span> " + value.name value.name = "<span class=\"fa fa-asterisk text-alert\"></span> " + value.name
value.parent = gettext("unknown (needs reload)")
return return
return return
@ -486,7 +470,4 @@ gui.servicesPools.link = (event) ->
onDelete: gui.methods.del(gui.servicesPools, gettext("Delete") + " service pool", "Service pool " + gettext("deletion error")) onDelete: gui.methods.del(gui.servicesPools, gettext("Delete") + " service pool", "Service pool " + gettext("deletion error"))
) )
return return
return
return return

View File

@ -276,7 +276,9 @@
$.each tblParams.buttons, (index, value) -> # Iterate through button definition $.each tblParams.buttons, (index, value) -> # Iterate through button definition
btn = null btn = null
switch value switch value
when "new" when "new"
if self.rest.permission() >= api.permissions.MANAGEMENT
if Object.keys(self.types).length isnt 0 if Object.keys(self.types).length isnt 0
menuId = gui.genRamdonId("dd-") menuId = gui.genRamdonId("dd-")
ordered = [] ordered = []
@ -309,6 +311,7 @@
sButtonClass: gui.config.dataTableButtons["new"].css sButtonClass: gui.config.dataTableButtons["new"].css
fnClick: clickHandlerFor(tblParams.onNew, "new", true) fnClick: clickHandlerFor(tblParams.onNew, "new", true)
when "edit" when "edit"
if self.rest.permission() >= api.permissions.MANAGEMENT
btn = btn =
sExtends: "text" sExtends: "text"
sButtonText: gui.config.dataTableButtons.edit.text sButtonText: gui.config.dataTableButtons.edit.text
@ -316,6 +319,7 @@
fnClick: clickHandlerFor(tblParams.onEdit, "edit") fnClick: clickHandlerFor(tblParams.onEdit, "edit")
sButtonClass: gui.config.dataTableButtons.edit.css sButtonClass: gui.config.dataTableButtons.edit.css
when "delete" when "delete"
if self.rest.permission() >= api.permissions.MANAGEMENT
btn = btn =
sExtends: "text" sExtends: "text"
sButtonText: gui.config.dataTableButtons["delete"].text sButtonText: gui.config.dataTableButtons["delete"].text
@ -329,7 +333,7 @@
fnClick: refreshFnc fnClick: refreshFnc
sButtonClass: gui.config.dataTableButtons.refresh.css sButtonClass: gui.config.dataTableButtons.refresh.css
when "permissions" when "permissions"
if api.config.admin if self.rest.permission() == api.permissions.ALL
btn = btn =
sExtends: "text" sExtends: "text"
sButtonText: gui.config.dataTableButtons.permissions.text sButtonText: gui.config.dataTableButtons.permissions.text
@ -346,6 +350,8 @@
# End export to excell # End export to excell
sButtonClass: gui.config.dataTableButtons.xls.css sButtonClass: gui.config.dataTableButtons.xls.css
else # Custom button, this has to be else # Custom button, this has to be
perm = if value.permission? then value.permission else api.permissions.NONE
if self.rest.permission() >= perm
try try
css = ((if value.css then value.css + " " else "")) + gui.config.dataTableButtons.custom.css css = ((if value.css then value.css + " " else "")) + gui.config.dataTableButtons.custom.css
btn = btn =

View File

@ -1,4 +1,18 @@
gui.permissions = (val, rest, tbl, refreshFnc) -> gui.permissions = (val, rest, tbl, refreshFnc) ->
baseId = gui.genRamdonId('perms-')
fillSelect = (perms, forUser) ->
$select = $('#' + baseId + (if forUser then '_user_select' else '_group_select'))
$select.empty()
padRight = (str, len)->
numPads = len - str.length
if (numPads > 0) then str + Array(numPads+1).join('&nbsp;') else str
for item in perms
if (forUser is true and item.type is 'user') or (forUser is false and item.type is 'group')
$select.append('<option value="' + item.id + '">' + padRight(item.auth_name + '\\' + item.entity_name, 28) + '&nbsp;| ' + item.perm_name)
addModal = (forUser) -> addModal = (forUser) ->
if forUser if forUser
@ -39,14 +53,18 @@ gui.permissions = (val, rest, tbl, refreshFnc) ->
if auth is -1 or item is -1 if auth is -1 or item is -1
gui.notify gettext("You must provide authenticator and") + " " + label, "danger" gui.notify gettext("You must provide authenticator and") + " " + label, "danger"
else # Save & close modal else # Save & close modal
rest.addPermission val.id, items, item, perm rest.addPermission val.id, items, item, perm, (
(perms) ->
$(modalId).modal "hide" $(modalId).modal "hide"
fillSelect perms, forUser
)
return return
# Makes form "beautyfull" :-) # Makes form "beautyfull" :-)
gui.tools.applyCustoms modalId gui.tools.applyCustoms modalId
return return
delModal = (forUser, selectedItems) -> delModal = (forUser, selectedItems) ->
if forUser if forUser
label = gettext('User') label = gettext('User')
@ -64,30 +82,19 @@ gui.permissions = (val, rest, tbl, refreshFnc) ->
gui.doLog modalId gui.doLog modalId
$(modalId + ' .button-revoke').on('click', () -> $(modalId + ' .button-revoke').on('click', () ->
rest.revokePermissions val.id, items, toDel rest.revokePermissions toDel, (
(perms) ->
$(modalId).modal "hide" $(modalId).modal "hide"
for v in selectedItems
$(v).remove()
)
) )
fillSelect = (baseId, perms, forUser) ->
$select = $('#' + baseId + (if forUser then '_user_select' else '_group_select'))
$select.empty()
padRight = (str, len)->
numPads = len - str.length
if (numPads > 0) then str + Array(numPads+1).join('&nbsp;') else str
for item in perms
if (forUser is true and item.type is 'user') or (forUser is false and item.type is 'group')
$select.append('<option value="' + item.id + '">' + padRight(item.auth_name + '\\' + item.name, 28) + '&nbsp;| ' + item.perm_name)
api.templates.get "permissions", (tmpl) -> api.templates.get "permissions", (tmpl) ->
rest.getPermissions val.id, (perms) -> rest.getPermissions val.id, (perms) ->
id = gui.genRamdonId('perms-')
content = api.templates.evaluate(tmpl, content = api.templates.evaluate(tmpl,
id: id id: baseId
perms: perms perms: perms
) )
modalId = gui.launchModal gettext("Permissions for") + " " + val.name, content, modalId = gui.launchModal gettext("Permissions for") + " " + val.name, content,
@ -95,31 +102,31 @@ gui.permissions = (val, rest, tbl, refreshFnc) ->
closeButton: '<button type="button" class="btn btn-default" data-dismiss="modal">Ok</button>' closeButton: '<button type="button" class="btn btn-default" data-dismiss="modal">Ok</button>'
# Fills user select # Fills user select
fillSelect id, perms, true fillSelect perms, true
fillSelect id, perms, false fillSelect perms, false
$('#' + id + '_user_del').on('click', () -> $('#' + baseId + '_user_del').on('click', () ->
$select = $('#' + id + '_user_select') $select = $('#' + baseId + '_user_select')
selected = $select.find(":selected") selected = $select.find(":selected")
return if selected.length is 0 return if selected.length is 0
delModal true, selected delModal true, selected
) )
$('#' + id + '_user_add').on('click', () -> $('#' + baseId + '_user_add').on('click', () ->
addModal yes addModal yes
) )
$('#' + id + '_group_del').on('click', () -> $('#' + baseId + '_group_del').on('click', () ->
$select = $('#' + id + '_group_select') $select = $('#' + baseId + '_group_select')
selected = $select.find(":selected") selected = $select.find(":selected")
return if selected.length is 0 return if selected.length is 0
delModal false, selected delModal false, selected
) )
$('#' + id + '_group_add').on('click', () -> $('#' + baseId + '_group_add').on('click', () ->
addModal no addModal no
) )

View File

@ -5,7 +5,7 @@
<div class="col-md-6 column"> <div class="col-md-6 column">
<div class="form-group"> <div class="form-group">
<label for="{{ id }}_select">{% endverbatim %}{% trans 'Users' %}{% verbatim %}</label> <label for="{{ id }}_select">{% endverbatim %}{% trans 'Users' %}{% verbatim %}</label>
<select class="form-control" multiple size="8" id="{{ id }}_user_select" style='font-family: "Courier New"'> <select class="form-control" multiple size="12" id="{{ id }}_user_select" style='font-family: "Courier New"'>
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -20,7 +20,7 @@
<div class="col-md-6 column"> <div class="col-md-6 column">
<div class="form-group"> <div class="form-group">
<label for="{{ id }}_select">{% endverbatim %}{% trans 'Groups' %}{% verbatim %}</label> <label for="{{ id }}_select">{% endverbatim %}{% trans 'Groups' %}{% verbatim %}</label>
<select class="form-control" multiple size="8" id="{{ id }}_group_select" style='font-family: "Courier New"'> <select class="form-control" multiple size="12" id="{{ id }}_group_select" style='font-family: "Courier New"'>
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@ -25,8 +25,9 @@
<label for="id_perm_select" class="col-sm-2 control-label">{% endverbatim %}{% trans 'Permission' %}{% verbatim %}</label> <label for="id_perm_select" class="col-sm-2 control-label">{% endverbatim %}{% trans 'Permission' %}{% verbatim %}</label>
<div class="col-sm-10"> <div class="col-sm-10">
<select id="id_perm_select" name="group" class="selectpicker show-menu-arrow show-tick modal_field_data" data-style="btn-default" data-width="100%"> <select id="id_perm_select" name="group" class="selectpicker show-menu-arrow show-tick modal_field_data" data-style="btn-default" data-width="100%">
<option value="1">Read only</option> <option value="1">{% endverbatim %}{% trans 'Read only' %}{% verbatim %}</option>
<option value="2">All Access</option> <option value="2">{% endverbatim %}{% trans 'Management Access' %}{% verbatim %}</option>
<option value="2">{% endverbatim %}{% trans 'Full Access' %}{% verbatim %}</option>
</select> </select>
</div> </div>
</div> </div>