diff --git a/server/src/uds/REST/methods/providers.py b/server/src/uds/REST/methods/providers.py index 0e87ab37..5011163e 100644 --- a/server/src/uds/REST/methods/providers.py +++ b/server/src/uds/REST/methods/providers.py @@ -40,6 +40,7 @@ from uds.core.util import permissions from uds.core.util.model import processUuid from .services import Services as DetailServices +from .services_usage import ServicesUsage from uds.REST import NotFound, RequestError from uds.REST.model import ModelHandler @@ -54,7 +55,11 @@ class Providers(ModelHandler): Providers REST handler ''' model = Provider - detail = {'services': DetailServices} + detail = { + 'services': DetailServices, + 'usage': ServicesUsage + } + custom_methods = [('allservices', False), ('service', False), ('maintenance', True)] save_fields = ['name', 'comments', 'tags'] diff --git a/server/src/uds/REST/methods/services_usage.py b/server/src/uds/REST/methods/services_usage.py new file mode 100644 index 00000000..db346e57 --- /dev/null +++ b/server/src/uds/REST/methods/services_usage.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +# pylint: disable=too-many-public-methods + +from __future__ import unicode_literals + +from django.utils.translation import ugettext as _ + +from uds.models import UserService +from uds.core.util.State import State +from uds.core.util.model import processUuid +from uds.core.util import log +from uds.REST.model import DetailHandler + + + +import logging + +logger = logging.getLogger(__name__) + +class ServicesUsage(DetailHandler): + ''' + Rest handler for Assigned Services, wich parent is Service + ''' + + @staticmethod + def itemToDict(item): + ''' + Converts an assigned/cached service db item to a dictionary for REST response + :param item: item to convert + :param is_cache: If item is from cache or not + ''' + props = item.getProperties() + + if item.user is None: + owner = '' + owner_info = { + 'auth_id': '', + 'user_id': '' + } + else: + owner = item.user.fullUsername + owner_info = { + 'auth_id': item.user.manager.uuid, + 'user_id': item.user.uuid + } + + + return { + 'id': item.uuid, + 'state_date': item.state_date, + 'creation_date': item.creation_date, + 'unique_id': item.unique_id, + 'friendly_name': item.friendly_name, + 'owner': owner, + 'owner_info': owner_info, + 'service': item.deployed_service.service.name, + 'service_id': item.deployed_service.service.uuid, + 'pool': item.deployed_service.name, + 'pool_id': item.deployed_service.uuid, + 'ip': props.get('ip', _('unknown')), + 'source_host': item.src_hostname, + 'source_ip': item.src_ip, + 'in_use': item.in_use + } + + def getItems(self, parent, item): + try: + if item is None: + userServicesQuery = UserService.objects.filter(deployed_service__service__provider=parent) + else: + userServicesQuery = UserService.objects.filter(deployed_service__service_uuid=processUuid(item)) + + return [ServicesUsage.itemToDict(k) for k in userServicesQuery.filter(state=State.USABLE).order_by('creation_date'). + prefetch_related('deployed_service').prefetch_related('deployed_service__service').prefetch_related('properties')] + + except Exception: + logger.exception('getItems') + self.invalidItemException() + + def getTitle(self, parent): + return _('Services Usage') + + def getFields(self, parent): + return [ + # {'creation_date': {'title': _('Creation date'), 'type': 'datetime'}}, + {'state_date': {'title': _('Access'), 'type': 'datetime'}}, + {'owner': {'title': _('Owner')}}, + {'service': {'title': _('Service')}}, + {'pool': {'title': _('Pool')}}, + {'unique_id': {'title': 'Unique ID'}}, + {'ip': {'title': _('IP')}}, + {'friendly_name': {'title': _('Friendly name')}}, + {'source_ip': {'title': _('Src Ip')}}, + {'source_host': {'title': _('Src Host')}}, + ] + + def getRowStyle(self, parent): + return {'field': 'state', 'prefix': 'row-state-'} + + def deleteItem(self, parent, item): # This is also used by CachedService, so we use "userServices" directly and is valid for both + try: + service = UserService.objects.get(uuid=processUuid(item)) + if service.deployed_service.service.provider.id != parent.id: + # Forged UUID service!!! + raise Exception('Access denied') + except Exception: + logger.exception('deleteItem') + self.invalidItemException() + + logger.debug('Deleting user service') + if service.state in (State.USABLE, State.REMOVING): + service.remove() + elif service.state == State.PREPARING: + service.cancel() + elif service.state == State.REMOVABLE: + self.invalidItemException(_('Item already being removed')) + else: + self.invalidItemException(_('Item is not removable')) + + return self.success() diff --git a/server/src/uds/REST/methods/user_services.py b/server/src/uds/REST/methods/user_services.py index 580ee254..159a6131 100644 --- a/server/src/uds/REST/methods/user_services.py +++ b/server/src/uds/REST/methods/user_services.py @@ -91,7 +91,7 @@ class AssignedService(DetailHandler): 'user_id': '' } else: - owner = item.user.manager.name + "-" + item.user.name + owner = item.user.fullUsername owner_info = { 'auth_id': item.user.manager.uuid, 'user_id': item.user.uuid diff --git a/server/src/uds/models/User.py b/server/src/uds/models/User.py index cf760a68..d8d6deb6 100644 --- a/server/src/uds/models/User.py +++ b/server/src/uds/models/User.py @@ -48,7 +48,7 @@ import logging logger = logging.getLogger(__name__) -__updated__ = '2015-04-30' +__updated__ = '2017-06-08' @python_2_unicode_compatible @@ -168,6 +168,10 @@ class User(UUIDModel): # This group matches yield g + @property + def fullUsername(self): + return self.manager.name + "\\" + self.name + def __str__(self): return u"User {0}(id:{1}) from auth {2}".format(self.name, self.id, self.manager.name) diff --git a/server/src/uds/static/adm/img/icons/usage.png b/server/src/uds/static/adm/img/icons/usage.png new file mode 100644 index 00000000..d02fc69b Binary files /dev/null and b/server/src/uds/static/adm/img/icons/usage.png differ diff --git a/server/src/uds/static/adm/js/gui-d-services.coffee b/server/src/uds/static/adm/js/gui-d-services.coffee index f79d163b..69869049 100644 --- a/server/src/uds/static/adm/js/gui-d-services.coffee +++ b/server/src/uds/static/adm/js/gui-d-services.coffee @@ -28,6 +28,7 @@ gui.providers.link = (event) -> providers: "providers-placeholder" provider_info: "provider-info-placeholder" services: "services-placeholder" + usage: "usage-placeholder" logs: "logs-placeholder" ) gui.setLinksEvents() @@ -194,6 +195,26 @@ gui.providers.link = (event) -> ) prevTables.push servicesTable prevTables.push logTable + + usageAPI = api.providers.detail(id, "usage", { permission: selected[0].permission }) + usage = new GuiElement(usageAPI, "usage-" + selected[0].type) + usageTable = usage.table( + icon: 'usage' + container: "usage-placeholder" + doNotLoadData: true + rowSelect: "multi" + + buttons: [ + "delete" + "xls" + ] + onDelete: gui.methods.del(usage, gettext("Delete user service"), gettext("User service deletion error"),) + scrollToTable: false + onLoad: (k) -> + gui.tools.unblockUI() + return + ) + return buttons: [ diff --git a/server/src/uds/templates/uds/admin/tmpl/providers.html b/server/src/uds/templates/uds/admin/tmpl/providers.html index a3316ebc..00978f6c 100644 --- a/server/src/uds/templates/uds/admin/tmpl/providers.html +++ b/server/src/uds/templates/uds/admin/tmpl/providers.html @@ -29,6 +29,7 @@
@@ -46,6 +47,12 @@
+
+
+
+
+
+
...
diff --git a/server/src/uds/transports/RDP/RDPFile.py b/server/src/uds/transports/RDP/RDPFile.py index 6c063e5d..e6d08fd2 100644 --- a/server/src/uds/transports/RDP/RDPFile.py +++ b/server/src/uds/transports/RDP/RDPFile.py @@ -40,7 +40,7 @@ from uds.core.util import OsDetector import six import os -__updated__ = '2017-06-05' +__updated__ = '2017-06-07' class RDPFile(object): @@ -249,6 +249,8 @@ class RDPFile(object): res += 'authentication level:i:0' + '\n' res += 'prompt for credentials:i:0' + '\n' res += 'negotiate security layer:i:1\n' + res += 'bandwidthautodetect:i:1\n' + res += 'connection type:i:7\n' res += 'videoplaybackmode:i:1\n' if self.smoothFonts is True: res += 'allow font smoothing:i:1\n'