Example #1
0
def clientEnabler(request, idService, idTransport):

    # Maybe we could even protect this even more by limiting referer to own server /? (just a meditation..)

    url = ''
    error = _('Service not ready. Please, try again in a while.')
    try:
        res = getService(request, idService, idTransport, doTest=False)
        if res is not None:

            scrambler = cryptoManager().randomString(32)
            password = cryptoManager().xor(webPassword(request), scrambler)

            _x, ads, _x, trans, _x = res

            data = {
                'service': 'A' + ads.uuid,
                'transport': trans.uuid,
                'user': request.user.uuid,
                'password': password
            }

            ticket = TicketStore.create(data)
            error = ''
            url = html.udsLink(request, ticket, scrambler)
    except Exception as e:
        error = six.text_type(e)

    # Not ready, show message and return to this page in a while
    return HttpResponse('{{ "url": "{}", "error": "{}" }}'.format(url, error), content_type='application/json')
Example #2
0
 def __init__(self, key=None, data=None):
     self.uuidGenerator = lambda: (cryptoManager().uuid() + cryptoManager().uuid()).replace('-', '')
     self.cache = Cache(TICKET_OWNER)
     self.data = data
     self.key = key
     if key is not None:
         self.load()
     else:
         self.key = self.uuidGenerator()
Example #3
0
def scrambleId(request, id_):
    if SCRAMBLE_SES not in request.session:
        request.session[SCRAMBLE_SES] = cryptoManager().randomString(SCRAMBLE_LEN)

    id_ = str(id_)
    if len(id_) < SCRAMBLE_LEN:
        id_ = (id_ + '~' + cryptoManager().randomString(SCRAMBLE_LEN))[:SCRAMBLE_LEN]

    scrambled = cryptoManager().xor(id_, request.session[SCRAMBLE_SES])
    return encoders.encode(scrambled, 'base64', asText=True)[:-3]
Example #4
0
    def getLink(self, userService, transport, ip, os, user, password, request):
        ci = self.processUserPassword(userService, user, password)
        username, password, domain = ci['username'], ci['password'], ci['domain']

        if domain != '':
            username = domain + '\\' + username

        scrambler = cryptoManager().randomString(32)
        passwordCrypted = cryptoManager().symCrypt(password, scrambler)

        # Build params dict
        params = {
            'protocol': 'rdp',
            'hostname': ip,
            'username': username,
            'password': passwordCrypted,
            'ignore-cert': 'true',
            'security': self.security.value,
            'drive-path': '/share/{}'.format(user.uuid),
            'create-drive-path': 'true'
        }

        if self.enableFileSharing.isTrue():
            params['enable-drive'] = 'true'

        if self.serverLayout.value != '-':
            params['server-layout'] = self.serverLayout.value

        if self.enableAudio.isTrue() is False:
            params['disable-audio'] = 'true'

        if self.enablePrinting.isTrue() is True:
            params['enable-printing'] = 'true'

        if self.wallpaper.isTrue() is True:
            params['enable-wallpaper'] = 'true'

        if self.desktopComp.isTrue() is True:
            params['enable-desktop-composition'] = 'true'

        if self.smooth.isTrue() is True:
            params['enable-font-smoothing'] = 'true'

        logger.debug('RDP Params: {0}'.format(params))

        ticket = TicketStore.create(params, validity=self.ticketValidity.num())

        return HttpResponseRedirect("{}/transport/?{}.{}&{}".format(self.guacamoleServer.value, ticket, scrambler, request.build_absolute_uri(reverse('utility.closer'))))
Example #5
0
    def get(uuid, invalidate=True, owner=None, secure=False):
        try:
            t = TicketStore.objects.get(uuid=uuid, owner=owner)
            validity = datetime.timedelta(seconds=t.validity)
            now = getSqlDatetime()

            logger.debug('Ticket validity: {} {}'.format(t.stamp + validity, now))
            if t.stamp + validity < now:
                raise TicketStore.InvalidTicket('Not valid anymore')

            if secure is True:
                data = pickle.loads(cryptoManager().decrypt(t.data))
            else:
                data = pickle.loads(t.data)

            # If has validator, execute it
            if t.validator is not None:
                validator = pickle.loads(t.validator)

                if validator(data) is False:
                    raise TicketStore.InvalidTicket('Validation failed')

            if invalidate is True:
                t.stamp = now - validity - datetime.timedelta(seconds=1)
                t.save()

            return data
        except TicketStore.DoesNotExist:
            raise TicketStore.InvalidTicket('Does not exists')
Example #6
0
    def script(self):
        idService = self._args[0]
        idTransport = self._args[1]
        scrambler = self._args[2]
        hostname = self._args[3]

        try:
            res = userServiceManager().getService(self._user, self._request.os, self._request.ip, idService, idTransport)
            logger.debug('Res: {}'.format(res))
            ip, userService, userServiceInstance, transport, transportInstance = res
            password = cryptoManager().symDecrpyt(self.getValue('password'), scrambler)

            userService.setConnectionSource(self._request.ip, hostname)  # 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)

            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)
        except Exception as e:
            logger.exception("Exception")
            return Connection.result(error=six.text_type(e))

        return password
Example #7
0
def userServiceEnabler(request, idService, idTransport):
    # Maybe we could even protect this even more by limiting referer to own server /? (just a meditation..)
    logger.debug('idService: {}, idTransport: {}'.format(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)

        _x, userService, _x, trans, _x = res

        data = {
            'service': 'A' + userService.uuid,
            'transport': trans.uuid,
            'user': request.user.uuid,
            'password': password
        }

        ticket = TicketStore.create(data)
        error = ''
        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)
    except MaxServicesReachedError:
        logger.info('Number of service reached MAX for service pool "{}"'.format(idService))
        error = errors.errorString(errors.MAX_SERVICES_REACHED)
    except ServiceAccessDeniedByCalendar:
        logger.info('Access tried to a calendar limited access pool "{}"'.format(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)
        }),
        content_type='application/json'
    )
Example #8
0
 def registerDownloadable(self, name, comment, path, mime='application/octet-stream'):
     """
     Registers a downloadable file.
     @param name: name shown
     @param path: path to file
     @params zip: If download as zip
     """
     _id = cryptoManager().uuid(name)
     self._downloadables[_id] = {'name': name, 'comment': comment, 'path': path, 'mime': mime}
Example #9
0
def webPassword(request):
    """
    The password is stored at session using a simple scramble algorithm that keeps the password splited at
    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.
    @param request: DJango Request
    @return: Unscrambled user password
    """
    return cryptoManager().symDecrpyt(request.session.get(PASS_KEY, ''), getUDSCookie(request))  # recover as original unicode string
Example #10
0
    def create(data, validator=None, validity=DEFAULT_VALIDITY, owner=None, secure=False):
        '''
        validity is in seconds
        '''
        if validator is not None:
            validator = pickle.dumps(validator)
        data = pickle.dumps(data)
        if secure:
            data = cryptoManager().encrypt(data)

        return TicketStore.objects.create(stamp=getSqlDatetime(), data=data, validator=validator, validity=validity, owner=owner).uuid
Example #11
0
def clientEnabler(request, idService, idTransport):

    # Maybe we could even protect this even more by limiting referer to own server /? (just a meditation..)
    logger.debug('idService: {}, idTransport: {}'.format(idService, idTransport))
    url = ''
    error = _('Service not ready. Please, try again in a while.')
    try:
        res = userServiceManager().getService(request.user, request.ip, idService, idTransport, doTest=False)
        scrambler = cryptoManager().randomString(32)
        password = cryptoManager().xor(webPassword(request), scrambler)

        _x, userService, _x, trans, _x = res

        data = {
            'service': 'A' + userService.uuid,
            'transport': trans.uuid,
            'user': request.user.uuid,
            'password': password
        }

        ticket = TicketStore.create(data)
        error = ''
        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)
    except MaxServicesReachedError:
        logger.info('Number of service reached MAX for service pool "{}"'.format(idService))
        error = errors.errorString(errors.MAX_SERVICES_REACHED)
    except Exception as e:
        logger.exception('Error')
        error = six.text_type(e)

    return HttpResponse(
        '{{ "url": "{}", "error": "{}" }}'.format(url, error),
        content_type='application/json'
    )
