Updated default wait-time && fixed mac access

This commit is contained in:
Adolfo Gómez García 2020-11-16 13:09:33 +01:00
parent e4345dfefa
commit 024bb5e748
7 changed files with 302 additions and 83 deletions

View File

@ -46,6 +46,7 @@ def createADUser():
except ImportError: except ImportError:
return return
# 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 import models from uds import models
@ -60,17 +61,57 @@ class BaseRDPTransport(transports.Transport):
Provides access via RDP to service. Provides access via RDP to service.
This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password
""" """
iconFile = 'rdp.png' iconFile = 'rdp.png'
protocol = transports.protocols.RDP protocol = transports.protocols.RDP
useEmptyCreds = gui.CheckBoxField(label=_('Empty creds'), order=11, tooltip=_('If checked, the credentials used to connect will be emtpy'), tab=gui.CREDENTIALS_TAB) useEmptyCreds = gui.CheckBoxField(
fixedName = gui.TextField(label=_('Username'), order=12, tooltip=_('If not empty, this username will be always used as credential'), tab=gui.CREDENTIALS_TAB) label=_('Empty creds'),
fixedPassword = gui.PasswordField(label=_('Password'), order=13, tooltip=_('If not empty, this password will be always used as credential'), tab=gui.CREDENTIALS_TAB) order=11,
withoutDomain = gui.CheckBoxField(label=_('Without Domain'), order=14, tooltip=_('If checked, the domain part will always be emptied (to connect to xrdp for example is needed)'), tab=gui.CREDENTIALS_TAB) tooltip=_('If checked, the credentials used to connect will be emtpy'),
fixedDomain = gui.TextField(label=_('Domain'), order=15, tooltip=_('If not empty, this domain will be always used as credential (used as DOMAIN\\user)'), tab=gui.CREDENTIALS_TAB) tab=gui.CREDENTIALS_TAB,
)
fixedName = gui.TextField(
label=_('Username'),
order=12,
tooltip=_('If not empty, this username will be always used as credential'),
tab=gui.CREDENTIALS_TAB,
)
fixedPassword = gui.PasswordField(
label=_('Password'),
order=13,
tooltip=_('If not empty, this password will be always used as credential'),
tab=gui.CREDENTIALS_TAB,
)
withoutDomain = gui.CheckBoxField(
label=_('Without Domain'),
order=14,
tooltip=_(
'If checked, the domain part will always be emptied (to connect to xrdp for example is needed)'
),
tab=gui.CREDENTIALS_TAB,
)
fixedDomain = gui.TextField(
label=_('Domain'),
order=15,
tooltip=_(
'If not empty, this domain will be always used as credential (used as DOMAIN\\user)'
),
tab=gui.CREDENTIALS_TAB,
)
allowSmartcards = gui.CheckBoxField(label=_('Allow Smartcards'), order=20, tooltip=_('If checked, this transport will allow the use of smartcards'), tab=gui.PARAMETERS_TAB) allowSmartcards = gui.CheckBoxField(
allowPrinters = gui.CheckBoxField(label=_('Allow Printers'), order=21, tooltip=_('If checked, this transport will allow the use of user printers'), tab=gui.PARAMETERS_TAB) label=_('Allow Smartcards'),
order=20,
tooltip=_('If checked, this transport will allow the use of smartcards'),
tab=gui.PARAMETERS_TAB,
)
allowPrinters = gui.CheckBoxField(
label=_('Allow Printers'),
order=21,
tooltip=_('If checked, this transport will allow the use of user printers'),
tab=gui.PARAMETERS_TAB,
)
allowDrives = gui.ChoiceField( allowDrives = gui.ChoiceField(
label=_('Local drives policy'), label=_('Local drives policy'),
order=22, order=22,
@ -81,20 +122,51 @@ class BaseRDPTransport(transports.Transport):
{'id': 'dynamic', 'text': 'Allow PnP drives'}, {'id': 'dynamic', 'text': 'Allow PnP drives'},
{'id': 'true', 'text': 'Allow any drive'}, {'id': 'true', 'text': 'Allow any drive'},
], ],
tab=gui.PARAMETERS_TAB tab=gui.PARAMETERS_TAB,
) )
enforceDrives = gui.TextField( enforceDrives = gui.TextField(
label=_('Force drives'), label=_('Force drives'),
order=23, order=23,
tooltip=_('Use comma separated values, for example "C:,D:". If drives policy is disallowed, this will be ignored'), tooltip=_(
tab=gui.PARAMETERS_TAB 'Use comma separated values, for example "C:,D:". If drives policy is disallowed, this will be ignored'
),
tab=gui.PARAMETERS_TAB,
) )
allowSerials = gui.CheckBoxField(label=_('Allow Serials'), order=24, tooltip=_('If checked, this transport will allow the use of user serial ports'), tab=gui.PARAMETERS_TAB) allowSerials = gui.CheckBoxField(
allowClipboard = gui.CheckBoxField(label=_('Enable clipboard'), order=25, tooltip=_('If checked, copy-paste functions will be allowed'), tab=gui.PARAMETERS_TAB, defvalue=gui.TRUE) label=_('Allow Serials'),
allowAudio = gui.CheckBoxField(label=_('Enable sound'), order=26, tooltip=_('If checked, sound will be redirected.'), tab=gui.PARAMETERS_TAB, defvalue=gui.TRUE) order=24,
allowWebcam = gui.CheckBoxField(label=_('Enable webcam'), order=27, tooltip=_('If checked, webcam will be redirected (ONLY Windows).'), tab=gui.PARAMETERS_TAB, defvalue=gui.FALSE) tooltip=_('If checked, this transport will allow the use of user serial ports'),
credssp = gui.CheckBoxField(label=_('Credssp Support'), order=28, tooltip=_('If checked, will enable Credentials Provider Support)'), tab=gui.PARAMETERS_TAB, defvalue=gui.TRUE) tab=gui.PARAMETERS_TAB,
)
allowClipboard = gui.CheckBoxField(
label=_('Enable clipboard'),
order=25,
tooltip=_('If checked, copy-paste functions will be allowed'),
tab=gui.PARAMETERS_TAB,
defvalue=gui.TRUE,
)
allowAudio = gui.CheckBoxField(
label=_('Enable sound'),
order=26,
tooltip=_('If checked, sound will be redirected.'),
tab=gui.PARAMETERS_TAB,
defvalue=gui.TRUE,
)
allowWebcam = gui.CheckBoxField(
label=_('Enable webcam'),
order=27,
tooltip=_('If checked, webcam will be redirected (ONLY Windows).'),
tab=gui.PARAMETERS_TAB,
defvalue=gui.FALSE,
)
credssp = gui.CheckBoxField(
label=_('Credssp Support'),
order=28,
tooltip=_('If checked, will enable Credentials Provider Support)'),
tab=gui.PARAMETERS_TAB,
defvalue=gui.TRUE,
)
screenSize = gui.ChoiceField( screenSize = gui.ChoiceField(
label=_('Screen Size'), label=_('Screen Size'),
@ -109,7 +181,7 @@ class BaseRDPTransport(transports.Transport):
{'id': '1920x1080', 'text': '1920x1080'}, {'id': '1920x1080', 'text': '1920x1080'},
{'id': '-1x-1', 'text': 'Full screen'}, {'id': '-1x-1', 'text': 'Full screen'},
], ],
tab=gui.DISPLAY_TAB tab=gui.DISPLAY_TAB,
) )
colorDepth = gui.ChoiceField( colorDepth = gui.ChoiceField(
@ -123,23 +195,105 @@ class BaseRDPTransport(transports.Transport):
{'id': '24', 'text': '24'}, {'id': '24', 'text': '24'},
{'id': '32', 'text': '32'}, {'id': '32', 'text': '32'},
], ],
tab=gui.DISPLAY_TAB tab=gui.DISPLAY_TAB,
) )
wallpaper = gui.CheckBoxField(label=_('Wallpaper/theme'), order=32, tooltip=_('If checked, the wallpaper and themes will be shown on machine (better user experience, more bandwidth)'), tab=gui.DISPLAY_TAB) wallpaper = gui.CheckBoxField(
multimon = gui.CheckBoxField(label=_('Multiple monitors'), order=33, tooltip=_('If checked, all client monitors will be used for displaying (only works on windows clients)'), tab=gui.DISPLAY_TAB) label=_('Wallpaper/theme'),
aero = gui.CheckBoxField(label=_('Allow Desk.Comp.'), order=34, tooltip=_('If checked, desktop composition will be allowed'), tab=gui.DISPLAY_TAB) order=32,
smooth = gui.CheckBoxField(label=_('Font Smoothing'), order=35, tooltip=_('If checked, fonts smoothing will be allowed'), tab=gui.DISPLAY_TAB) tooltip=_(
showConnectionBar = gui.CheckBoxField(label=_('Connection Bar'), order=36, tooltip=_('If checked, connection bar will be shown (only on Windows clients)'), tab=gui.DISPLAY_TAB, defvalue=gui.TRUE) 'If checked, the wallpaper and themes will be shown on machine (better user experience, more bandwidth)'
),
tab=gui.DISPLAY_TAB,
)
multimon = gui.CheckBoxField(
label=_('Multiple monitors'),
order=33,
tooltip=_(
'If checked, all client monitors will be used for displaying (only works on windows clients)'
),
tab=gui.DISPLAY_TAB,
)
aero = gui.CheckBoxField(
label=_('Allow Desk.Comp.'),
order=34,
tooltip=_('If checked, desktop composition will be allowed'),
tab=gui.DISPLAY_TAB,
)
smooth = gui.CheckBoxField(
label=_('Font Smoothing'),
order=35,
tooltip=_('If checked, fonts smoothing will be allowed'),
tab=gui.DISPLAY_TAB,
)
showConnectionBar = gui.CheckBoxField(
label=_('Connection Bar'),
order=36,
tooltip=_('If checked, connection bar will be shown (only on Windows clients)'),
tab=gui.DISPLAY_TAB,
defvalue=gui.TRUE,
)
multimedia = gui.CheckBoxField(label=_('Multimedia sync'), order=40, tooltip=_('If checked. Linux client will use multimedia parameter for xfreerdp'), tab='Linux Client') multimedia = gui.CheckBoxField(
alsa = gui.CheckBoxField(label=_('Use Alsa'), order=41, tooltip=_('If checked, Linux client will try to use ALSA, otherwise Pulse will be used'), tab='Linux Client') label=_('Multimedia sync'),
redirectHome = gui.CheckBoxField(label=_('Redirect home folder'), order=42, tooltip=_('If checked, Linux client will try to redirect /home local folder'), tab='Linux Client', defvalue=gui.FALSE) order=40,
printerString = gui.TextField(label=_('Printer string'), order=43, tooltip=_('If printer is checked, the printer string used with xfreerdp client'), tab='Linux Client', length=256) tooltip=_(
smartcardString = gui.TextField(label=_('Smartcard string'), order=44, tooltip=_('If smartcard is checked, the smartcard string used with xfreerdp client'), tab='Linux Client', length=256) 'If checked. Linux client will use multimedia parameter for xfreerdp'
customParameters = gui.TextField(label=_('Custom parameters'), order=45, tooltip=_('If not empty, extra parameter to include for Linux Client (for example /usb:id,dev:054c:0268, or aything compatible with your xfreerdp client)'), tab='Linux Client', length=256) ),
tab='Linux Client',
)
alsa = gui.CheckBoxField(
label=_('Use Alsa'),
order=41,
tooltip=_(
'If checked, Linux client will try to use ALSA, otherwise Pulse will be used'
),
tab='Linux Client',
)
redirectHome = gui.CheckBoxField(
label=_('Redirect home folder'),
order=42,
tooltip=_('If checked, Linux client will try to redirect /home local folder'),
tab='Linux Client',
defvalue=gui.FALSE,
)
printerString = gui.TextField(
label=_('Printer string'),
order=43,
tooltip=_(
'If printer is checked, the printer string used with xfreerdp client'
),
tab='Linux Client',
length=256,
)
smartcardString = gui.TextField(
label=_('Smartcard string'),
order=44,
tooltip=_(
'If smartcard is checked, the smartcard string used with xfreerdp client'
),
tab='Linux Client',
length=256,
)
customParameters = gui.TextField(
label=_('Custom parameters'),
order=45,
tooltip=_(
'If not empty, extra parameter to include for Linux Client (for example /usb:id,dev:054c:0268, or aything compatible with your xfreerdp client)'
),
tab='Linux Client',
length=256,
)
allowMacMSRDC = gui.CheckBoxField(label=_('Allow Microsoft Rdp Client'), order=40, tooltip=_('If checked, allows use of Microsoft Remote Desktop Clien. PASSWORD WILL BE PRONPTED!'), tab='Mac OS X', defvalue=gui.FALSE) allowMacMSRDC = gui.CheckBoxField(
label=_('Allow Microsoft Rdp Client'),
order=40,
tooltip=_(
'If checked, allows use of Microsoft Remote Desktop Client. PASSWORD WILL BE PROMPTED!'
),
tab='Mac OS X',
defvalue=gui.FALSE,
)
def isAvailableFor(self, userService: 'models.UserService', ip: str) -> bool: def isAvailableFor(self, userService: 'models.UserService', ip: str) -> bool:
""" """
@ -157,11 +311,15 @@ class BaseRDPTransport(transports.Transport):
self.cache.put(ip, 'N', READY_CACHE_TIMEOUT) self.cache.put(ip, 'N', READY_CACHE_TIMEOUT)
return ready == 'Y' return ready == 'Y'
def processedUser(self, userService: 'models.UserService', user: 'models.User') -> str: def processedUser(
self, userService: 'models.UserService', user: 'models.User'
) -> str:
v = self.processUserPassword(userService, user, '') v = self.processUserPassword(userService, user, '')
return v['username'] return v['username']
def processUserPassword(self, userService: 'models.UserService', user: 'models.User', password: 'str') -> typing.Dict[str, typing.Any]: def processUserPassword(
self, userService: 'models.UserService', user: 'models.User', password: 'str'
) -> typing.Dict[str, typing.Any]:
username = user.getUsernameForAuth() username = user.getUsernameForAuth()
if self.fixedName.value: if self.fixedName.value:
@ -201,22 +359,31 @@ class BaseRDPTransport(transports.Transport):
if '\\' in username: if '\\' in username:
domain, username = username.split('\\') domain, username = username.split('\\')
return {'protocol': self.protocol, 'username': username, 'password': password, 'domain': domain} return {
'protocol': self.protocol,
'username': username,
'password': password,
'domain': domain,
}
def getConnectionInfo( def getConnectionInfo(
self, self,
userService: typing.Union['models.UserService', 'models.ServicePool'], userService: typing.Union['models.UserService', 'models.ServicePool'],
user: 'models.User', user: 'models.User',
password: str password: str,
) -> typing.Dict[str, str]: ) -> typing.Dict[str, str]:
return self.processUserPassword(userService, user, password) return self.processUserPassword(userService, user, password)
def getScript(self, scriptNameTemplate: str, osName: str, params: typing.Dict[str, typing.Any]) -> typing.Tuple[str, str, typing.Dict[str, typing.Any]]: def getScript(
self, scriptNameTemplate: str, osName: str, params: typing.Dict[str, typing.Any]
) -> typing.Tuple[str, str, typing.Dict[str, typing.Any]]:
# Reads script # Reads script
scriptNameTemplate = scriptNameTemplate.format(osName) scriptNameTemplate = scriptNameTemplate.format(osName)
with open(os.path.join(os.path.dirname(__file__), scriptNameTemplate)) as f: with open(os.path.join(os.path.dirname(__file__), scriptNameTemplate)) as f:
script = f.read() script = f.read()
# Reads signature # Reads signature
with open(os.path.join(os.path.dirname(__file__), scriptNameTemplate + '.signature')) as f: with open(
os.path.join(os.path.dirname(__file__), scriptNameTemplate + '.signature')
) as f:
signature = f.read() signature = f.read()
return script, signature, params return script, signature, params

