1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-10-27 11:33:54 +03:00
Files
openuds/server/src/uds/REST/methods/op_calendars.py

297 lines
12 KiB
Python

# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 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.U. 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
"""
import dataclasses
import datetime
import json
import logging
import typing
from django.utils.translation import gettext as _
from django.db.models import Model
from uds.core import exceptions, types, consts
from uds.core.types.rest import TableInfo
from uds.core.util import log, ensure, ui as ui_utils
from uds.core.util.model import process_uuid
from uds import models
from uds.REST.model import DetailHandler
# Not imported at runtime, just for type checking
logger = logging.getLogger(__name__)
ALLOW = 'ALLOW'
DENY = 'DENY'
@dataclasses.dataclass
class AccessCalendarItem(types.rest.BaseRestItem):
id: str
calendar_id: str
calendar: str
access: str
priority: int
class AccessCalendars(DetailHandler[AccessCalendarItem]):
@staticmethod
def as_item(item: 'models.CalendarAccess|models.CalendarAccessMeta') -> AccessCalendarItem:
return AccessCalendarItem(
id=item.uuid,
calendar_id=item.calendar.uuid,
calendar=item.calendar.name,
access=item.access,
priority=item.priority,
)
def get_items(
self, parent: 'Model', item: typing.Optional[str]
) -> types.rest.ItemsResult[AccessCalendarItem]:
# parent can be a ServicePool or a metaPool
parent = typing.cast(typing.Union['models.ServicePool', 'models.MetaPool'], parent)
try:
if not item:
return [AccessCalendars.as_item(i) for i in self.filter_queryset(parent.calendarAccess.all())]
return AccessCalendars.as_item(parent.calendarAccess.get(uuid=process_uuid(item)))
except models.CalendarAccess.DoesNotExist:
raise exceptions.rest.NotFound(_('Access calendar not found: {}').format(item)) from None
except Exception as e:
logger.exception('err: %s', item)
raise exceptions.rest.RequestError(f'Error retrieving access calendar: {e}') from e
def get_table(self, parent: 'Model') -> types.rest.TableInfo:
return (
ui_utils.TableBuilder(_('Access calendars'))
.numeric_column('priority', _('Priority'))
.text_column('calendar', _('Calendar'))
.text_column('access', _('Access'))
.build()
)
def save_item(self, parent: 'Model', item: typing.Optional[str]) -> typing.Any:
parent = typing.cast(typing.Union['models.ServicePool', 'models.MetaPool'], parent)
# If already exists
uuid = process_uuid(item) if item is not None else None
try:
calendar: models.Calendar = models.Calendar.objects.get(
uuid=process_uuid(self._params['calendar_id'])
)
access: str = self._params['access'].upper()
if access not in (ALLOW, DENY):
raise Exception()
except models.Calendar.DoesNotExist:
raise exceptions.rest.NotFound(
_('Calendar not found: {}').format(self._params['calendar_id'])
) from None
except Exception as e:
logger.error('Error saving calendar access: %s', e)
raise exceptions.rest.RequestError(_('Invalid parameters on request')) from e
priority = int(self._params['priority'])
if uuid is not None:
calendar_access = parent.calendarAccess.get(uuid=uuid)
calendar_access.calendar = calendar
calendar_access.access = access
calendar_access.priority = priority
calendar_access.save(update_fields=['calendar', 'access', 'priority'])
else:
calendar_access = parent.calendarAccess.create(calendar=calendar, access=access, priority=priority)
log.log(
parent,
types.log.LogLevel.INFO,
f'{"Added" if uuid is None else "Updated"} access calendar {calendar.name}/{access} by {self._user.pretty_name}',
types.log.LogSource.ADMIN,
)
return {'id': calendar_access.uuid}
def delete_item(self, parent: 'Model', item: str) -> None:
parent = typing.cast(typing.Union['models.ServicePool', 'models.MetaPool'], parent)
calendar_access = parent.calendarAccess.get(uuid=process_uuid(self._args[0]))
log_str = f'Removed access calendar {calendar_access.calendar.name} by {self._user.pretty_name}'
calendar_access.delete()
log.log(parent, types.log.LogLevel.INFO, log_str, types.log.LogSource.ADMIN)
@dataclasses.dataclass
class ActionCalendarItem(types.rest.BaseRestItem):
id: str
calendar_id: str
calendar: str
action: str
description: str
at_start: bool
events_offset: int
params: dict[str, typing.Any]
pretty_params: str
next_execution: typing.Optional[datetime.datetime]
last_execution: typing.Optional[datetime.datetime]
class ActionsCalendars(DetailHandler[ActionCalendarItem]):
"""
Processes the transports detail requests of a Service Pool
"""
CUSTOM_METHODS = [
'execute',
]
@staticmethod
def as_dict(item: 'models.CalendarAction') -> ActionCalendarItem:
action = consts.calendar.CALENDAR_ACTION_DICT.get(item.action)
descrption = action.get('description') if action is not None else ''
params = json.loads(item.params)
return ActionCalendarItem(
id=item.uuid,
calendar_id=item.calendar.uuid,
calendar=item.calendar.name,
action=item.action,
description=descrption,
at_start=item.at_start,
events_offset=item.events_offset,
params=params,
pretty_params=item.pretty_params,
next_execution=item.next_execution,
last_execution=item.last_execution,
)
def get_items(
self, parent: 'Model', item: typing.Optional[str]
) -> types.rest.ItemsResult[ActionCalendarItem]:
parent = ensure.is_instance(parent, models.ServicePool)
try:
if item is None:
return [ActionsCalendars.as_dict(i) for i in self.filter_queryset(parent.calendaraction_set.all())]
i = parent.calendaraction_set.get(uuid=process_uuid(item))
return ActionsCalendars.as_dict(i)
except models.CalendarAction.DoesNotExist:
raise exceptions.rest.NotFound(_('Scheduled action not found: {}').format(item)) from None
except Exception as e:
logger.error('Error retrieving scheduled action %s: %s', item, e)
raise exceptions.rest.RequestError(f'Error retrieving scheduled action: {e}') from e
def get_table(self, parent: 'Model') -> TableInfo:
return (
ui_utils.TableBuilder(_('Scheduled actions'))
.text_column('calendar', _('Calendar'))
.text_column('description', _('Action'))
.text_column('pretty_params', _('Parameters'))
.dict_column('at_start', _('Relative to'), dct={True: _('Start'), False: _('End')})
.text_column('events_offset', _('Time offset'))
.datetime_column('next_execution', _('Next execution'))
.datetime_column('last_execution', _('Last execution'))
.build()
)
def save_item(self, parent: 'Model', item: typing.Optional[str]) -> typing.Any:
parent = ensure.is_instance(parent, models.ServicePool)
# If already exists
uuid = process_uuid(item) if item is not None else None
calendar = models.Calendar.objects.get(uuid=process_uuid(self._params['calendar_id']))
action = self._params['action'].upper()
if action not in consts.calendar.CALENDAR_ACTION_DICT:
raise exceptions.rest.RequestError(_('Invalid action: {}').format(action))
events_offset = int(self._params['events_offset'])
at_start = self._params['at_start'] not in ('false', False, '0', 0)
params = json.dumps(self._params['params'])
# logger.debug('Got parameters: {} {} {} {} ----> {}'.format(calendar, action, events_offset, at_start, params))
log_string = (
f'{"Added" if uuid is None else "Updated"} scheduled action '
f'{calendar.name},{action},{events_offset},{"start" if at_start else "end"},{params} '
f'by {self._user.pretty_name}'
)
if uuid is not None:
calendar_action = models.CalendarAction.objects.get(uuid=uuid)
calendar_action.calendar = calendar
calendar_action.service_pool = parent
calendar_action.action = action
calendar_action.at_start = at_start
calendar_action.events_offset = events_offset
calendar_action.params = params
calendar_action.save()
else:
calendar_action = models.CalendarAction.objects.create(
calendar=calendar,
service_pool=parent,
action=action,
at_start=at_start,
events_offset=events_offset,
params=params,
)
log.log(parent, types.log.LogLevel.INFO, log_string, types.log.LogSource.ADMIN)
return {'id': calendar_action.uuid}
def delete_item(self, parent: 'Model', item: str) -> None:
parent = ensure.is_instance(parent, models.ServicePool)
calendar_action = models.CalendarAction.objects.get(uuid=process_uuid(self._args[0]))
log_str = (
f'Removed scheduled action "{calendar_action.calendar.name},'
f'{calendar_action.action},{calendar_action.events_offset},'
f'{calendar_action.at_start and "Start" or "End"},'
f'{calendar_action.params}" by {self._user.pretty_name}'
)
calendar_action.delete()
log.log(parent, types.log.LogLevel.INFO, log_str, types.log.LogSource.ADMIN)
def execute(self, parent: 'Model', item: str) -> typing.Any:
parent = ensure.is_instance(parent, models.ServicePool)
logger.debug('Launching action')
uuid = process_uuid(item)
calendar_action: models.CalendarAction = models.CalendarAction.objects.get(uuid=uuid)
self.check_access(calendar_action, types.permissions.PermissionType.MANAGEMENT)
log_str = (
f'Launched scheduled action "{calendar_action.calendar.name},'
f'{calendar_action.action},{calendar_action.events_offset},'
f'{calendar_action.at_start and "Start" or "End"},'
f'{calendar_action.params}" by {self._user.pretty_name}'
)
log.log(parent, types.log.LogLevel.INFO, log_str, types.log.LogSource.ADMIN)
calendar_action.execute()
return self.success()