Example #12
0
def guacamole(request, tunnelId):
    logger.debug('Received credentials request for tunnel id {0}'.format(tunnelId))

    tunnelId, scrambler = tunnelId.split('.')

    try:
        val = TicketStore.get(tunnelId, invalidate=False)
        val['password'] = cryptoManager().symDecrpyt(val['password'], scrambler)

        response = dict2resp(val)
    except Exception:
        logger.error('Invalid guacamole ticket (F5 on client?): {}'.format(tunnelId))
        return HttpResponse(ERROR, content_type=CONTENT_TYPE)

    return HttpResponse(response, content_type=CONTENT_TYPE)
Example #13
0
def getUDSCookie(request, response=None, force=False):
    '''
    Generates a random cookie for uds, used, for example, to encript things
    '''
    if 'uds' not in request.COOKIES:
        cookie = cryptoManager().randomString(48)
        if response is not None:
            response.set_cookie('uds', cookie)
        request.COOKIES['uds'] = cookie
    else:
        cookie = request.COOKIES['uds']

    if response is not None and force is True:
        response.set_cookie('uds', cookie)

    return cookie
Example #14
0
    def saveItem(self, parent, item):
        logger.debug('Saving user {0} / {1}'.format(parent, item))
        valid_fields = ['name', 'real_name', 'comments', 'state', 'staff_member', 'is_admin']
        if 'password' in self._params:
            valid_fields.append('password')
            self._params['password'] = cryptoManager().hash(self._params['password'])

        fields = self.readFieldsFromParams(valid_fields)
        user = None
        try:
            auth = parent.getInstance()
            if item is None:  # Create new
                auth.createUser(fields)  # this throws an exception if there is an error (for example, this auth can't create users)
                toSave = {}
                for k in valid_fields:
                    toSave[k] = fields[k]
                user = parent.users.create(**toSave)
            else:
                auth.modifyUser(fields)  # Notifies authenticator
                toSave = {}
                for k in valid_fields:
                    toSave[k] = fields[k]
                user = parent.users.get(uuid=processUuid(item))
                user.__dict__.update(toSave)

            logger.debug('User parent: {}'.format(user.parent))
            if auth.isExternalSource is False and (user.parent is None or user.parent == ''):
                groups = self.readFieldsFromParams(['groups'])['groups']
                logger.debug('Groups: {}'.format(groups))
                logger.debug('Got Groups {}'.format(parent.groups.filter(uuid__in=groups)))
                user.groups = parent.groups.filter(uuid__in=groups)

            user.save()

        except User.DoesNotExist:
            self.invalidItemException()
        except IntegrityError:  # Duplicate key probably
            raise RequestError(_('User already exists (duplicate key error)'))
        except AuthenticatorException as e:
            raise RequestError(six.text_type(e))
        except ValidationError as e:
            raise RequestError(six.text_type(e.message))
        except Exception:
            logger.exception('Saving user')
            self.invalidRequestException()

        return self.getItems(parent, user.uuid)
Example #15
0
    def get(self):
        '''
        Processes get requests
        '''
        logger.debug("Client args for GET: {0}".format(self._args))

        if len(self._args) == 0:
            url = self._request.build_absolute_uri(reverse('ClientDownload'))
            return Client.result({
                'availableVersion': CLIENT_VERSION,
                'requiredVersion': REQUIRED_CLIENT_VERSION,
                'downloadUrl': url
            })

        if len(self._args) == 1:
            return Client.result(_('Correct'))

        try:
            ticket, scrambler = self._args
        except Exception:
            raise RequestError('Invalid request')

        try:
            data = TicketStore.get(ticket)
        except Exception:
            return Client.result(error=errors.ACCESS_DENIED)

        self._request.user = User.objects.get(uuid=data['user'])

        try:
            logger.debug(data)
            res = getService(self._request, data['service'], data['transport'])
            logger.debug('Res: {}'.format(res))
            if res is not None:
                ip, userService, userServiceInstance, transport, transportInstance = res
                password = cryptoManager().xor(data['password'], scrambler).decode('utf-8')

                transportScript = transportInstance.getUDSTransportScript(userService, transport, ip, self._request.os, self._request.user, password, self._request)

                logger.debug('Script:\n{}'.format(transportScript))

                return Client.result(result=transportScript.encode('bz2').encode('base64'))
        except Exception as e:
            logger.exception("Exception")
            return Client.result(error=six.text_type(e))

        return Client.result(error=errors.SERVICE_NOT_READY)
Example #16
0
    def unserializeForm(self, values: bytes):
        """
        This method unserializes the values previously obtained using
        :py:meth:`serializeForm`, and stores
        the valid values form form fileds inside its corresponding field
        """
        if not values:  # Has nothing
            return

        try:
            # Set all values to defaults ones
            for k in self._gui:
                if self._gui[k].isType(
                        gui.InputField.HIDDEN_TYPE
                ) and self._gui[k].isSerializable() is False:
                    # logger.debug('Field {0} is not unserializable'.format(k))
                    continue
                self._gui[k].value = self._gui[k].defValue

            values = typing.cast(bytes, encoders.decode(values, 'zip'))
            if not values:  # Has nothing
                return

            for txt in values.split(b'\002'):
                kb, v = txt.split(b'\003')
                k = kb.decode('utf8')  # Convert name to unicode
                if k in self._gui:
                    try:
                        if v and v[0] == 1:
                            val = pickle.loads(v[1:])
                        elif v and v[0] == 4:
                            val = cryptoManager().AESDecrypt(
                                v[1:], UDSB, True).decode()
                        else:
                            val = v
                            # Ensure "legacy bytes" values are loaded correctly as unicode
                            if isinstance(val, bytes):
                                val = val.decode('utf_8')
                    except Exception:
                        logger.exception('Pickling {} from {}'.format(k, self))
                        val = ''
                    self._gui[k].value = val
                # logger.debug('Value for {0}:{1}'.format(k, val))
        except Exception:
            logger.exception('Exception on unserialization on %s',
                             self.__class__)
Example #17
0
    def get(
        uuid: str,
        invalidate: bool = True,
        owner: typing.Optional[str] = None,
        secure: bool = False,
    ) -> typing.Any:
        try:
            dbOwner = owner
            if secure:
                if not owner:
                    raise ValueError(
                        'Tried to use a secure ticket without owner')
                dbOwner = SECURED

            t = TicketStore.objects.get(uuid=uuid, owner=dbOwner)
            validity = datetime.timedelta(seconds=t.validity)
            now = getSqlDatetime()

            logger.debug('Ticket validity: %s %s', t.stamp + validity, now)
            if t.stamp + validity < now:
                raise TicketStore.InvalidTicket('Not valid anymore')

            data: bytes = t.data

            if secure:  # Owner has already been tested and it's not emtpy
                data = cryptoManager().AESDecrypt(
                    data,
                    typing.cast(str, owner).encode())

            data = pickle.loads(data)

            # If has validator, execute it
            if t.validator:
                validator: ValidatorType = pickle.loads(t.validator)

                if validator(data) is False:
                    raise TicketStore.InvalidTicket('Validation failed')

            if invalidate is True:
                t.stamp = now - validity - datetime.timedelta(seconds=1)
                t.save(update_fields=['stamp'])

            return data
        except TicketStore.DoesNotExist:
            raise TicketStore.InvalidTicket('Does not exists')
Example #18
0
def getUDSCookie(request: HttpRequest,
                 response: typing.Optional[HttpResponse] = None,
                 force: bool = False) -> str:
    '''
    Generates a random cookie for uds, used, for example, to encript things
    '''
    if 'uds' not in request.COOKIES:
        cookie = cryptoManager().randomString(48)
        if response is not None:
            response.set_cookie('uds', cookie)
        request.COOKIES['uds'] = cookie
    else:
        cookie = request.COOKIES['uds']

    if response and force:
        response.set_cookie('uds', cookie)

    return cookie
