1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-01-03 01:17:56 +03:00

Making more homogeneous the new window on brower-based transports

This commit is contained in:
Adolfo Gómez García 2024-04-09 16:01:26 +02:00
parent fca36ead57
commit 339eeb7967
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
11 changed files with 120 additions and 62 deletions

View File

@ -35,7 +35,7 @@ import time
import typing
from datetime import datetime
from . import actor, auth, cache, calendar, images, net, os, system, ticket, rest, services
from . import actor, auth, cache, calendar, images, net, os, system, ticket, rest, services, transports
# Date related constants
NEVER: typing.Final[datetime] = datetime(1972, 7, 1)

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2023 Virtual Cable S.L.U.
# 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 typing
ON_NEW_WINDOW_VAR: typing.Final[str] = 'o_n_w'
ON_SAME_WINDOW_VAR: typing.Final[str] = 'o_s_w'

View File

@ -30,23 +30,23 @@
"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import sys
import codecs
import logging
import typing
import collections.abc
import logging
import sys
import typing
from django.utils.translation import gettext_noop as _
from uds.core import types, consts
from uds import models
from uds.core import consts, types
from uds.core.module import Module
from uds.core.util import net
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
from uds.core.types.requests import ExtendedHttpRequestWithUser
from uds.core.environment import Environment
from uds import models
from uds.core.types.requests import ExtendedHttpRequestWithUser
logger = logging.getLogger(__name__)
@ -310,3 +310,28 @@ class Transport(Module):
If transport provides own link, this method provides the link itself
"""
return 'https://www.udsenterprise.com'
def update_link_window(
self,
link: str,
*,
on_same_window: bool = False,
on_new_window: bool = False,
uuid: typing.Optional[str] = None,
default_uuid: typing.Optional[str] = None,
) -> str:
uuid = uuid or self.get_uuid()
default_uuid = default_uuid or self.get_uuid()
amp = '&' if '?' in link else '?'
if not on_new_window and not on_same_window:
return f'{link}{amp}{consts.transports.ON_SAME_WINDOW_VAR}={default_uuid}'
if on_same_window:
return f'{link}{amp}{consts.transports.ON_SAME_WINDOW_VAR}=yes'
# Must be on new window
return f'{link}{amp}{consts.transports.ON_NEW_WINDOW_VAR}={uuid}'
return link

View File

@ -112,6 +112,7 @@ class ManagedObjectModel(UUIDModel):
env = self.get_environment()
obj = klass(env, values)
obj.set_uuid(self.uuid) # Set the uuid of the object to the one stored in the database
self.deserialize(obj, values)
self._cached_instance = obj

View File

@ -87,8 +87,6 @@ class Provider(ManagedObjectModel, TaggingMixin):
prov: 'ServiceProvider' = typing.cast(
'ServiceProvider', super().get_instance(values=values)
)
# Set uuid
prov.set_uuid(self.uuid)
return prov
def is_in_maintenance(self) -> bool:

File diff suppressed because one or more lines are too long

View File

@ -490,11 +490,11 @@ class HTML5RDPTransport(transports.Transport):
ticket = models.TicketStore.create(params, validity=self.ticket_validity.as_int())
onw = f'&o_n_w={transport.uuid}'
onw = f'&{consts.transports.ON_NEW_WINDOW_VAR}={transport.uuid}'
if self.force_new_window.value == consts.TRUE_STR:
onw = f'&o_n_w={userservice.deployed_service.uuid}'
onw = f'&{consts.transports.ON_NEW_WINDOW_VAR}={userservice.deployed_service.uuid}'
elif self.force_new_window.value == 'overwrite':
onw = '&o_s_w=yes'
onw = f'&{consts.transports.ON_SAME_WINDOW_VAR}=yes'
path = self.custom_glyptodon_path.value if self.use_glyptodon.as_bool() else '/guacamole'
# Remove trailing /
path = path.rstrip('/')

View File

@ -70,7 +70,7 @@ class HTML5SSHTransport(transports.Transport):
tunnel = fields.tunnel_field()
useGlyptodonTunnel = HTML5RDPTransport.use_glyptodon
use_glyptodon = HTML5RDPTransport.use_glyptodon
username = gui.TextField(
label=_('Username'),
@ -225,16 +225,14 @@ class HTML5SSHTransport(transports.Transport):
scrambler = CryptoManager().random_string(32)
ticket = models.TicketStore.create(params, validity=self.ticket_validity.as_int())
onw = ''
if self.force_new_window.value == 'true':
onw = 'o_n_w={}'
onw = f'&{consts.transports.ON_NEW_WINDOW_VAR}={transport.uuid}'
if self.force_new_window.value == consts.TRUE_STR:
onw = f'&{consts.transports.ON_NEW_WINDOW_VAR}={userservice.deployed_service.uuid}'
elif self.force_new_window.value == 'overwrite':
onw = 'o_s_w=yes'
onw = onw.format(hash(transport.name))
path = self.custom_glyptodon_path.value if self.useGlyptodonTunnel.as_bool() else '/guacamole'
onw = f'&{consts.transports.ON_SAME_WINDOW_VAR}=yes'
path = self.custom_glyptodon_path.value if self.use_glyptodon.as_bool() else '/guacamole'
# Remove trailing /
path = path.rstrip('/')
tunnel_server = fields.get_tunnel_from_field(self.tunnel)
return str(f'https://{tunnel_server.host}:{tunnel_server.port}{path}/#/?data={ticket}.{scrambler}{onw}')
return f'https://{tunnel_server.host}:{tunnel_server.port}{path}/#/?data={ticket}.{scrambler}{onw}'

View File

@ -197,12 +197,14 @@ class HTML5VNCTransport(transports.Transport):
scrambler = CryptoManager().random_string(32)
ticket = models.TicketStore.create(params, validity=self.ticket_validity.as_int())
onw = ''
if self.force_new_window.value == 'true':
onw = 'o_n_w={}'
onw = f'&{consts.transports.ON_NEW_WINDOW_VAR}={transport.uuid}'
if self.force_new_window.value == consts.TRUE_STR:
onw = f'&{consts.transports.ON_NEW_WINDOW_VAR}={userservice.deployed_service.uuid}'
elif self.force_new_window.value == 'overwrite':
onw = 'o_s_w=yes'
onw = onw.format(hash(transport.name))
onw = f'&{consts.transports.ON_SAME_WINDOW_VAR}=yes'
path = self.custom_glyptodon_path.value if self.use_glyptodon.as_bool() else '/guacamole'
# Remove trailing /
path = path.rstrip('/')
path = self.custom_glyptodon_path.value if self.use_glyptodon.as_bool() else '/guacamole'
# Remove trailing /

View File

@ -84,13 +84,8 @@ class TestTransport(transports.Transport):
if not values:
return
# Strip spaces
if not (
self.test_url.value.startswith('http://')
or self.test_url.value.startswith('https://')
):
raise exceptions.ui.ValidationError(
_('The url must be http or https')
)
if not (self.test_url.value.startswith('http://') or self.test_url.value.startswith('https://')):
raise exceptions.ui.ValidationError(_('The url must be http or https'))
# Same check as normal RDP transport
def is_ip_allowed(self, userservice: 'models.UserService', ip: str) -> bool:
@ -112,11 +107,10 @@ class TestTransport(transports.Transport):
username: str = user.get_username_for_auth()
username, password = userservice.process_user_password(username, password)
url = self.test_url.value.replace('_IP_', ip).replace('_USER_', username)
onw = (
'&o_n_w={}'.format(hash(transport.name))
if self.force_new_window.as_bool()
else ''
url = self.update_link_window(
self.test_url.value.replace('_IP_', ip).replace('_USER_', username),
on_same_window=False,
on_new_window=self.force_new_window.as_bool(),
)
return str("{}{}".format(url, onw))
return url

View File

@ -72,28 +72,33 @@ class URLCustomTransport(transports.Transport):
old_field_name='urlPattern', # Allows compat with old versions
)
force_new_window = gui.CheckBoxField(
label=_('Force new HTML Window'),
force_new_window = gui.ChoiceField(
order=91,
tooltip=_(
'If checked, every connection will try to open its own window instead of reusing the "global" one.'
),
default=False,
label=_('Force new HTML Window'),
tooltip=_('Select windows behavior for opening URL'),
required=True,
choices=[
gui.choice_item(
'false',
_('Open every connection on the same window, but keeps UDS window.'),
),
gui.choice_item('true', _('Force every connection to be opened on a new window.')),
gui.choice_item(
'overwrite',
_('Override UDS window and replace it with the connection.'),
),
],
default='true',
tab=types.ui.Tab.ADVANCED,
old_field_name='forceNewWindow', # Allows compat with old versions
old_field_name='forceNewWindow',
)
def initialize(self, values: 'types.core.ValuesType') -> None:
if not values:
return
# Strip spaces
if not (
self.url_pattern.value.startswith('http://')
or self.url_pattern.value.startswith('https://')
):
raise exceptions.ui.ValidationError(
_('The url must be http or https')
)
if not (self.url_pattern.value.startswith('http://') or self.url_pattern.value.startswith('https://')):
raise exceptions.ui.ValidationError(_('The url must be http or https'))
# Same check as normal RDP transport
def is_ip_allowed(self, userservice: 'models.UserService', ip: str) -> bool:
@ -115,11 +120,10 @@ class URLCustomTransport(transports.Transport):
username: str = user.get_username_for_auth()
username, password = userservice.process_user_password(username, password)
url = self.url_pattern.value.replace('_IP_', ip).replace('_USER_', username)
onw = (
'&o_n_w={}'.format(hash(transport.name))
if self.force_new_window.as_bool()
else ''
return self.update_link_window(
self.url_pattern.value.replace('_IP_', ip).replace('_USER_', username),
on_same_window=self.force_new_window.value == 'overwrite',
on_new_window=self.force_new_window.value == 'true',
uuid=userservice.service_pool.uuid if self.force_new_window.value == 'true' else None,
default_uuid=userservice.service_pool.uuid,
)
return str("{}{}".format(url, onw))