1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-01-05 09:17:54 +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 import typing
from datetime import datetime 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 # Date related constants
NEVER: typing.Final[datetime] = datetime(1972, 7, 1) 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 @author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
import sys
import codecs import codecs
import logging
import typing
import collections.abc import collections.abc
import logging
import sys
import typing
from django.utils.translation import gettext_noop as _ 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.module import Module
from uds.core.util import net from uds.core.util import net
# Not imported at runtime, just for type checking # Not imported at runtime, just for type checking
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from uds.core.types.requests import ExtendedHttpRequestWithUser
from uds.core.environment import Environment from uds.core.environment import Environment
from uds import models from uds.core.types.requests import ExtendedHttpRequestWithUser
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -310,3 +310,28 @@ class Transport(Module):
If transport provides own link, this method provides the link itself If transport provides own link, this method provides the link itself
""" """
return 'https://www.udsenterprise.com' 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() env = self.get_environment()
obj = klass(env, values) 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.deserialize(obj, values)
self._cached_instance = obj self._cached_instance = obj

View File

@ -87,8 +87,6 @@ class Provider(ManagedObjectModel, TaggingMixin):
prov: 'ServiceProvider' = typing.cast( prov: 'ServiceProvider' = typing.cast(
'ServiceProvider', super().get_instance(values=values) 'ServiceProvider', super().get_instance(values=values)
) )
# Set uuid
prov.set_uuid(self.uuid)
return prov return prov
def is_in_maintenance(self) -> bool: 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()) 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: 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': 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' path = self.custom_glyptodon_path.value if self.use_glyptodon.as_bool() else '/guacamole'
# Remove trailing / # Remove trailing /
path = path.rstrip('/') path = path.rstrip('/')

View File

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

View File

@ -84,13 +84,8 @@ class TestTransport(transports.Transport):
if not values: if not values:
return return
# Strip spaces # Strip spaces
if not ( if not (self.test_url.value.startswith('http://') or self.test_url.value.startswith('https://')):
self.test_url.value.startswith('http://') raise exceptions.ui.ValidationError(_('The url must be http or https'))
or self.test_url.value.startswith('https://')
):
raise exceptions.ui.ValidationError(
_('The url must be http or https')
)
# Same check as normal RDP transport # Same check as normal RDP transport
def is_ip_allowed(self, userservice: 'models.UserService', ip: str) -> bool: 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: str = user.get_username_for_auth()
username, password = userservice.process_user_password(username, password) username, password = userservice.process_user_password(username, password)
url = self.test_url.value.replace('_IP_', ip).replace('_USER_', username) url = self.update_link_window(
self.test_url.value.replace('_IP_', ip).replace('_USER_', username),
onw = ( on_same_window=False,
'&o_n_w={}'.format(hash(transport.name)) on_new_window=self.force_new_window.as_bool(),
if self.force_new_window.as_bool()
else ''
) )
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 old_field_name='urlPattern', # Allows compat with old versions
) )
force_new_window = gui.CheckBoxField( force_new_window = gui.ChoiceField(
label=_('Force new HTML Window'),
order=91, order=91,
tooltip=_( label=_('Force new HTML Window'),
'If checked, every connection will try to open its own window instead of reusing the "global" one.' 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.'),
), ),
default=False, 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, 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: def initialize(self, values: 'types.core.ValuesType') -> None:
if not values: if not values:
return return
# Strip spaces # Strip spaces
if not ( if not (self.url_pattern.value.startswith('http://') or self.url_pattern.value.startswith('https://')):
self.url_pattern.value.startswith('http://') raise exceptions.ui.ValidationError(_('The url must be http or https'))
or self.url_pattern.value.startswith('https://')
):
raise exceptions.ui.ValidationError(
_('The url must be http or https')
)
# Same check as normal RDP transport # Same check as normal RDP transport
def is_ip_allowed(self, userservice: 'models.UserService', ip: str) -> bool: 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: str = user.get_username_for_auth()
username, password = userservice.process_user_password(username, password) username, password = userservice.process_user_password(username, password)
url = self.url_pattern.value.replace('_IP_', ip).replace('_USER_', username) return self.update_link_window(
self.url_pattern.value.replace('_IP_', ip).replace('_USER_', username),
onw = ( on_same_window=self.force_new_window.value == 'overwrite',
'&o_n_w={}'.format(hash(transport.name)) on_new_window=self.force_new_window.value == 'true',
if self.force_new_window.as_bool() uuid=userservice.service_pool.uuid if self.force_new_window.value == 'true' else None,
else '' default_uuid=userservice.service_pool.uuid,
) )
return str("{}{}".format(url, onw))