Example #19
0
 def registerDownloadable(self,
                          name,
                          comment,
                          path,
                          mime='application/octet-stream'):
     """
     Registers a downloadable file.
     @param name: name shown
     @param path: path to file
     @params zip: If download as zip
     """
     _id = cryptoManager().uuid(name)
     self._downloadables[_id] = {
         'name': name,
         'comment': comment,
         'path': path,
         'mime': mime
     }
Example #20
0
def guacamole(request, tunnelId):
    logger.debug(
        'Received credentials request for tunnel id {0}'.format(tunnelId))

    tunnelId, scrambler = tunnelId.split('.')

    try:
        val = TicketStore.get(tunnelId, invalidate=False)
        val['password'] = cryptoManager().symDecrpyt(val['password'],
                                                     scrambler)

        response = dict2resp(val)
    except Exception:
        logger.error(
            'Invalid guacamole ticket (F5 on client?): {}'.format(tunnelId))
        return HttpResponse(ERROR, content_type=CONTENT_TYPE)

    return HttpResponse(response, content_type=CONTENT_TYPE)
Example #21
0
    def getLink(  # pylint: disable=too-many-locals
            self, userService: 'models.UserService',
            transport: 'models.Transport', ip: str, os: typing.Dict[str, str],
            user: '******', password: str, request: 'HttpRequest') -> str:
        # Build params dict
        params = {
            'protocol': 'vnc',
            'hostname': ip,
            'port': str(self.vncPort.num()),
        }

        if self.username.value.strip():
            params['username'] = self.username.value.strip()

        if self.password.value.strip():
            params['password'] = self.password.value.strip()

        if self.colorDepth.value != '-':
            params['color-depth'] = self.colorDepth.value

        if self.swapRedBlue.isTrue():
            params['swap-red-blue'] = 'true'

        if self.cursor.isTrue():
            params['cursor'] = 'remote'

        if self.readOnly.isTrue():
            params['read-only'] = 'true'

        logger.debug('VNC Params: %s', params)

        scrambler = cryptoManager().randomString(32)
        ticket = models.TicketStore.create(params,
                                           validity=self.ticketValidity.num())

        onw = ''
        if self.forceNewWindow.value == gui.TRUE:
            onw = 'o_n_w={}'
        elif self.forceNewWindow.value == 'overwrite':
            onw = 'o_s_w=yes'
        onw = onw.format(hash(transport.name))

        return str("{}/guacamole/#/?data={}.{}{}".format(
            self.guacamoleServer.value, ticket, scrambler, onw))
Example #22
0
        def set(self, value: str):
            if GlobalConfig.isInitialized() is False:
                _saveLater.append((self, value))
                return

            if self._crypt is True:
                value = cryptoManager().encrypt(value)

            # Editable here means that this configuration value can be edited by admin directly (generally, that this is a "clean text" value)

            logger.debug('Saving config %s.%s as %s', self._section.name(), self._key, value)
            try:
                obj, _ = DBConfig.objects.get_or_create(section=self._section.name(), key=self._key)  # @UndefinedVariable
                obj.value, obj.crypt, obj.long, obj.field_type = value, self._crypt, self._longText, self._type
                obj.save()
            except Exception:
                logger.exception('Exception')
                # Probably a migration issue, just ignore it
                logger.info("Could not save configuration key %s.%s", self._section.name(), self._key)
Example #23
0
    def create(data,
               validator=None,
               validity=DEFAULT_VALIDITY,
               owner=None,
               secure=False):
        '''
        validity is in seconds
        '''
        if validator is not None:
            validator = pickle.dumps(validator)
        data = pickle.dumps(data)
        if secure:
            data = cryptoManager().encrypt(data)

        return TicketStore.objects.create(stamp=getSqlDatetime(),
                                          data=data,
                                          validator=validator,
                                          validity=validity,
                                          owner=owner).uuid
Example #24
0
    def authenticate(self, username: str, credentials: str,
                     groupsManager: 'auths.GroupsManager') -> bool:
        logger.debug('Username: %s, Password: %s', username, credentials)
        dbAuth = self.dbAuthenticator()
        try:
            user: '******' = dbAuth.users.get(name=username,
                                                   state=State.ACTIVE)
        except Exception:
            return False

        if user.parent:  # Direct auth not allowed for "derived" users
            return False

        # Internal Db Auth has its own groups, and if it active it is valid
        if user.password == cryptoManager().hash(credentials):
            #  hashlib.sha1(credentials.encode('utf-8')).hexdigest():
            groupsManager.validate([g.name for g in user.groups.all()])
            return True
        return False
Example #25
0
 def marshal(self) -> bytes:
     """
     Serializes the os manager data so we can store it in database
     """
     base = codecs.encode(super().marshal(), 'hex').decode()
     return '\t'.join(
         [
             'v4',
             self._domain,
             self._ou,
             self._account,
             cryptoManager().encrypt(self._password),
             base,
             self._group,
             self._serverHint,
             self._ssl,
             self._removeOnExit,
         ]
     ).encode('utf8')
Example #26
0
        def __init__(self,
                     section: 'Config.Section',
                     key: str,
                     default: str = '',
                     crypt: bool = False,
                     longText: bool = False,
                     **kwargs):
            logger.debug('Var: %s %s KWARGS: %s', section, key, kwargs)
            self._type: int = kwargs.get('type', -1)

            self._section: 'Config.Section' = section
            self._key: str = key
            self._crypt: bool = crypt
            self._longText: bool = longText
            if crypt is False or not default:
                self._default: str = default
            else:
                self._default = cryptoManager().encrypt(default)
            self._data: typing.Optional[str] = None
Example #27
0
    def authenticate(self, username, credentials, groupsManager):
        logger.debug('Username: {0}, Password: {1}'.format(username, credentials))
        auth = self.dbAuthenticator()
        try:
            try:
                usr = auth.users.get(name=username, state=State.ACTIVE)
            except Exception:
                return False

            if usr.parent is not None and usr.parent != '':  # Direct auth not allowed for "derived" users
                return False

            # Internal Db Auth has its own groups, and if it active it is valid
            if usr.password == cryptoManager().hash(credentials):
                #  hashlib.sha1(credentials.encode('utf-8')).hexdigest():
                groupsManager.validate([g.name for g in usr.groups.all()])
                return True
            return False
        except dbAuthenticator.DoesNotExist:  # @UndefinedVariable
            return False
Example #28
0
    def authenticate(self, username, credentials, groupsManager):
        logger.debug('Username: {0}, Password: {1}'.format(username, credentials))
        auth = self.dbAuthenticator()
        try:
            try:
                usr = auth.users.get(name=username, state=State.ACTIVE)
            except Exception:
                return False

            if usr.parent is not None and usr.parent != '':  # Direct auth not allowed for "derived" users
                return False

            # Internal Db Auth has its own groups, and if it active it is valid
            if usr.password == cryptoManager().hash(credentials):
                #  hashlib.sha1(credentials.encode('utf-8')).hexdigest():
                groupsManager.validate([g.name for g in usr.groups.all()])
                return True
            return False
        except dbAuthenticator.DoesNotExist:  # @UndefinedVariable
            return False
Example #29
0
    def store(uuid, data, validator=None, validity=DEFAULT_VALIDITY, owner=owner, secure=False):
        '''
        Stores an ticketstore. If one with this uuid already exists, replaces it. Else, creates a new one
        validity is in seconds
        '''
        if validator is not None:
            validator = pickle.dumps(validator)

        data = pickle.dumps(data)
        if secure:
            data = cryptoManager().encrypt()

        try:
            t = TicketStore.objects.get(uuid=uuid, owner=owner)
            t.data = data
            t.stamp = getSqlDatetime()
            t.validity = validity
            t.save()
        except TicketStore.DoesNotExist:
            t = TicketStore.objects.create(uuid=uuid, stamp=getSqlDatetime(), data=data, validator=validator, validity=validity, owner=owner)
Example #30
0
    def storeSessionAuthdata(session, id_auth, username, password, locale, is_admin, staff_member, scrambler):
        '''
        Stores the authentication data inside current session
        :param session: session handler (Djano user session object)
        :param id_auth: Authenticator id (DB object id)
        :param username: Name of user (login name)
        :param locale: Assigned locale
        :param is_admin: If user is considered admin or not
        :param staff_member: If is considered as staff member
        '''
        if is_admin:
            staff_member = True  # Make admins also staff members :-)

        session['REST'] = {
            'auth': id_auth,
            'username': username,
            'password': cryptoManager().xor(password, scrambler),  # Stores "bytes"
            'locale': locale,
            'is_admin': is_admin,
            'staff_member': staff_member
        }
