Fixed connection client working

This commit is contained in:
Adolfo Gómez García 2021-05-04 12:32:56 +02:00
parent 9a7afe7839
commit e920628395
5 changed files with 143 additions and 80 deletions

View File

@ -243,6 +243,7 @@ class Handler:
session.save()
self._authToken = session.session_key
self._session = session
return self._authToken
def cleanAuthToken(self) -> None:

View File

@ -39,7 +39,7 @@ from uds.REST import RequestError
from uds.core.managers import userServiceManager
from uds.core.managers import cryptoManager
from uds.core.services.exceptions import ServiceNotReadyError
from uds.web.util import errors
from uds.web.util import errors, services
logger = logging.getLogger(__name__)
@ -50,17 +50,18 @@ class Connection(Handler):
"""
Processes actor requests
"""
authenticated = True # Actor requests are not authenticated
authenticated = True # Connection requests are authenticated
needs_admin = False
needs_staff = False
@staticmethod
def result(
result: typing.Any = None,
error: typing.Optional[typing.Union[str, int]] = None,
errorCode: int = 0,
retryable: bool = False
) -> typing.Dict[str, typing.Any]:
result: typing.Any = None,
error: typing.Optional[typing.Union[str, int]] = None,
errorCode: int = 0,
retryable: bool = False,
) -> typing.Dict[str, typing.Any]:
"""
Helper method to create a "result" set for connection response
:param result: Result value to return (can be None, in which case it is converted to empty string '')
@ -83,33 +84,44 @@ class Connection(Handler):
def serviceList(self):
# We look for services for this authenticator groups. User is logged in in just 1 authenticator, so his groups must coincide with those assigned to ds
from uds.web.util.services import getServicesData
# Ensure user is present on request, used by web views methods
self._request.user = self._user
self._request.user = self._user # type: ignore
return Connection.result(result=getServicesData(self._request))
return Connection.result(result=services.getServicesData(self._request))
def connection(self, doNotCheck: bool = False):
idService = self._args[0]
idTransport = self._args[1]
try:
ip, userService, iads, trans, itrans = userServiceManager().getService( # pylint: disable=unused-variable
self._user, self._request.os, self._request.ip, idService, idTransport, not doNotCheck
(
ip,
userService,
iads,
trans,
itrans,
) = userServiceManager().getService( # pylint: disable=unused-variable
self._user,
self._request.os, # type: ignore
self._request.ip, # type: ignore
idService,
idTransport,
not doNotCheck,
)
ci = {
'username': '',
'password': '',
'domain': '',
'protocol': 'unknown',
'ip': ip
'ip': ip,
}
if itrans: # only will be available id doNotCheck is False
ci.update(itrans.getConnectionInfo(userService, self._user, 'UNKNOWN'))
return Connection.result(result=ci)
except ServiceNotReadyError as e:
# Refresh ticket and make this retrayable
return Connection.result(error=errors.SERVICE_IN_PREPARATION, errorCode=e.code, retryable=True)
return Connection.result(
error=errors.SERVICE_IN_PREPARATION, errorCode=e.code, retryable=True
)
except Exception as e:
logger.exception("Exception")
return Connection.result(error=str(e))
@ -122,23 +134,59 @@ class Connection(Handler):
hostname = self._args[3]
try:
res = userServiceManager().getService(self._user, self._request.os, self._request.ip, idService, idTransport)
res = userServiceManager().getService(
self._user, self._request.os, self._request.ip, idService, idTransport # type: ignore
)
logger.debug('Res: %s', res)
ip, userService, userServiceInstance, transport, transportInstance = res # pylint: disable=unused-variable
(
ip,
userService,
userServiceInstance,
transport,
transportInstance,
) = res # pylint: disable=unused-variable
password = cryptoManager().symDecrpyt(self.getValue('password'), scrambler)
userService.setConnectionSource(self._request.ip, hostname) # Store where we are accessing from so we can notify Service
userService.setConnectionSource(
self._request.ip, hostname # type: ignore
) # Store where we are accessing from so we can notify Service
transportScript = transportInstance.getEncodedTransportScript(userService, transport, ip, self._request.os, self._user, password, self._request)
if not ip:
raise ServiceNotReadyError()
transportScript = transportInstance.getEncodedTransportScript(
userService,
transport,
ip,
self._request.os, # type: ignore
self._user,
password,
self._request,
)
return Connection.result(result=transportScript)
except ServiceNotReadyError as e:
# Refresh ticket and make this retrayable
return Connection.result(error=errors.SERVICE_IN_PREPARATION, errorCode=e.code, retryable=True)
return Connection.result(
error=errors.SERVICE_IN_PREPARATION, errorCode=e.code, retryable=True
)
except Exception as e:
logger.exception("Exception")
return Connection.result(error=str(e))
def getTicketContent(self):
return {} # TODO: use this for something?
def getUdsLink(self):
# Returns the UDS link for the user & transport
self._request.user = self._user # type: ignore
self._request._cryptedpass = self._session['REST']['password'] # type: ignore
self._request._scrambler = self._request.META['HTTP_SCRAMBLER'] # type: ignore
linkInfo = services.enableService(self._request, idService=self._args[0], idTransport=self._args[1])
if linkInfo['error']:
return Connection.result(error=linkInfo['error'])
return Connection.result(result=linkInfo['url'])
def get(self):
"""
Processes get requests
@ -157,9 +205,12 @@ class Connection(Handler):
return self.connection()
if len(self._args) == 3:
# /connection/idService/idTransport/skipChecking
if self._args[2] == 'skipChecking':
# /connection/idService/idTransport/skipcheck
if self._args[2] == 'skipcheck':
return self.connection(True)
# /connection/idService/idTransport/udslink
elif self._args[2] == 'udslink':
return self.getUdsLink()
if len(self._args) == 4:
# /connection/idService/idTransport/scrambler/hostname