View File

@ -67,7 +67,7 @@ class RDPFile:
smartcardString = None smartcardString = None
enablecredsspsupport = False enablecredsspsupport = False
enableClipboard = False enableClipboard = False
linuxCustomParameters = None linuxCustomParameters: typing.Optional[str] = None
enforcedShares: typing.Optional[str] = None enforcedShares: typing.Optional[str] = None
def __init__( def __init__(
@ -183,7 +183,7 @@ class RDPFile:
if forceRDPSecurity: if forceRDPSecurity:
params.append('/sec:rdp') params.append('/sec:rdp')
if self.linuxCustomParameters is not None and self.linuxCustomParameters.strip() != '': if self.linuxCustomParameters and self.linuxCustomParameters.strip() != '':
params += shlex.split(self.linuxCustomParameters.strip()) params += shlex.split(self.linuxCustomParameters.strip())
return params return params
@ -191,15 +191,15 @@ class RDPFile:
def getGeneric(self): # pylint: disable=too-many-statements def getGeneric(self): # pylint: disable=too-many-statements
password = '{password}' password = '{password}'
screenMode = '2' if self.fullScreen else '1' screenMode = '2' if self.fullScreen else '1'
audioMode = self.redirectAudio and "0" or "2" audioMode = '2' if self.redirectAudio else '0'
serials = self.redirectSerials and "1" or "0" serials = '1' if self.redirectSerials else '0'
scards = self.redirectSmartcards and "1" or "0" scards = '1' if self.redirectSmartcards else '0'
printers = self.redirectPrinters and "1" or "0" printers = '1' if self.redirectPrinters else '0'
compression = self.compression and "1" or "0" compression = '1' if self.compression else '0'
connectionBar = self.displayConnectionBar and "1" or "0" connectionBar = '1' if self.displayConnectionBar else '0'
disableWallpaper = self.showWallpaper and "0" or "1" disableWallpaper = '1' if self.showWallpaper else '0'
useMultimon = self.multimon and "1" or "0" useMultimon = '1' if self.multimon else '0'
enableClipboard = self.enableClipboard and "1" or "0" enableClipboard = '1' if self.enableClipboard else '0'
res = '' res = ''
res += 'screen mode id:i:' + screenMode + '\n' res += 'screen mode id:i:' + screenMode + '\n'

View File

@ -57,19 +57,37 @@ READY_CACHE_TIMEOUT = 30
class TRDPTransport(BaseRDPTransport): class TRDPTransport(BaseRDPTransport):
''' """
Provides access via RDP to service. Provides access via RDP to service.
This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password
''' """
typeName = _('RDP') typeName = _('RDP')
typeType = 'TSRDPTransport' typeType = 'TSRDPTransport'
typeDescription = _('RDP Protocol. Tunneled connection.') typeDescription = _('RDP Protocol. Tunneled connection.')
group = transports.TUNNELED_GROUP group = transports.TUNNELED_GROUP
tunnelServer = gui.TextField(label=_('Tunnel server'), order=1, tooltip=_('IP or Hostname of tunnel server sent to client device ("public" ip) and port. (use HOST:PORT format)'), tab=gui.TUNNEL_TAB) tunnelServer = gui.TextField(
label=_('Tunnel server'),
order=1,
tooltip=_(
'IP or Hostname of tunnel server sent to client device ("public" ip) and port. (use HOST:PORT format)'
),
tab=gui.TUNNEL_TAB,
)
# tunnelCheckServer = gui.TextField(label=_('Tunnel host check'), order=2, tooltip=_('If not empty, this server will be used to check if service is running before assigning it to user. (use HOST:PORT format)'), tab=gui.TUNNEL_TAB) # tunnelCheckServer = gui.TextField(label=_('Tunnel host check'), order=2, tooltip=_('If not empty, this server will be used to check if service is running before assigning it to user. (use HOST:PORT format)'), tab=gui.TUNNEL_TAB)
tunnelWait = gui.NumericField(length=3, label=_('Tunnel wait time'), defvalue='10', minValue=1, maxValue=65536, order=2, tooltip=_('Maximum time to wait before closing the tunnel listener'), required=True, tab=gui.TUNNEL_TAB) tunnelWait = gui.NumericField(
length=3,
label=_('Tunnel wait time'),
defvalue='30',
minValue=5,
maxValue=65536,
order=2,
tooltip=_('Maximum time to wait before closing the tunnel listener'),
required=True,
tab=gui.TUNNEL_TAB,
)
useEmptyCreds = BaseRDPTransport.useEmptyCreds useEmptyCreds = BaseRDPTransport.useEmptyCreds
fixedName = BaseRDPTransport.fixedName fixedName = BaseRDPTransport.fixedName
@ -106,7 +124,9 @@ class TRDPTransport(BaseRDPTransport):
def initialize(self, values: 'Module.ValuesType'): def initialize(self, values: 'Module.ValuesType'):
if values: if values:
if values['tunnelServer'].count(':') != 1: if values['tunnelServer'].count(':') != 1:
raise transports.Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field')) raise transports.Transport.ValidationException(
_('Must use HOST:PORT in Tunnel Server Field')
)
def getUDSTransportScript( # pylint: disable=too-many-locals def getUDSTransportScript( # pylint: disable=too-many-locals
self, self,
@ -116,7 +136,7 @@ class TRDPTransport(BaseRDPTransport):
os: typing.Dict[str, str], os: typing.Dict[str, str],
user: 'models.User', user: 'models.User',
password: str, password: str,
request: 'HttpRequest' request: 'HttpRequest',
) -> typing.Tuple[str, str, typing.Dict[str, typing.Any]]: ) -> typing.Tuple[str, str, typing.Dict[str, typing.Any]]:
# We use helper to keep this clean # We use helper to keep this clean
# prefs = user.prefs('rdp') # prefs = user.prefs('rdp')
@ -132,14 +152,19 @@ class TRDPTransport(BaseRDPTransport):
width, height = self.screenSize.value.split('x') width, height = self.screenSize.value.split('x')
depth = self.colorDepth.value depth = self.colorDepth.value
tunpass = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _i in range(12)) tunpass = ''.join(
random.SystemRandom().choice(string.ascii_letters + string.digits)
for _i in range(12)
)
tunuser = TicketStore.create(tunpass) tunuser = TicketStore.create(tunpass)
sshHost, sshPort = self.tunnelServer.value.split(':') sshHost, sshPort = self.tunnelServer.value.split(':')
logger.debug('Username generated: %s, password: %s', tunuser, tunpass) logger.debug('Username generated: %s, password: %s', tunuser, tunpass)
r = RDPFile(width == '-1' or height == '-1', width, height, depth, target=os['OS']) r = RDPFile(
width == '-1' or height == '-1', width, height, depth, target=os['OS']
)
r.enablecredsspsupport = ci.get('sso') == 'True' or self.credssp.isTrue() r.enablecredsspsupport = ci.get('sso') == 'True' or self.credssp.isTrue()
r.address = '{address}' r.address = '{address}'
r.username = username r.username = username
@ -168,11 +193,13 @@ class TRDPTransport(BaseRDPTransport):
osName = { osName = {
OsDetector.Windows: 'windows', OsDetector.Windows: 'windows',
OsDetector.Linux: 'linux', OsDetector.Linux: 'linux',
OsDetector.Macintosh: 'macosx' OsDetector.Macintosh: 'macosx',
}.get(os['OS']) }.get(os['OS'])
if osName is None: if osName is None:
return super().getUDSTransportScript(userService, transport, ip, os, user, password, request) return super().getUDSTransportScript(
userService, transport, ip, os, user, password, request
)
sp = { sp = {
'tunUser': tunuser, 'tunUser': tunuser,
@ -188,19 +215,24 @@ class TRDPTransport(BaseRDPTransport):
if osName == 'windows': if osName == 'windows':
if password != '': if password != '':
r.password = '{password}' r.password = '{password}'
sp.update({ sp.update(
{
'as_file': r.as_file, 'as_file': r.as_file,
}) }
)
elif osName == 'linux': elif osName == 'linux':
sp.update({ sp.update(
{
'as_new_xfreerdp_params': r.as_new_xfreerdp_params, 'as_new_xfreerdp_params': r.as_new_xfreerdp_params,
}) }
)
else: # Mac else: # Mac
sp.update({ sp.update(
{
'as_new_xfreerdp_params': r.as_new_xfreerdp_params, 'as_new_xfreerdp_params': r.as_new_xfreerdp_params,
'as_file': r.as_file if self.allowMacMSRDC.isTrue() else '', 'as_file': r.as_file if self.allowMacMSRDC.isTrue() else '',
'as_rdp_url': r.as_rdp_url if self.allowMacMSRDC.isTrue() else '', 'as_rdp_url': r.as_rdp_url if self.allowMacMSRDC.isTrue() else '',
}) }
)
return self.getScript('scripts/{}/tunnel.py', osName, sp) return self.getScript('scripts/{}/tunnel.py', osName, sp)

View File

@ -49,6 +49,16 @@ if executable is None:
<ul> <ul>
<li> <li>
<p><b>Xfreerdp</b> from homebrew</p> <p><b>Xfreerdp</b> from homebrew</p>
<p>
<ul>
<li>Install brew (from brew.sh website)</li>
<li>Install xquartz<br/>
<b>brew cask install xquartz</b></li>
<li>Install freerdp<br/>
<b>brew install freerdp</b></li>
<li>Reboot so xquartz will be automatically started when needed</li>
</ul>
</p>
</li> </li>
</ul> </ul>
''') ''')

View File

@ -1 +1 @@
Hfx61+xy7yzVCe4mq8/9rekZvTpbW64z3rPRV7ythmT+zLjOdd07nCresS/mA5FwfK1B2dGqkUw2Sj7XYfPVHp+pfxa1MSVDeAeyh12mhLNi9AcEvrkRw7kfQ59ZHgm4fZvAtOpkWqN/pU+V73T5eTdhIBATRS9PCwJwbYOTiEw7ndF+nZpvW/n6E0grpSoqr2QuEqj8tK+4Sb7OTtwHeYku3KDM/CZ8RYP5+OLW1OcyjpqkbbL9xgN+8zPu0wNpBMHSzwRNkQ8I0WVvWhtyhbx2RpwaFBd2ETOAUzaczJoZ/R8dNOBqBbqUk3YkuJdGhUQ+bo8ZL9Ga14Ew/wKyht+G9IkiyLzVgOxKCPT05g0ept2DU2PqJw3isK3vzkhHV7JQC7T75Aqouc/N37YNcv/OWsTD+HcLHpZoo8kMaG6PQxyfg9smrcuJPoeOHVbklRLR7cptuZ8HPEyTCKjvCx5rpi2uvXw/LsHsEWesqYt69o8BOOMhm23Hz0LK/ZAhQ7Ll/9p17+g/JeUIFeBxuXNg5A28fvTdbK6GwzbSf54Y7/woyFFcw+ii/FRUpjehN0R9M6JVqLa0XTYgQuIwrPApL5uepdZZcNFXn/ax9lK7HPkJy55iun4buQFODxMi4slgJaPrjkiY8/LwQzh0cohwWBOy05U2nOEahxUQXrA= NL6cwtXIFixi6D/z5uksdNnAzd7RSP6xG92DzQ84XCGhBaYHtsMdk7pPMo/zwI6mMNyKJLqvwSUaS3eCdLU7SnTM3xAj4SSGSbFTNRD6q+2f2K2xU3etiGQagQ3vcmt+o79JMXP80qsfYA717fPlRLii7WRm56w5FwgiUWEmas4WIw6e7oXe6ucQoJJ9X4P6w3K5gR4cGBOHOz5HNxrIxy2d+OcahLV2du9efzaB+qJNpqon2ETkS0y3b5jO+xWcsChRGjzpdgcqXd6EkTz/Sow4zzQoeunkESz9TShNou3lJC8ITlBN+FKgJujh7wzpVDvcDIqRwQhkD7vaMsu+Yu/47LBhY30LHesmQcc7av5kkcCLWkz3QCRQo49G2/Flw6PuIidrxj1k5H/+yA0ktwXNB4EUsPtePLpWaYo0xZuaM1fr3kDpAcSAaKSIeAxJ2Oxl9HLNnBDwyTaZzZiIl23tbRIJeSDL9C42qXqgNWvq44TNxtuDpItJNyu1ov+ZJJaPxlYiC6C7v0sNNsvOiCJXl3CLS56v65W4xZSxAfC4juybRA09RugfpMO+N/hS/xGbaNVsYvEmS/UGVKdFBgrtF/SwTYO3mBzhbAyeKQhV7Ovd5Vs5noKd4DbZG7XKNASqbKWwkrZFzNxX2mYJpr9ZnACr4aJ+RnM38KDEGOQ=

View File

@ -52,6 +52,16 @@ if executable is None:
<ul> <ul>
<li> <li>
<p><b>Xfreerdp</b> from homebrew</p> <p><b>Xfreerdp</b> from homebrew</p>
<p>
<ul>
<li>Install brew (from brew.sh website)</li>
<li>Install xquartz<br/>
<b>brew cask install xquartz</b></li>
<li>Install freerdp<br/>
<b>brew install freerdp</b></li>
<li>Reboot so xquartz will be automatically started when needed</li>
</ul>
</p>
</li> </li>
</ul> </ul>
''') ''')

View File

@ -1 +1 @@
k9goVY5L7HOfpHjmcqnIiAaDnSVwlBJ/DNOPb1PL1XwpfaAgYp2qlAyY/fbi1YLWfAsbJhdhWgJH9iF1KLEx3Jn1P5J4ObpxKkVmjUW0oYF4qZdkZrik8VtHCgvwDkEPHyP/uzIwMWb8ydiTxoUBH+ZJTbo0qeQ7wgbnfljckJHNnWurVBtp2rFxjolQFR89cOGkLkvdPxHqcHAj1dxUkJdhmka9z3bVyb5NhmX8mtCvpR5QM3DHo8lRYHQxwtnRZooIrIr+5zKflzXoZnRn189tF2yCyk349MeUX3fnm8F22DpCP1HBkN5A9ACkRfANawbDlyR1ynoiApqEqLPM3aKojxifESNdGaD8pf9MXVsxzk+ufAphzRnU6lh3As4/+NNfJ3zbTbD6DFfj+oez0zEcF1QcA40z55+u8CYChltduJCwu1qUS0I+exAlLpEn9riJd2KmgxlLDDx/9EJjXOwVoUXKOteN4kHQXtMRB3CWneHezfgA6RdRolxxy5EBK71mwDf8BcG8seyBRXcd+p+mbhBcJPqdI/eci9g5h3JFHolOINsbNt2PZk23j7nvHRfArFCvJN1dkz7XZpgDPznvE+B0SzQHKEbGvybESlOfdo+DiFWtwt8mueTP6sMqncced+ztUsHdHDxFiVrIyh3KVzZiyEl2YFJxkLdrRH0= ZY1MWtxfNim0HgF+EKH7+DvyjKKbB+BvbNALHHcjeiiCnazZOI9ToMghEPUvMosPKWrbGJ+bmIJDdxpCLvIJJi2Vi9QHkXg9FiRR/1FJwwhLYiinv2bQa+ChYoEfVVZidCqFcDhaaIit9Kw/nBAa0luqZUYVB3ZUwbMgHPd4RNWCvcyGM2P6s+RzJyASajn2LPNrWqik2qQa2+ZHLR8mcsJ9+VYJApSbYhbOFyeM/QJCrhS7U9FQv9vJQGZES+UvcFYZy99QRv4V8Ko5izZ96EeVVaod3IXkGLd7HvC1fDeL7JZeQORJC3HZkLwslqqux9BX/1kb5XQV6qBDTsEpk3o/tw7PJVMHOu8l+LfNJnxFCyEwRkELhZeeZxvgAHYqYNuJyOsY6JyRu9okxwtAMkxYDJBWpDq9LAlHyPEZek3TXVrM+OAkz33TOM1AuDqToMadLM4tY2fsWCIEdEMCF9GFUEfGT2BT/U6x9JtFM8I8HVpdxXMkrSTagHkArmcX9+AbjifrBh6KYXFxiCPV/HZHRT0IrQ8JeaquUxKC9IRdCqVCiN6Nwq/Ww1hXzsYY07iEe/6xV5/deJhr8dLzM0+hDwnp2ag3T0DEghlBIZRZqV/CeSVPngespiyfOmbUQSpiokGtiYO6bHD8MHcgwn9uzr3xD9MCsArldpn9TSA=