Example #31
0
def webLogin(request: HttpRequest, response: HttpResponse, user: User, password: str) -> bool:
    """
    Helper function to, once the user is authenticated, store the information at the user session.
    @return: Always returns True
    """
    from uds import REST

    if user.id != ROOT_ID:  # If not ROOT user (this user is not inside any authenticator)
        manager_id = user.manager.id
    else:
        manager_id = -1

    # If for any reason the "uds" cookie is removed, recreated it
    cookie = getUDSCookie(request, response)

    user.updateLastAccess()
    request.session.clear()
    request.session[USER_KEY] = user.id
    request.session[PASS_KEY] = cryptoManager().symCrypt(password, cookie)  # Stores "bytes"
    # Ensures that this user will have access through REST api if logged in through web interface
    REST.Handler.storeSessionAuthdata(request.session, manager_id, user.name, password, get_language(), request.os, user.is_admin, user.staff_member, cookie)
    return True
Example #32
0
    def storeSessionAuthdata(session, id_auth, username, password, locale, platform, is_admin, staff_member, scrambler):
        """
        Stores the authentication data inside current session
        :param session: session handler (Djano user session object)
        :param id_auth: Authenticator id (DB object id)
        :param username: Name of user (login name)
        :param locale: Assigned locale
        :param is_admin: If user is considered admin or not
        :param staff_member: If is considered as staff member
        """
        if is_admin:
            staff_member = True  # Make admins also staff members :-)

        session['REST'] = {
            'auth': id_auth,
            'username': username,
            'password': cryptoManager().symCrypt(password, scrambler),  # Stores "bytes"
            'locale': locale,
            'platform': platform,
            'is_admin': is_admin,
            'staff_member': staff_member
        }
Example #33
0
 def create_for_tunnel(
         userService: 'UserService',
         port: int,
         host: typing.Optional[str] = None,
         extra: typing.Optional[typing.Mapping[str, typing.Any]] = None,
         validity: int = 60 * 60 *
     24,  # 24 Hours default validity for tunnel tickets
 ) -> str:
     owner = cryptoManager().randomString(length=8)
     data = {
         'u': userService.user.uuid,
         's': userService.uuid,
         'h': host,
         'p': port,
         'e': extra,
     }
     return (TicketStore.create(
         data=data,
         validity=validity,
         owner=owner,
         secure=True,
     ) + owner)
Example #34
0
def webLogin(request, response, user, password):
    """
    Helper function to, once the user is authenticated, store the information at the user session.
    @return: Always returns True
    """
    from uds import REST

    if user.id != ROOT_ID:  # If not ROOT user (this user is not inside any authenticator)
        manager_id = user.manager.id
    else:
        manager_id = -1

    # If for any reason the "uds" cookie is removed, recreated it
    cookie = getUDSCookie(request, response)

    user.updateLastAccess()
    request.session.clear()
    request.session[USER_KEY] = user.id
    request.session[PASS_KEY] = cryptoManager().symCrypt(password, cookie)  # Stores "bytes"
    # Ensures that this user will have access through REST api if logged in through web interface
    REST.Handler.storeSessionAuthdata(request.session, manager_id, user.name, password, get_language(), request.os, user.is_admin, user.staff_member, cookie)
    return True
Example #35
0
    def script(self):
        # Could be one-liner, (... = ..[0:4]), but mypy complains so this is fine :)
        idService = self._args[0]
        idTransport = self._args[1]
        scrambler = self._args[2]
        hostname = self._args[3]

        try:
            res = userServiceManager().getService(self._user, self._request.os, self._request.ip, idService, idTransport)
            logger.debug('Res: %s', res)
            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

            transportScript = transportInstance.getEncodedTransportScript(userService, transport, ip, self._request.os, 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)
        except Exception as e:
            logger.exception("Exception")
            return Connection.result(error=str(e))
Example #36
0
def guacamole(request: HttpRequest, tunnelId: str) -> HttpResponse:
    logger.debug('Received credentials request for tunnel id %s', tunnelId)

    try:
        tunnelId, scrambler = tunnelId.split('.')

        val = TicketStore.get(tunnelId, invalidate=False)

        # Extra check that the ticket data belongs to original requested user service/user
        if 'ticket-info' in val:
            ti = val['ticket-info']
            del val['ticket-info']  # Do not send this data to guacamole!! :)

            try:
                userService = UserService.objects.get(uuid=ti['userService'])
            except Exception:
                logger.error(
                    'The requested guacamole userservice does not exists anymore'
                )
                raise
            if userService.user.uuid != ti['user']:
                logger.error(
                    'The requested userservice has changed owner and is not accesible'
                )
                raise Exception()

        if 'password' in val:
            val['password'] = cryptoManager().symDecrpyt(
                val['password'], scrambler)

        response = dict2resp(val)
    except Exception:
        # logger.error('Invalid guacamole ticket (F5 on client?): %s', tunnelId)
        return HttpResponse(ERROR, content_type=CONTENT_TYPE)

    return HttpResponse(response, content_type=CONTENT_TYPE)