View File

@ -311,7 +311,11 @@ def webPassword(request: HttpRequest) -> str:
session (db) and client browser cookies. This method uses this two values to recompose the user password
so we can provide it to remote sessions.
"""
return cryptoManager().symDecrpyt(request.session.get(PASS_KEY, ''), getUDSCookie(request)) # recover as original unicode string
if hasattr(request, 'session'):
return cryptoManager().symDecrpyt(request.session.get(PASS_KEY, ''), getUDSCookie(request)) # recover as original unicode string
else: # No session, get from _session instead, this is an "client" REST request
return cryptoManager().symDecrpyt(request._cryptedpass, request._scrambler) # type: ignore
def webLogout(request: HttpRequest, exit_url: typing.Optional[str] = None) -> HttpResponse:

View File

@ -28,6 +28,7 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
import json
import logging
import typing
@ -35,15 +36,19 @@ from django.utils.translation import ugettext
from django.utils import formats
from django.urls import reverse
from uds.models import ServicePool, Transport, Network, ServicePoolGroup, MetaPool, getSqlDatetime
from uds.models import ServicePool, Transport, Network, ServicePoolGroup, MetaPool, getSqlDatetime, TicketStore
from uds.core.util.config import GlobalConfig
from uds.core.services.exceptions import ServiceNotReadyError, MaxServicesReachedError, ServiceAccessDeniedByCalendar
from uds.core.auths.auth import webPassword
from uds.web.util import errors
from uds.core.util import html
from uds.core.managers import userServiceManager
from uds.core.managers import userServiceManager, cryptoManager
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
from django.http import HttpRequest # pylint: disable=ungrouped-imports
from django.http import HttpRequest, HttpResponse
logger = logging.getLogger(__name__)
@ -242,3 +247,54 @@ def getServicesData(request: 'HttpRequest') -> typing.Dict[str, typing.Any]: #
'transports': validTrans,
'autorun': autorun
}
def enableService(request: 'HttpRequest', idService: str, idTransport: str) -> typing.Mapping[str, typing.Any]:
# Maybe we could even protect this even more by limiting referer to own server /? (just a meditation..)
logger.debug('idService: %s, idTransport: %s', idService, idTransport)
url = ''
error = ugettext('Service not ready. Please, try again in a while.')
# If meta service, process and rebuild idService & idTransport
try:
res = userServiceManager().getService(request.user, request.os, request.ip, idService, idTransport, doTest=False)
scrambler = cryptoManager().randomString(32)
password = cryptoManager().symCrypt(webPassword(request), scrambler)
userService, trans = res[1], res[3]
typeTrans = trans.getType()
error = '' # No error
if typeTrans.ownLink:
url = reverse('TransportOwnLink', args=('A' + userService.uuid, trans.uuid))
else:
data = {
'service': 'A' + userService.uuid,
'transport': trans.uuid,
'user': request.user.uuid,
'password': password
}
ticket = TicketStore.create(data)
url = html.udsLink(request, ticket, scrambler)
except ServiceNotReadyError as e:
logger.debug('Service not ready')
# Not ready, show message and return to this page in a while
# error += ' (code {0:04X})'.format(e.code)
error = ugettext('Your service is being created, please, wait for a few seconds while we complete it.)') + '({}%)'.format(int(e.code * 25))
except MaxServicesReachedError:
logger.info('Number of service reached MAX for service pool "%s"', idService)
error = errors.errorString(errors.MAX_SERVICES_REACHED)
except ServiceAccessDeniedByCalendar:
logger.info('Access tried to a calendar limited access pool "%s"', idService)
error = errors.errorString(errors.SERVICE_CALENDAR_DENIED)
except Exception as e:
logger.exception('Error')
error = str(e)
return {
'url': str(url),
'error': str(error)
}

View File

@ -33,18 +33,16 @@ import logging
import typing
from django.utils.translation import ugettext as _
from django.urls import reverse
from django.http import HttpResponse
from django.views.decorators.cache import cache_page, never_cache
from uds.core.auths.auth import webLoginRequired, webPassword
from uds.core.managers import userServiceManager, cryptoManager
from uds.models import TicketStore
from uds.core.managers import userServiceManager
from uds.core.ui.images import DEFAULT_IMAGE
from uds.core.util.model import processUuid
from uds.models import Transport, Image
from uds.core.util import html, log
from uds.core.services.exceptions import ServiceNotReadyError, MaxServicesReachedError, ServiceAccessDeniedByCalendar
from uds.core.util import log
from uds.core.services.exceptions import ServiceNotReadyError
from uds.web.util import errors
from uds.web.util import services
@ -113,58 +111,11 @@ def serviceImage(request: 'HttpRequest', idImage: str) -> HttpResponse:
@webLoginRequired(admin=False)
@never_cache
def userServiceEnabler(request: 'HttpRequest', idService: str, idTransport: str) -> HttpResponse:
# Maybe we could even protect this even more by limiting referer to own server /? (just a meditation..)
logger.debug('idService: %s, idTransport: %s', idService, idTransport)
url = ''
error = _('Service not ready. Please, try again in a while.')
# If meta service, process and rebuild idService & idTransport
try:
res = userServiceManager().getService(request.user, request.os, request.ip, idService, idTransport, doTest=False)
scrambler = cryptoManager().randomString(32)
password = cryptoManager().symCrypt(webPassword(request), scrambler)
userService, trans = res[1], res[3]
typeTrans = trans.getType()
error = '' # No error
if typeTrans.ownLink:
url = reverse('TransportOwnLink', args=('A' + userService.uuid, trans.uuid))
else:
data = {
'service': 'A' + userService.uuid,
'transport': trans.uuid,
'user': request.user.uuid,
'password': password
}
ticket = TicketStore.create(data)
url = html.udsLink(request, ticket, scrambler)
except ServiceNotReadyError as e:
logger.debug('Service not ready')
# Not ready, show message and return to this page in a while
# error += ' (code {0:04X})'.format(e.code)
error = _('Your service is being created, please, wait for a few seconds while we complete it.)') + '({}%)'.format(int(e.code * 25))
except MaxServicesReachedError:
logger.info('Number of service reached MAX for service pool "%s"', idService)
error = errors.errorString(errors.MAX_SERVICES_REACHED)
except ServiceAccessDeniedByCalendar:
logger.info('Access tried to a calendar limited access pool "%s"', idService)
error = errors.errorString(errors.SERVICE_CALENDAR_DENIED)
except Exception as e:
logger.exception('Error')
error = str(e)
return HttpResponse(
json.dumps({
'url': str(url),
'error': str(error)
}),
json.dumps(services.enableService(request, idService=idService, idTransport=idTransport)),
content_type='application/json'
)
def closer(request: 'HttpRequest') -> HttpResponse:
return HttpResponse('<html><body onload="window.close()"></body></html>')