Example #37
0
class GlobalConfig:
    """
    Simple helper to keep track of global configuration
    """
    SESSION_EXPIRE_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('sessionExpireTime', '24', type=Config.NUMERIC_FIELD)  # Max session duration (in use) after a new publishment has been made
    # Delay between cache checks. reducing this number will increase cache generation speed but also will load service providers
    CACHE_CHECK_DELAY: Config.Value = Config.section(GLOBAL_SECTION).value('cacheCheckDelay', '19', type=Config.NUMERIC_FIELD)
    # Delayed task number of threads PER SERVER, with higher number of threads, deplayed task will complete sooner, but it will give more load to overall system
    DELAYED_TASKS_THREADS: Config.Value = Config.section(GLOBAL_SECTION).value('delayedTasksThreads', '4', type=Config.NUMERIC_FIELD)
    # Number of scheduler threads running PER SERVER, with higher number of threads, deplayed task will complete sooner, but it will give more load to overall system
    SCHEDULER_THREADS: Config.Value = Config.section(GLOBAL_SECTION).value('schedulerThreads', '3', type=Config.NUMERIC_FIELD)
    # Waiting time before removing "errored" and "removed" publications, cache, and user assigned machines. Time is in seconds
    CLEANUP_CHECK: Config.Value = Config.section(GLOBAL_SECTION).value('cleanupCheck', '3607', type=Config.NUMERIC_FIELD)
    # Time to maintaing "info state" items before removing it, in seconds
    KEEP_INFO_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('keepInfoTime', '14401', type=Config.NUMERIC_FIELD)  # Defaults to 2 days 172800?? better 4 hours xd
    # Max number of services to be "preparing" at same time
    MAX_PREPARING_SERVICES: Config.Value = Config.section(GLOBAL_SECTION).value('maxPreparingServices', '15', type=Config.NUMERIC_FIELD)  # Defaults to 15 services at once (per service provider)
    # Max number of service to be at "removal" state at same time
    MAX_REMOVING_SERVICES: Config.Value = Config.section(GLOBAL_SECTION).value('maxRemovingServices', '15', type=Config.NUMERIC_FIELD)  # Defaults to 15 services at once (per service provider)
    # If we ignore limits (max....)
    IGNORE_LIMITS: Config.Value = Config.section(GLOBAL_SECTION).value('ignoreLimits', '0', type=Config.BOOLEAN_FIELD)
    # Number of services to initiate removal per run of CacheCleaner
    USER_SERVICE_CLEAN_NUMBER: Config.Value = Config.section(GLOBAL_SECTION).value('userServiceCleanNumber', '3', type=Config.NUMERIC_FIELD)  # Defaults to 3 per wun
    # Removal Check time for cache, publications and deployed services
    REMOVAL_CHECK: Config.Value = Config.section(GLOBAL_SECTION).value('removalCheck', '31', type=Config.NUMERIC_FIELD)  # Defaults to 30 seconds
    # Login URL
    LOGIN_URL: Config.Value = Config.section(GLOBAL_SECTION).value('loginUrl', '/login', type=Config.TEXT_FIELD)  # Defaults to /login
    # Session duration
    USER_SESSION_LENGTH: Config.Value = Config.section(SECURITY_SECTION).value('userSessionLength', '14400', type=Config.NUMERIC_FIELD)  # Defaults to 4 hours
    # Superuser (do not need to be at database!!!)
    SUPER_USER_LOGIN: Config.Value = Config.section(SECURITY_SECTION).value('superUser', 'root', type=Config.TEXT_FIELD)
    # Superuser password (do not need to be at database!!!)
    SUPER_USER_PASS: Config.Value = Config.section(SECURITY_SECTION).valueCrypt('rootPass', 'udsmam0', type=Config.TEXT_FIELD)
    # Idle time before closing session on admin
    SUPER_USER_ALLOW_WEBACCESS: Config.Value = Config.section(SECURITY_SECTION).value('allowRootWebAccess', '1', type=Config.BOOLEAN_FIELD)
    # Time an admi session can be idle before being "logged out"
    ADMIN_IDLE_TIME: Config.Value = Config.section(SECURITY_SECTION).value('adminIdleTime', '14400', type=Config.NUMERIC_FIELD)  # Defaults to 4 hous
    # Time betwen checks of unused services by os managers
    # Unused services will be invoked for every machine assigned but not in use AND that has been assigned at least this time
    # (only if os manager asks for this characteristic)
    CHECK_UNUSED_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('checkUnusedTime', '631', type=Config.NUMERIC_FIELD)  # Defaults to 10 minutes
    # Default CSS Used
    CSS: Config.Value = Config.section(GLOBAL_SECTION).value('css', settings.STATIC_URL + 'css/uds.css', type=Config.TEXT_FIELD)
    # Max logins before blocking an account
    MAX_LOGIN_TRIES: Config.Value = Config.section(GLOBAL_SECTION).value('maxLoginTries', '3', type=Config.NUMERIC_FIELD)
    # Block time in second for an user that makes too many mistakes, 5 minutes default
    LOGIN_BLOCK: Config.Value = Config.section(GLOBAL_SECTION).value('loginBlockTime', '300', type=Config.NUMERIC_FIELD)
    # Do autorun of service if just one service.
    # 0 = No autorun, 1 = Autorun at login
    # In a future, maybe necessary another value "2" that means that autorun always
    AUTORUN_SERVICE: Config.Value = Config.section(GLOBAL_SECTION).value('autorunService', '0', type=Config.BOOLEAN_FIELD)
    # Redirect HTTP to HTTPS
    REDIRECT_TO_HTTPS: Config.Value = Config.section(GLOBAL_SECTION).value('redirectToHttps', '0', type=Config.BOOLEAN_FIELD)
    # Max time needed to get a service "fully functional" before it's considered "failed" and removed
    # The time is in seconds
    MAX_INITIALIZING_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('maxInitTime', '3601', type=Config.NUMERIC_FIELD)
    # Custom HTML for login page
    CUSTOM_HTML_LOGIN: Config.Value = Config.section(GLOBAL_SECTION).value('customHtmlLogin', '', type=Config.LONGTEXT_FIELD)
    # Maximum logs per user service
    MAX_LOGS_PER_ELEMENT: Config.Value = Config.section(GLOBAL_SECTION).value('maxLogPerElement', '100', type=Config.NUMERIC_FIELD)
    # Time to restrain a deployed service in case it gives some errors at some point
    RESTRAINT_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('restrainTime', '600', type=Config.NUMERIC_FIELD)
    # Number of errors that must occurr in RESTRAIN_TIME to restrain deployed service
    RESTRAINT_COUNT: Config.Value = Config.section(GLOBAL_SECTION).value('restrainCount', '3', type=Config.NUMERIC_FIELD)

    # Statistics duration, in days
    STATS_DURATION: Config.Value = Config.section(GLOBAL_SECTION).value('statsDuration', '365', type=Config.NUMERIC_FIELD)
    # If disallow login using /login url, and must go to an authenticator
    DISALLOW_GLOBAL_LOGIN: Config.Value = Config.section(GLOBAL_SECTION).value('disallowGlobalLogin', '0', type=Config.BOOLEAN_FIELD)

    # Allos preferences access to users
    PREFERENCES_ALLOWED: Config.Value = Config.section(GLOBAL_SECTION).value('allowPreferencesAccess', '1', type=Config.BOOLEAN_FIELD)

    # Allowed "trusted sources" for request
    TRUSTED_SOURCES: Config.Value = Config.section(SECURITY_SECTION).value('Trusted Hosts', '*', type=Config.TEXT_FIELD)

    # Allow clients to notify their own ip (if set), or use always the request extracted IP
    HONOR_CLIENT_IP_NOTIFY: Config.Value = Config.section(SECURITY_SECTION).value('honorClientNotifyIP', '0', type=Config.BOOLEAN_FIELD)

    # If there is a proxy in front of us
    BEHIND_PROXY: Config.Value = Config.section(SECURITY_SECTION).value('Behind a proxy', '0', type=Config.BOOLEAN_FIELD)

    # If we use new logout mechanics
    EXCLUSIVE_LOGOUT: Config.Value = Config.section(SECURITY_SECTION).value('Exclusive Logout', '0', type=Config.BOOLEAN_FIELD)

    # Clusters related vars

    # Maximum desired CPU Load. If cpu is over this value, a migration of a service is "desirable"
    # CLUSTER_MIGRATE_CPULOAD: Config.Value = Config.section(CLUSTER_SECTION).value('Migration CPU Load', '80', type=Config.NUMERIC_FIELD)
    # Maximum CPU Load for a node to be elegible for destination of a migration
    # CLUSTER_ELEGIBLE_CPULOAD: Config.Value = Config.section(CLUSTER_SECTION).value('Destination CPU Load', '60', type=Config.NUMERIC_FIELD)
    # Minimum desired Memory free for a cluster node. If free memory (in %) is under this percentage,
    # a migration of a service inside this node is "desirable"
    # CLUSTER_MIGRATE_MEMORYLOAD: Config.Value = Config.section(CLUSTER_SECTION).value('Migration Free Memory', '20', type=Config.NUMERIC_FIELD)
    # Minimum Free memory for a node to be elegible for a destination of a migration
    # CLUSTER_ELEGIBLE_MEMORYLOAD: Config.Value = Config.section(CLUSTER_SECTION).value('Migration Free Memory', '40', type=Config.NUMERIC_FIELD)

    RELOAD_TIME: Config.Value = Config.section(GLOBAL_SECTION).value('Page reload Time', '300', type=Config.NUMERIC_FIELD)

    LIMITED_BY_CALENDAR_TEXT: Config.Value = Config.section(GLOBAL_SECTION).value('Calendar access denied text', '', type=Config.TEXT_FIELD)  # Defaults to Nothing
    # Custom message for error when limiting by calendar

    # This is used so templates can change "styles" from admin interface
    LOWERCASE_USERNAME: Config.Value = Config.section(SECURITY_SECTION).value('Convert username to lowercase', '1', type=Config.BOOLEAN_FIELD)

    # Global UDS ID (common for all servers on the same cluster)
    UDS_ID: Config.Value = Config.section(GLOBAL_SECTION).value('UDS ID', cryptoManager().uuid(), type=Config.READ_FIELD)

    # Site display name & copyright info
    SITE_NAME: Config.Value = Config.section(GLOBAL_SECTION).value('Site name', 'UDS Enterprise', type=Config.TEXT_FIELD)
    SITE_COPYRIGHT: Config.Value = Config.section(GLOBAL_SECTION).value('Site copyright info', '© Virtual Cable S.L.U.', type=Config.TEXT_FIELD)
    SITE_COPYRIGHT_LINK: Config.Value = Config.section(GLOBAL_SECTION).value('Site copyright link', 'https://www.udsenterprise.com', type=Config.TEXT_FIELD)

    EXPERIMENTAL_FEATURES: Config.Value = Config.section(GLOBAL_SECTION).value('Experimental Features', '0', type=Config.BOOLEAN_FIELD)

    _initDone = False

    @staticmethod
    def isInitialized():
        return GlobalConfig._initDone

    @staticmethod
    def initialize() -> None:
        if GlobalConfig._initDone is False:
            try:
                # Tries to initialize database data for global config so it is stored asap and get cached for use
                GlobalConfig._initDone = True
                for v in GlobalConfig.__dict__.values():
                    if isinstance(v, Config.Value):
                        v.get()

                for c in _getLater:
                    logger.debug('Get later: %s', c)
                    c.get()

                _getLater[:] = []

                for c, v in _saveLater:
                    logger.debug('Saving delayed value: %s', c)
                    c.set(v)
                _saveLater[:] = []

                # Process some global config parameters
                # GlobalConfig.UDS_THEME.setParams(['html5', 'semantic'])

            except Exception:
                logger.debug('Config table do not exists!!!, maybe we are installing? :-)')
Example #38
0
    def get(self):
        '''
        Processes get requests
        '''
        logger.debug("Client args for GET: {0}".format(self._args))

        if len(self._args) == 0:  # Gets version
            url = self._request.build_absolute_uri(reverse('ClientDownload'))
            return Client.result({
                'availableVersion': CLIENT_VERSION,
                'requiredVersion': REQUIRED_CLIENT_VERSION,
                'downloadUrl': url
            })

        if len(self._args) == 1:  # Simple test
            return Client.result(_('Correct'))

        try:
            ticket, scrambler = self._args  # If more than 2 args, got an error
            hostname = self._params['hostname']  # Or if hostname is not included...
            srcIp = self._request.ip

            # Ip is optional,
            if GlobalConfig.HONOR_CLIENT_IP_NOTIFY.getBool() is True:
                srcIp = self._params.get('ip', srcIp)

        except Exception:
            raise RequestError('Invalid request')

        logger.debug('Got Ticket: {}, scrambled: {}, Hostname: {}, Ip: {}'.format(ticket, scrambler, hostname, srcIp))

        try:
            data = TicketStore.get(ticket)
        except Exception:
            return Client.result(error=errors.ACCESS_DENIED)

        self._request.user = User.objects.get(uuid=data['user'])

        try:
            logger.debug(data)
            res = userServiceManager().getService(self._request.user, self._request.ip, data['service'], data['transport'])
            logger.debug('Res: {}'.format(res))
            ip, userService, userServiceInstance, transport, transportInstance = res
            password = cryptoManager().xor(data['password'], scrambler).decode('utf-8')

            userService.setConnectionSource(srcIp, hostname)  # Store where we are accessing from so we can notify Service

            transportScript = transportInstance.getUDSTransportScript(userService, transport, ip, self._request.os, self._request.user, password, self._request)

            logger.debug('Script:\n{}'.format(transportScript))

            return Client.result(result=transportScript.encode('bz2').encode('base64'))
        except ServiceNotReadyError as e:
            # Refresh ticket and make this retrayable
            TicketStore.revalidate(ticket, 20)  # Retry will be in at most 5 seconds
            return Client.result(error=errors.SERVICE_IN_PREPARATION, errorCode=e.code, retryable=True)
        except Exception as e:
            logger.exception("Exception")
            return Client.result(error=six.text_type(e))

        # Will never reach this
        raise RuntimeError('Unreachable point reached!!!')
Example #39
0
def generateUuid(obj: typing.Any = None) -> str:
    """
    Generates a ramdom uuid for models default
    """
    return cryptoManager().uuid(obj=obj).lower()
Example #40
0
    def get(self):  # pylint: disable=too-many-locals
        """
        Processes get requests
        """
        logger.debug('Client args for GET: %s', self._args)

        if not self._args:  # Gets version
            return Client.result({
                'availableVersion':
                CLIENT_VERSION,
                'requiredVersion':
                REQUIRED_CLIENT_VERSION,
                'downloadUrl':
                self._request.build_absolute_uri(
                    reverse('page.client-download'))
            })

        if len(self._args) == 1:  # Simple test
            return Client.result(_('Correct'))

        try:
            ticket, scrambler = self._args  # If more than 2 args, got an error.  pylint: disable=unbalanced-tuple-unpacking
            hostname = self._params[
                'hostname']  # Or if hostname is not included...
            srcIp = self._request.ip

            # Ip is optional,
            if GlobalConfig.HONOR_CLIENT_IP_NOTIFY.getBool() is True:
                srcIp = self._params.get('ip', srcIp)

        except Exception:
            raise RequestError('Invalid request')

        logger.debug('Got Ticket: %s, scrambled: %s, Hostname: %s, Ip: %s',
                     ticket, scrambler, hostname, srcIp)

        try:
            data = TicketStore.get(ticket)
        except Exception:
            return Client.result(error=errors.ACCESS_DENIED)

        self._request.user = User.objects.get(uuid=data['user'])

        try:
            logger.debug(data)
            ip, userService, userServiceInstance, transport, transportInstance = userServiceManager(
            ).getService(self._request.user, self._request.os,
                         self._request.ip, data['service'], data['transport'])
            logger.debug('Res: %s %s %s %s %s', ip, userService,
                         userServiceInstance, transport, transportInstance)
            password = cryptoManager().symDecrpyt(data['password'], scrambler)

            userService.setConnectionSource(
                srcIp, hostname
            )  # Store where we are accessing from so we can notify Service

            transportScript, signature, params = transportInstance.getEncodedTransportScript(
                userService, transport, ip, self._request.os,
                self._request.user, password, self._request)

            logger.debug('Signature: %s', signature)
            logger.debug('Data:#######\n%s\n###########', params)

            return Client.result(
                result={
                    'script':
                    transportScript,
                    'signature':
                    signature,  # It is already on base64
                    'params':
                    encoders.encode(encoders.encode(json.dumps(params), 'bz2'),
                                    'base64',
                                    asText=True),
                })
        except ServiceNotReadyError as e:
            # Refresh ticket and make this retrayable
            TicketStore.revalidate(
                ticket,
                20)  # Retry will be in at most 5 seconds, so 20 is fine :)
            return Client.result(error=errors.SERVICE_IN_PREPARATION,
                                 errorCode=e.code,
                                 retryable=True)
        except Exception as e:
            logger.exception("Exception")
            return Client.result(error=str(e))
Example #41
0
 def generateUuid():
     # more owner is this:
     # ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(40))
     return cryptoManager().randomString(40)
Example #42
0
 def generateUuid() -> str:
     return cryptoManager().randomString(40)
Example #43
0
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)
    }),
                        content_type='application/json')
Example #44
0
 def generateUuid():
     # more owner is this:
     # ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(40))
     return cryptoManager().randomString(40)
Example #45
0
def ticketAuth(request: 'HttpRequest', ticketId: str) -> HttpResponse:  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
    """
    Used to authenticate an user via a ticket
    """
    try:
        data = TicketStore.get(ticketId, invalidate=True)

        try:
            # Extract ticket.data from ticket.data storage, and remove it if success
            username = data['username']
            groups = data['groups']
            auth = data['auth']
            realname = data['realname']
            servicePool = data['servicePool']
            password = cryptoManager().decrypt(data['password'])
            transport = data['transport']
        except Exception:
            logger.error('Ticket stored is not valid')
            raise auths.exceptions.InvalidUserException()

        auth = Authenticator.objects.get(uuid=auth)
        # If user does not exists in DB, create it right now
        # Add user to groups, if they exists...
        grps: typing.List = []
        for g in groups:
            try:
                grps.append(auth.groups.get(uuid=g))
            except Exception:
                logger.debug('Group list has changed since ticket assignment')

        if not grps:
            logger.error('Ticket has no valid groups')
            raise Exception('Invalid ticket authentication')

        usr = auth.getOrCreateUser(username, realname)
        if usr is None or State.isActive(
                usr.state) is False:  # If user is inactive, raise an exception
            raise auths.exceptions.InvalidUserException()

        # Add groups to user (replace existing groups)
        usr.groups.set(grps)

        # Force cookie generation
        webLogin(request, None, usr, password)

        request.user = usr  # Temporarily store this user as "authenticated" user, next requests will be done using session
        request.session[
            'ticket'] = '1'  # Store that user access is done using ticket

        # Override and recalc transport based on current os
        transport = None

        logger.debug("Service & transport: %s, %s", servicePool, transport)

        # Check if servicePool is part of the ticket
        if servicePool:
            # If service pool is in there, also is transport
            res = userServiceManager().getService(request.user, request.os,
                                                  request.ip,
                                                  'F' + servicePool, transport,
                                                  False)
            _, userService, _, transport, _ = res

            transportInstance = transport.getInstance()
            if transportInstance.ownLink is True:
                link = reverse('TransportOwnLink',
                               args=('A' + userService.uuid, transport.uuid))
            else:
                link = html.udsAccessLink(request, 'A' + userService.uuid,
                                          transport.uuid)

            request.session['launch'] = link
            response = HttpResponseRedirect(reverse('page.ticket.launcher'))
        else:
            response = HttpResponseRedirect(reverse('page.index'))

        # Now ensure uds cookie is at response
        getUDSCookie(request, response, True)
        return response
    except ServiceNotReadyError as e:
        return errors.errorView(request, errors.SERVICE_NOT_READY)
    except TicketStore.InvalidTicket:
        return errors.errorView(request, errors.RELOAD_NOT_SUPPORTED)
    except Authenticator.DoesNotExist:
        logger.error('Ticket has an non existing authenticator')
        return errors.errorView(request, errors.ACCESS_DENIED)
    except ServicePool.DoesNotExist:
        logger.error('Ticket has an invalid Service Pool')
        return errors.errorView(request, errors.SERVICE_NOT_FOUND)
    except Exception as e:
        logger.exception('Exception')
        return errors.exceptionView(request, e)
Example #46
0
from uds.REST import Handler
from uds.REST import RequestError
from uds.models import UserService


import datetime
import six

import logging

logger = logging.getLogger(__name__)


# Actor key, configurable in Security Section of administration interface
actorKey = Config.Config.section(Config.SECURITY_SECTION).value('Master Key',
                                                                cryptoManager().uuid(datetime.datetime.now()).replace('-', ''),
                                                                type=Config.Config.TEXT_FIELD)
actorKey.get()

# Error codes:
ERR_INVALID_KEY = 1
ERR_HOST_NOT_MANAGED = 2
ERR_USER_SERVICE_NOT_FOUND = 3
ERR_OSMANAGER_ERROR = 4


# Enclosed methods under /actor path
class Actor(Handler):
    '''
    Processes actor requests
    '''
Example #47
0
    def put(self):
        """
        Processes put requests, currently only under "create"
        """
        logger.debug(self._args)

        # Parameters can only be theese

        for p in self._params:
            if p not in VALID_PARAMS:
                logger.debug('Parameter {} not in valid ticket parameters list'.format(p))
                raise RequestError('Invalid parameters')

        if len(self._args) != 1 or self._args[0] not in ('create',):
            raise RequestError('Invalid method')

        if 'username' not in self._params or 'groups' not in self._params:
            raise RequestError('Invalid parameters')

        found = None
        for i in ('authId', 'authTag', 'auth', 'authSmallName'):
            if i in self._params:
                found = i
                break

        if found is None:
            raise RequestError('Invalid parameters (no auth)')

        force = self._params.get('force', '0') in ('1', 'true', 'True')

        userIp = self._params.get('userIp', None)

        try:
            authId = self._params.get('authId', None)
            authTag = self._params.get('authTag', self._params.get('authSmallName', None))
            authName = self._params.get('auth', None)

            # Will raise an exception if no auth found
            if authId is not None:
                auth = Authenticator.objects.get(uuid=processUuid(authId.lower()))
            elif authName is not None:
                auth = Authenticator.objects.get(name=authName)
            else:
                auth = Authenticator.objects.get(small_name=authTag)

            username = self._params['username']
            password = self._params.get('password', '')  # Some machines needs password, depending on configuration
            groups = self._params['groups']
            if isinstance(groups, (six.text_type, six.binary_type)):
                groups = (groups,)
            grps = []
            for g in groups:
                try:
                    grps.append(auth.groups.get(name=g).uuid)
                except Exception:
                    logger.info('Group {} from ticket does not exists on auth {}, forced creation: {}'.format(g, auth, force))
                    if force:
                        grps.append(auth.groups.create(name=g, comments='Autocreated form ticket by using force paratemeter').uuid)

            if len(grps) == 0:  # No valid group in groups names
                raise Exception('Authenticator does not contain ANY of the requested groups')

            groups = grps

            time = int(self._params.get('time', 60))
            time = 60 if time < 1 else time
            realname = self._params.get('realname', self._params['username'])
            servicePool = self._params.get('servicePool', None)

            transport = self._params.get('transport', None)

            if servicePool is not None:
                servicePool = DeployedService.objects.get(uuid=processUuid(servicePool))

                # If forced that servicePool must honor groups
                if force:
                    for addGrp in set(groups) - set(servicePool.assignedGroups.values_list('uuid', flat=True)):
                        servicePool.assignedGroups.add(auth.groups.get(uuid=addGrp))

                if transport is not None:
                    transport = Transport.objects.get(uuid=processUuid(transport))
                    try:
                        servicePool.validateTransport(transport)
                    except Exception:
                        logger.error('Transport {} is not valid for Service Pool {}'.format(transport.name, servicePool.name))
                        raise Exception('Invalid transport for Service Pool')
                else:
                    if userIp is None:
                        transport = tools.DictAsObj({'uuid': None})
                    else:
                        transport = None
                        for v in servicePool.transports.order_by('priority'):
                            if v.validForIp(userIp):
                                transport = v
                                break

                        if transport is None:
                            logger.error('Service pool {} does not has valid transports for ip {}'.format(servicePool.name, userIp))
                            raise Exception('Service pool does not has any valid transports for ip {}'.format(userIp))

                servicePool = servicePool.uuid
                transport = transport.uuid  # pylint: disable=maybe-no-member

        except Authenticator.DoesNotExist:
            return Tickets.result(error='Authenticator does not exists')
        except DeployedService.DoesNotExist:
            return Tickets.result(error='Service pool does not exists')
        except Transport.DoesNotExist:
            return Tickets.result(error='Transport does not exists')
        except Exception as e:
            return Tickets.result(error=six.text_type(e))

        data = {
            'username': username,
            'password': cryptoManager().encrypt(password),
            'realname': realname,
            'groups': groups,
            'auth': auth.uuid,
            'servicePool': servicePool,
            'transport': transport,
        }

        ticket = TicketStore.create(data)

        return Tickets.result(ticket)
Example #48
0
def generateUuid():
    '''
    Generates a ramdom uuid for models default
    '''
    return cryptoManager().uuid().lower()
Example #49
0
    def put(self):  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
        """
        Processes put requests, currently only under "create"
        """
        logger.debug(self._args)

        # Check that call is correct (pamateters, args, ...)
        self._checkInput()

        if 'username' not in self._params or 'groups' not in self._params:
            raise RequestError('Invalid parameters')

        force: bool = self._params.get('force', '0') in ('1', 'true', 'True')

        userIp: typing.Optional[str] = self._params.get('userIp', None)

        try:
            authId = self._params.get('authId', None)
            authName = self._params.get('auth', None)
            authTag = self._params.get('authTag', self._params.get('authSmallName', None))

            # Will raise an exception if no auth found
            if authId:
                auth = models.Authenticator.objects.get(uuid=processUuid(authId.lower()))
            elif authName:
                auth = models.Authenticator.objects.get(name=authName)
            else:
                auth = models.Authenticator.objects.get(small_name=authTag)

            username: str = self._params['username']
            password: str = self._params.get('password', '')  # Some machines needs password, depending on configuration

            groupIds: typing.List[str] = []
            for groupName in tools.asList(self._params['groups']):
                try:
                    groupIds.append(auth.groups.get(name=groupName).uuid)
                except Exception:
                    logger.info('Group %s from ticket does not exists on auth %s, forced creation: %s', groupName, auth, force)
                    if force:  # Force creation by call
                        groupIds.append(auth.groups.create(name=groupName, comments='Autocreated form ticket by using force paratemeter').uuid)

            if not groupIds:  # No valid group in groups names
                raise RequestError('Authenticator does not contain ANY of the requested groups and force is not used')

            time = int(self._params.get('time', 60))
            time = 60 if time < 1 else time
            realname: str = self._params.get('realname', self._params['username'])

            if 'servicePool' in self._params:
                servicePool: models.ServicePool = models.ServicePool.objects.get(uuid=processUuid(self._params['servicePool']))

                # If forced that servicePool must honor groups
                if force:
                    for addGrp in set(groupIds) - set(servicePool.assignedGroups.values_list('uuid', flat=True)):
                        servicePool.assignedGroups.add(auth.groups.get(uuid=addGrp))

                if 'transport' in self._params:
                    transport: models.Transport = models.Transport.objects.get(uuid=processUuid(self._params['transport']))
                    try:
                        servicePool.validateTransport(transport)
                    except Exception:
                        logger.error('Transport %s is not valid for Service Pool %s', transport.name, servicePool.name)
                        raise Exception('Invalid transport for Service Pool')
                else:
                    transport = models.Transport(uuid=None)
                    if userIp:
                        for v in servicePool.transports.order_by('priority'):
                            if v.validForIp(userIp):
                                transport = v
                                break

                        if transport.uuid is None:
                            logger.error('Service pool %s does not has valid transports for ip %s', servicePool.name, userIp)
                            raise Exception('Service pool does not has any valid transports for ip {}'.format(userIp))

                servicePool = servicePool.uuid
                transport = transport.uuid

        except models.Authenticator.DoesNotExist:
            return Tickets.result(error='Authenticator does not exists')
        except models.ServicePool.DoesNotExist:
            return Tickets.result(error='Service pool does not exists')
        except models.Transport.DoesNotExist:
            return Tickets.result(error='Transport does not exists')
        except Exception as e:
            return Tickets.result(error=str(e))

        data = {
            'username': username,
            'password': cryptoManager().encrypt(password),
            'realname': realname,
            'groups': groupIds,
            'auth': auth.uuid,
            'servicePool': servicePool,
            'transport': transport,
        }

        ticket = models.TicketStore.create(data)

        return Tickets.result(ticket)
Example #50
0
    def get(self):
        """
        Processes get requests
        """
        logger.debug("Client args for GET: {0}".format(self._args))

        if len(self._args) == 0:  # Gets version
            url = self._request.build_absolute_uri(reverse('page.client-download'))
            return Client.result({
                'availableVersion': CLIENT_VERSION,
                'requiredVersion': REQUIRED_CLIENT_VERSION,
                'downloadUrl': url
            })

        if len(self._args) == 1:  # Simple test
            return Client.result(_('Correct'))

        try:
            ticket, scrambler = self._args  # If more than 2 args, got an error
            hostname = self._params['hostname']  # Or if hostname is not included...
            srcIp = self._request.ip

            # Ip is optional,
            if GlobalConfig.HONOR_CLIENT_IP_NOTIFY.getBool() is True:
                srcIp = self._params.get('ip', srcIp)

        except Exception:
            raise RequestError('Invalid request')

        logger.debug('Got Ticket: {}, scrambled: {}, Hostname: {}, Ip: {}'.format(ticket, scrambler, hostname, srcIp))

        try:
            data = TicketStore.get(ticket)
        except Exception:
            return Client.result(error=errors.ACCESS_DENIED)

        self._request.user = User.objects.get(uuid=data['user'])

        try:
            logger.debug(data)
            res = userServiceManager().getService(self._request.user, self._request.ip, data['service'], data['transport'])
            logger.debug('Res: {}'.format(res))
            ip, userService, userServiceInstance, transport, transportInstance = res
            password = cryptoManager().symDecrpyt(data['password'], scrambler)

            userService.setConnectionSource(srcIp, hostname)  # Store where we are accessing from so we can notify Service

            transportScript, signature, params = transportInstance.getEncodedTransportScript(userService, transport, ip, self._request.os, self._request.user, password, self._request)

            logger.debug('Signature: {}'.format(signature))
            logger.debug('Data:#######\n{}\n###########'.format(params))

            return Client.result(result={
                'script': transportScript,
                'signature': signature,  # It is already on base64
                'params': json.dumps(params).encode('bz2').encode('base64'),
            })
        except ServiceNotReadyError as e:
            # Refresh ticket and make this retrayable
            TicketStore.revalidate(ticket, 20)  # Retry will be in at most 5 seconds
            return Client.result(error=errors.SERVICE_IN_PREPARATION, errorCode=e.code, retryable=True)
        except Exception as e:
            logger.exception("Exception")
            return Client.result(error=six.text_type(e))

        # Will never reach this
        raise RuntimeError('Unreachable point reached!!!')
Example #51
0
from uds.REST import Handler
from uds.REST import RequestError
from uds.core.managers import cryptoManager
from uds.core.osmanagers import OSManager
from uds.core.util import Config
from uds.core.util.State import State
from uds.core.util.model import processUuid
from uds.models import TicketStore
from uds.models import UserService

logger = logging.getLogger(__name__)

# Actor key, configurable in Security Section of administration interface
actorKey = Config.Config.section(Config.SECURITY_SECTION).value(
    'Master Key',
    cryptoManager().uuid(datetime.datetime.now()).replace('-', ''),
    type=Config.Config.TEXT_FIELD)

actorKey.get()

# Error codes:
ERR_INVALID_KEY = 1
ERR_HOST_NOT_MANAGED = 2
ERR_USER_SERVICE_NOT_FOUND = 3
ERR_OSMANAGER_ERROR = 4

# Constants for tickets
OWNER = 'ACTOR'
SECURE_OWNER = 'SACTOR'

Example #52
0
def generateUuid():
    """
    Generates a ramdom uuid for models default
    """
    return cryptoManager().uuid().lower()
Example #53
0
def unscrambleId(request, id_):
    idd = cryptoManager().xor(encoders.decode(id_ + '==\n', 'base64', asText=False), request.session[SCRAMBLE_SES])
    return idd.decode('utf8').split('~')[0]
Example #54
0
    def getLink(  # pylint: disable=too-many-locals
            self,
            userService: 'models.UserService',
            transport: 'models.Transport',
            ip: str,
            os: typing.Dict[str, str],
            user: '******',
            password: str,
            request: 'HttpRequest'
        ) -> str:
        credsInfo = self.processUserAndPassword(userService, user, password)
        username, password, domain = credsInfo['username'], credsInfo['password'], credsInfo['domain']

        if domain != '':
            username = domain + '\\' + username

        scrambler = cryptoManager().randomString(32)
        passwordCrypted = cryptoManager().symCrypt(password, scrambler)

        # Build params dict
        params = {
            'protocol': 'rdp',
            'hostname': ip,
            'username': username,
            'password': passwordCrypted,
            'ignore-cert': 'true',
            'security': self.security.value,
            'drive-path': '/share/{}'.format(user.uuid),
            'create-drive-path': 'true'
        }

        if self.enableFileSharing.isTrue():
            params['enable-drive'] = 'true'

        if self.serverLayout.value != '-':
            params['server-layout'] = self.serverLayout.value

        if self.enableAudio.isTrue() is False:
            params['disable-audio'] = 'true'

        if self.enablePrinting.isTrue() is True:
            params['enable-printing'] = 'true'
            params['printer-name'] = 'UDS-Printer'

        if self.wallpaper.isTrue() is True:
            params['enable-wallpaper'] = 'true'

        if self.desktopComp.isTrue() is True:
            params['enable-desktop-composition'] = 'true'

        if self.smooth.isTrue() is True:
            params['enable-font-smoothing'] = 'true'

        logger.debug('RDP Params: %s', params)

        ticket = models.TicketStore.create(params, validity=self.ticketValidity.num())

        return HttpResponseRedirect(
            "{}/transport/{}?{}.{}&{}".format(
                self.guacamoleServer.value,
                'o_n_w' if self.forceNewWindow.isTrue() else '',
                ticket,
                scrambler,
                'javascript:window.close();'
            )
        )