Example #1
0
class IPServerService(services.Service):

    # Gui
    remoteHost = gui.TextField(
        oder=1,
        length=64,
        label=_('Remote Server'),
        tooltip=_(
            'IP or Hostname of remote host (must be resolvable by clients)'),
        required=True,
    )

    # Description of service
    typeName = _('Single server service by IP')
    typeType = 'IPServerService'
    typeDescription = _(
        'This service provides access to POWERED-ON Server by IP')
    iconFile = 'machine.png'

    # Characteristics of service
    maxDeployed = -1  # If the service provides more than 1 "provided service" (-1 = no limit, 0 = ???? (do not use it!!!), N = max number to deploy
    usesCache = False  # Cache are running machine awaiting to be assigned
    usesCache_L2 = False  # L2 Cache are running machines in suspended state
    needsManager = False  # If the service needs a s.o. manager (managers are related to agents provided by services itselfs, i.e. virtual machines with agent)
    mustAssignManually = False  # If true, the system can't do an automatic assignation of a deployed user service from this service

    deployedType = IPMachineDeployed

    servicesTypeProvided = (serviceTypes.VDI, )

    def initialize(self, values):
        """
        We check here form values to see if they are valid.

        Note that we check them throught FROM variables, that already has been
        initialized by __init__ method of base class, before invoking this.
        """
        if values is not None:
            if self.remoteHost.value == '':
                raise services.Service.ValidationException(
                    'No remote host found')

    # 172.27.0.1~00000
    def getData(self):
        return self.nameGenerator().get(self.remoteHost.value + '~')

    def releaseData(self, data):
        baseName = data.split('~')[0] + '~'
        self.nameGenerator().free(baseName, data)
Example #2
0
class IPSingleMachineService(services.Service):

    # Gui
    ip = gui.TextField(length=64,
                       label=_('Machine IP'),
                       order=1,
                       tooltip=_('Machine IP'),
                       required=True)

    # Description of service
    typeName = _('Static Single IP')
    typeType = 'IPSingleMachineService'
    typeDescription = _(
        'This service provides access to POWERED-ON Machine by IP')
    iconFile = 'machine.png'

    # Characteristics of service
    maxDeployed = -1  # If the service provides more than 1 "provided service" (-1 = no limit, 0 = ???? (do not use it!!!), N = max number to deploy
    usesCache = False  # Cache are running machine awaiting to be assigned
    usesCache_L2 = False  # L2 Cache are running machines in suspended state
    needsManager = False  # If the service needs a s.o. manager (managers are related to agents provided by services itselfs, i.e. virtual machines with agent)
    mustAssignManually = False  # If true, the system can't do an automatic assignation of a deployed user service from this service

    deployedType = IPMachineDeployed

    servicesTypeProvided = (serviceTypes.VDI, )

    def initialize(self, values):
        pass

    def getUnassignedMachine(self):
        ip = None
        try:
            self.storage.lock()
            counter = self.storage.getPickle('counter')
            counter = counter + 1 if counter is not None else 1
            self.storage.putPickle('counter', counter)
            ip = '{}~{}'.format(self.ip.value, counter)
        except Exception:
            ip = None
            logger.exception("Exception at getUnassignedMachine")
        finally:
            self.storage.unlock()

        return ip

    def unassignMachine(self, ip):
        pass
Example #3
0
class HTML5RDPTransport(Transport):
    '''
    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
    '''
    typeName = _('HTML5 RDP Transport')
    typeType = 'HTML5RDPTransport'
    typeDescription = _('RDP Transport using HTML5 client')
    iconFile = 'rdp.png'

    ownLink = True
    supportedOss = OsDetector.allOss
    protocol = protocols.RDP

    guacamoleServer = gui.TextField(
        label=_('Tunnel Server'),
        order=1,
        tooltip=
        _('Host of the tunnel server (use http/https & port if needed) as accesible from users'
          ),
        defvalue='https://',
        length=64,
        required=True)
    useEmptyCreds = gui.CheckBoxField(
        label=_('Empty creds'),
        order=2,
        tooltip=_('If checked, the credentials used to connect will be emtpy'))
    fixedName = gui.TextField(
        label=_('Username'),
        order=3,
        tooltip=_(
            'If not empty, this username will be always used as credential'))
    fixedPassword = gui.PasswordField(
        label=_('Password'),
        order=4,
        tooltip=_(
            'If not empty, this password will be always used as credential'))
    withoutDomain = gui.CheckBoxField(
        label=_('Without Domain'),
        order=5,
        tooltip=
        _('If checked, the domain part will always be emptied (to connecto to xrdp for example is needed)'
          ))
    fixedDomain = gui.TextField(
        label=_('Domain'),
        order=6,
        tooltip=
        _('If not empty, this domain will be always used as credential (used as DOMAIN\\user)'
          ))
    enableAudio = gui.CheckBoxField(
        label=_('Enable Audio'),
        order=7,
        tooltip=
        _('If checked, the audio will be redirected to client (if client browser supports it)'
          ))
    enablePrinting = gui.CheckBoxField(
        label=_('Enable Printing'),
        order=8,
        tooltip=
        _('If checked, the printing will be redirected to client (if client browser supports it)'
          ))

    def initialize(self, values):
        if values is None:
            return
        if self.guacamoleServer.value[0:4] != 'http':
            raise Transport.ValidationException(
                _('The server must be http or https'))

    # Same check as normal RDP transport
    def isAvailableFor(self, ip):
        '''
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        '''
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache().get(ip)
        if ready is None:
            # Check again for readyness
            if connection.testServer(ip, '3389') is True:
                self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache().put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def processedUser(self, userService, userName):
        v = self.processUserPassword(userService, userName, '')
        return v['username']

    def processUserPassword(self, service, user, password):
        username = user.getUsernameForAuth()

        if self.fixedName.value is not '':
            username = self.fixedName.value

        proc = username.split('@')
        if len(proc) > 1:
            domain = proc[1]
        else:
            domain = ''
        username = proc[0]

        if self.fixedPassword.value is not '':
            password = self.fixedPassword.value
        if self.fixedDomain.value is not '':
            domain = self.fixedDomain.value
        if self.useEmptyCreds.isTrue():
            username, password, domain = '', '', ''

        if self.withoutDomain.isTrue():
            domain = ''

        if '.' in domain:  # Dotter domain form
            username = username + '@' + domain
            domain = ''

        # Fix username/password acording to os manager
        username, password = service.processUserPassword(username, password)

        return {
            'protocol': self.protocol,
            'username': username,
            'password': password,
            'domain': domain
        }

    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

        # Build params dict
        params = {
            'protocol': 'rdp',
            'hostname': ip,
            'username': username,
            'password': password,
            'ignore-cert': 'true'
        }

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

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

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

        ticket = TicketStore.create(params)

        return HttpResponseRedirect("{}/transport/?{}&{}".format(
            self.guacamoleServer.value, ticket,
            request.build_absolute_uri(reverse('Index'))))
Example #4
0
class RegexLdap(auths.Authenticator):

    host = gui.TextField(length=64, label=_('Host'), order=1, tooltip=_('Ldap Server Host'), required=True)
    port = gui.NumericField(length=5, label=_('Port'), defvalue='389', order=2, tooltip=_('Ldap port (usually 389 for non ssl and 636 for ssl)'), required=True)
    ssl = gui.CheckBoxField(label=_('Use SSL'), order=3, tooltip=_('If checked, the connection will be ssl, using port 636 instead of 389'))
    username = gui.TextField(length=64, label=_('User'), order=4, tooltip=_('Username with read privileges on the base selected'), required=True, tab=gui.CREDENTIALS_TAB)
    password = gui.PasswordField(lenth=32, label=_('Password'), order=5, tooltip=_('Password of the ldap user'), required=True, tab=gui.CREDENTIALS_TAB)
    timeout = gui.NumericField(length=3, label=_('Timeout'), defvalue='10', order=6, tooltip=_('Timeout in seconds of connection to LDAP'), required=True, minValue=1)

    ldapBase = gui.TextField(length=64, label=_('Base'), order=7, tooltip=_('Common search base (used for "users" and "groups")'), required=True, tab=_('Ldap info'))
    userClass = gui.TextField(length=64, label=_('User class'), defvalue='posixAccount', order=8, tooltip=_('Class for LDAP users (normally posixAccount)'), required=True, tab=_('Ldap info'))
    userIdAttr = gui.TextField(length=64, label=_('User Id Attr'), defvalue='uid', order=9, tooltip=_('Attribute that contains the user id'), required=True, tab=_('Ldap info'))
    userNameAttr = gui.TextField(length=640, label=_('User Name Attr'), multiline=2, defvalue='uid', order=10, tooltip=_('Attributes that contains the user name (list of comma separated values)'), required=True, tab=_('Ldap info'))
    groupNameAttr = gui.TextField(length=640, label=_('Group Name Attr'), multiline=2, defvalue='cn', order=11, tooltip=_('Attribute that contains the group name'), required=True, tab=_('Ldap info'))
    # regex = gui.TextField(length=64, label = _('Regular Exp. for groups'), defvalue = '^(.*)', order = 12, tooltip = _('Regular Expression to extract the group name'), required = True)

    typeName = _('Regex LDAP Authenticator')
    typeType = 'RegexLdapAuthenticator'
    typeDescription = _('Regular Expressions LDAP authenticator')
    iconFile = 'auth.png'

    # If it has and external source where to get "new" users (groups must be declared inside UDS)
    isExternalSource = True
    # If we need to enter the password for this user
    needsPassword = False
    # Label for username field
    userNameLabel = _('Username')
    # Label for group field
    groupNameLabel = _("Group")
    # Label for password field
    passwordLabel = _("Password")

    def __init__(self, dbAuth, environment, values=None):
        super(RegexLdap, self).__init__(dbAuth, environment, values)
        if values is not None:
            self.__validateField(values['userNameAttr'], str(self.userNameAttr.label))
            self.__validateField(values['userIdAttr'], str(self.userIdAttr.label))
            self.__validateField(values['groupNameAttr'], str(self.groupNameAttr.label))

            self._host = values['host']
            self._port = values['port']
            self._ssl = gui.strToBool(values['ssl'])
            self._username = values['username']
            self._password = values['password']
            self._timeout = values['timeout']
            self._ldapBase = values['ldapBase']
            self._userClass = values['userClass']
            self._userIdAttr = values['userIdAttr']
            self._groupNameAttr = values['groupNameAttr']
            # self._regex = values['regex']
            self._userNameAttr = values['userNameAttr']

        else:
            self._host = None
            self._port = None
            self._ssl = None
            self._username = None
            self._password = None
            self._timeout = None
            self._ldapBase = None
            self._userClass = None
            self._userIdAttr = None
            self._groupNameAttr = None
            # self._regex = None
            self._userNameAttr = None
        self._connection = None

    def __validateField(self, field, fieldLabel):
        '''
        Validates the multi line fields refering to attributes
        '''
        for line in field.splitlines():
            if line.find('=') != -1:
                _, pattern = line.split('=')[0:2]
                if pattern.find('(') == -1:
                    pattern = '(' + pattern + ')'
                try:
                    re.search(pattern, '')
                except:
                    raise auths.Authenticator.ValidationException('Invalid pattern in {0}: {1}'.format(fieldLabel, line))

    def __getAttrsFromField(self, field):
        res = []
        for line in field.splitlines():
            equalPos = line.find('=')
            if equalPos != -1:
                attr = line[:equalPos]
            else:
                attr = line
            res.append(attr.encode('utf-8'))
        return res

    def __processField(self, field, attributes):
        res = []
        logger.debug('Attributes: {}'.format(attributes))
        for line in field.splitlines():
            equalPos = line.find('=')
            if equalPos == -1:
                line += '=(.*)'
                equalPos = line.find('=')
            attr, pattern = (line[:equalPos], line[equalPos + 1:])
            attr = attr.lower()
            # if pattern do not have groups, define one with full re
            if pattern.find('(') == -1:
                pattern = '(' + pattern + ')'
            val = attributes.get(attr, [])
            if type(val) is not list:  # May we have a single value
                val = [val]

            logger.debug('Pattern: {0}'.format(pattern))

            for vv in val:
                try:
                    v = vv.decode('utf-8')
                    logger.debug('v, vv: {}, {}'.format(v, vv))
                    srch = re.search(pattern, v, re.IGNORECASE)
                    logger.debug("Found against {0}: {1} ".format(v, srch.groups()))
                    if srch is None:
                        continue
                    res.append(''.join(srch.groups()))
                except Exception:
                    pass  # Ignore exceptions here
        return res

    def valuesDict(self):
        return {
            'host': self._host, 'port': self._port, 'ssl': gui.boolToStr(self._ssl),
            'username': self._username, 'password': self._password, 'timeout': self._timeout,
            'ldapBase': self._ldapBase, 'userClass': self._userClass,
            'userIdAttr': self._userIdAttr, 'groupNameAttr': self._groupNameAttr,
            'userNameAttr': self._userNameAttr
        }

    def __str__(self):
        return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, userIdAttr = {6}, groupNameAttr = {7}, userName attr = {8}".format(
               self._username, self._password, self._host, self._port, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr,
               self._userNameAttr)

    def marshal(self):
        return '\t'.join([
            'v2',
            self._host, self._port, gui.boolToStr(self._ssl), self._username, self._password, self._timeout,
            self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._userNameAttr
        ])

    def unmarshal(self, val):
        data = val.split('\t')
        if data[0] == 'v1':
            logger.debug("Data: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, _regex, self._userNameAttr = data[1:]
            self._ssl = gui.strToBool(self._ssl)
            self._groupNameAttr = self._groupNameAttr + '=' + _regex
            self._userNameAttr = '\n'.join(self._userNameAttr.split(','))
        elif data[0] == 'v2':
            logger.debug("Data v2: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._userNameAttr = data[1:]
            self._ssl = gui.strToBool(self._ssl)

    def __connection(self, username=None, password=None):
        if self._connection is None or username is not None:  # We want this method also to check credentials
            l = None
            cache = False
            try:
                if password is not None:
                    password = password.encode('utf-8')

                # ldap.set_option(ldap.OPT_DEBUG_LEVEL, 9)
                ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
                schema = self._ssl and 'ldaps' or 'ldap'
                port = self._port != '389' and ':' + self._port or ''
                uri = "%s://%s%s" % (schema, self._host, port)
                logger.debug('Ldap uri: {0}'.format(uri))
                l = ldap.initialize(uri=uri)
                l.set_option(ldap.OPT_REFERRALS, 0)
                l.network_timeout = l.timeout = int(self._timeout)
                l.protocol_version = ldap.VERSION3

                if username is None:
                    cache = True
                    username = self._username
                    password = self._password

                l.simple_bind_s(who=username, cred=password)
            except ldap.LDAPError as e:
                str_ = _('Ldap connection error: ')
                if isinstance(e.message, dict):
                    str_ += ', '.join(e.message.get('info', ''), e.message.get('desc'))
                else:
                    str_ += six.text_type(e)
                raise Exception(str_)
            if cache is True:
                self._connection = l
            else:
                return l  # Do not cache nor overwrite "global" connection
        return self._connection

    def __getUser(self, username):
        try:
            con = self.__connection()
            filter_ = '(&(objectClass=%s)(%s=%s))' % (self._userClass, self._userIdAttr, ldap.filter.escape_filter_chars(username, 0))
            attrlist = [self._userIdAttr.encode('utf-8')] + self.__getAttrsFromField(self._userNameAttr) + self.__getAttrsFromField(self._groupNameAttr)

            logger.debug('Getuser filter_: {0}, attr list: {1}'.format(filter_, attrlist))
            res = con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE,
                                   filterstr=filter_, attrlist=attrlist, sizelimit=LDAP_RESULT_LIMIT)[0]
            usr = dict((k, '') for k in attrlist)
            dct = {k.lower(): v for k, v in six.iteritems(res[1])}
            usr.update(dct)
            usr.update({'dn': res[0], '_id': username})
            logger.debug('Usr: {0}'.format(usr))
            return usr
        except Exception:
            logger.exception('Exception:')
            return None

    def __getGroups(self, usr):
        return self.__processField(self._groupNameAttr, usr)

    def __getUserRealName(self, usr):
        return ' '.join(self.__processField(self._userNameAttr, usr))
        # return ' '.join([ (type(usr.get(id_, '')) is list and ' '.join(( str(k) for k in usr.get(id_, ''))) or str(usr.get(id_, ''))) for id_ in self._userNameAttr.split(',') ]).strip()

    def authenticate(self, username, credentials, groupsManager):
        '''
        Must authenticate the user.
        We can have to different situations here:
           1.- The authenticator is external source, what means that users may be unknown to system before callig this
           2.- The authenticator isn't external source, what means that users have been manually added to system and are known before this call
        We receive the username, the credentials used (normally password, but can be a public key or something related to pk) and a group manager.
        The group manager is responsible for letting know the authenticator which groups we currently has active.
        @see: uds.core.auths.GroupsManager
        '''
        try:
            # Locate the user at LDAP
            usr = self.__getUser(username)

            if usr is None:
                return False

            # Let's see first if it credentials are fine
            self.__connection(usr['dn'], credentials)  # Will raise an exception if it can't connect

            groupsManager.validate(self.__getGroups(usr))

            return True

        except Exception:
            return False

    def createUser(self, usrData):
        '''
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        External sources already has the user  cause they are managed externally, so, it can at most test if the users exists on external source
        before accepting it.
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, real_name, comments, state & password
        @return:  Raises an exception (AuthException) it things didn't went fine
        '''
        res = self.__getUser(usrData['name'])
        if res is None:
            raise AuthenticatorException(_('Username not found'))
        # Fills back realName field
        usrData['real_name'] = self.__getUserRealName(res)

    def getRealName(self, username):
        '''
        Tries to get the real name of an user
        '''
        res = self.__getUser(username)
        if res is None:
            return username
        return self.__getUserRealName(res)

    def modifyUser(self, usrData):
        '''
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        Modify user has no reason on external sources, so it will never be used (probably)
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, realName, comments, state & password
        @return:  Raises an exception it things doesn't go fine
        '''
        return self.createUser(usrData)

    def createGroup(self, groupData):
        '''
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        External sources already has its own groups and, at most, it can check if it exists on external source before accepting it
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @params groupData: a dict that has, at least, name, comments and active
        @return:  Raises an exception it things doesn't go fine
        '''
        pass

    def getGroups(self, username, groupsManager):
        '''
        Looks for the real groups to which the specified user belongs
        Updates groups manager with valid groups
        Remember to override it in derived authentication if needed (external auths will need this, for internal authenticators this is never used)
        '''
        user = self.__getUser(username)
        if user is None:
            raise AuthenticatorException(_('Username not found'))
        groups = self.__getGroups(user)
        groupsManager.validate(groups)

    def searchUsers(self, pattern):
        try:
            con = self.__connection()
            res = []
            for r in con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(&(objectClass=%s)(%s=%s*))' % (self._userClass, self._userIdAttr, pattern), sizelimit=LDAP_RESULT_LIMIT):
                if r[0] is not None:  # Must have a dn, we do not accept references to other
                    dct = {k.lower(): v for k, v in six.iteritems(r[1])}
                    logger.debug('R: {0}'.format(dct))
                    usrId = dct.get(self._userIdAttr.lower(), '')
                    usrId = type(usrId) == list and usrId[0] or usrId
                    res.append({
                        'id': usrId,
                        'name': self.__getUserRealName(dct)
                    })
            logger.debug(res)
            return res
        except Exception:
            logger.exception("Exception: ")
            raise AuthenticatorException(_('Too many results, be more specific'))

    @staticmethod
    def test(env, data):
        try:
            auth = RegexLdap(None, env, data)
            return auth.testConnection()
        except Exception as e:
            logger.error("Exception found testing Simple LDAP auth {0}: {1}".format(e.__class__, e))
            return [False, "Error testing connection"]

    def testConnection(self):
        try:
            con = self.__connection()
        except Exception as e:
            return [False, str(e)]

        try:
            con.search_s(base=self._ldapBase, scope=ldap.SCOPE_BASE)
        except Exception:
            return [False, _('Ldap search base is incorrect')]

        try:
            if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(objectClass=%s)' % self._userClass, sizelimit=1)) == 1:
                raise Exception()
            return [False, _('Ldap user class seems to be incorrect (no user found by that class)')]
        except Exception as e:
            # If found 1 or more, all right
            pass

        # Now test objectclass and attribute of users
        try:
            if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(&(objectClass=%s)(%s=*))' % (self._userClass, self._userIdAttr), sizelimit=1)) == 1:
                raise Exception()
            return [False, _('Ldap user id attr is probably wrong (can\'t find any user with both conditions)')]
        except Exception as e:
            # If found 1 or more, all right
            pass

        for grpNameAttr in self._groupNameAttr.split('\n'):
            vals = grpNameAttr.split('=')[0]
            if vals == 'dn':
                continue
            try:
                if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(%s=*)' % vals, sizelimit=1)) == 1:
                    continue
            except:
                continue
            return [False, _('Ldap group id attribute seems to be incorrect (no group found by that attribute)')]

        # Now try to test regular expression to see if it matches anything (
        try:
            # Check the existence of at least a () grouping
            # Check validity of regular expression (try to compile it)
            # this only right now
            pass
        except Exception as e:
            pass

        return [True, _("Connection params seem correct, test was succesfully executed")]
Example #5
0
class BaseSpiceTransport(Transport):
    '''
    Provides access via SPICE 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
    '''
    iconFile = 'spice.png'
    protocol = protocols.SPICE
    allowedProviders = oVirtProvider.offers

    useEmptyCreds = gui.CheckBoxField(
        order=1,
        label=_('Empty credentials'),
        tooltip=_('If checked, the credentials used to connect will be emtpy'),
        tab=gui.CREDENTIALS_TAB
    )
    fixedName = gui.TextField(
        order=2,
        label=_('Username'),
        tooltip=_('If not empty, this username will be always used as credential'),
        tab=gui.CREDENTIALS_TAB
    )
    fixedPassword = gui.PasswordField(
        order=3,
        label=_('Password'),
        tooltip=_('If not empty, this password will be always used as credential'),
        tab=gui.CREDENTIALS_TAB
    )
    serverCertificate = gui.TextField(
        order=4,
        length=4096,
        multiline=4,
        label=_('Certificate'),
        tooltip=_('Server certificate (public), can be found on your ovirt engine, probably at /etc/pki/ovirt-engine/certs/ca.der (Use the contents of this file).'),
        required=False
    )
    fullScreen = gui.CheckBoxField(
        order=5,
        label=_('Fullscreen Mode'),
        tooltip=_('If checked, viewer will be shown on fullscreen mode-'),
        tab=gui.ADVANCED_TAB
    )
    smartCardRedirect = gui.CheckBoxField(
        order=6,
        label=_('Smartcard Redirect'),
        tooltip=_('If checked, SPICE protocol will allow smartcard redirection.'),
        defvalue=gui.FALSE,
        tab=gui.ADVANCED_TAB
    )
    usbShare = gui.CheckBoxField(
        order=7,
        label=_('Enable USB'),
        tooltip=_('If checked, USB redirection will be allowed.'),
        defvalue=gui.FALSE,
        tab=gui.ADVANCED_TAB
    )
    autoNewUsbShare = gui.CheckBoxField(
        order=8,
        label=_('New USB Auto Sharing'),
        tooltip=_('Auto-redirect USB devices when plugged in.'),
        defvalue=gui.FALSE,
        tab=gui.ADVANCED_TAB
    )

    def isAvailableFor(self, userService, ip):
        '''
        Checks if the transport is available for the requested destination ip
        '''
        ready = self.cache.get(ip)
        if ready is None:
            userServiceInstance = userService.getInstance()
            con = userServiceInstance.getConsoleConnection()

            logger.debug('Connection data: {}'.format(con))

            if con is None:
                return False

            port, secure_port = con['port'], con['secure_port']
            port = -1 if port is None else port
            secure_port = -1 if secure_port is None else secure_port

            # test ANY of the ports
            port_to_test = port if port != -1 else secure_port
            if port_to_test == -1:
                self.cache.put('cachedMsg', 'Could not find the PORT for connection', 120)  # Write a message, that will be used from getCustom
                logger.info('SPICE didn\'t find has any port: {}'.format(con))
                return False

            self.cache.put('cachedMsg',
                           'Could not reach server "{}" on port "{}" from broker (prob. causes are name resolution & firewall rules)'.format(con['address'], port_to_test),
                           120)

            if connection.testServer(con['address'], port_to_test) is True:
                self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT)
                ready = 'Y'

        return ready == 'Y'

    def getCustomAvailableErrorMsg(self, userService, ip):
        msg = self.cache.get('cachedMsg')
        if msg is None:
            return Transport.getCustomAvailableErrorMsg(self, userService, ip)
        return msg

    def processedUser(self, userService, userName):
        v = self.processUserPassword(userService, userName, '')
        return v['username']

    def processUserPassword(self, service, user, password):
        username = user.getUsernameForAuth()

        if self.fixedName.value != '':
            username = self.fixedName.value

        if self.fixedPassword.value != '':
            password = self.fixedPassword.value
        if self.useEmptyCreds.isTrue():
            username, password = '', ''

        # Fix username/password acording to os manager
        username, password = service.processUserPassword(username, password)

        return {'protocol': self.protocol, 'username': username, 'password': password}

    def getConnectionInfo(self, service, user, password):
        return self.processUserPassword(service, user, password)

    def getScript(self, script):
        with open(os.path.join(os.path.dirname(__file__), script)) as f:
            data = f.read()
        return data
Example #6
0
class WinRandomPassManager(WindowsOsManager):
    typeName = _('Windows Random Password OS Manager')
    typeType = 'WinRandomPasswordManager'
    typeDescription = _('Os Manager to control windows machines, with user password set randomly.')
    iconFile = 'wosmanager.png'

    # Apart form data from windows os manager, we need also domain and credentials
    userAccount = gui.TextField(length=64, label=_('Account'), order=2, tooltip=_('User account to change password'), required=True)
    password = gui.PasswordField(length=64, label=_('Password'), order=3, tooltip=_('Current (template) password of the user account'), required=True)

    # Inherits base "onLogout"
    onLogout = WindowsOsManager.onLogout
    idle = WindowsOsManager.idle

    def __init__(self, environment, values):
        super(WinRandomPassManager, self).__init__(environment, values)
        if values is not None:
            if values['userAccount'] == '':
                raise osmanagers.OSManager.ValidationException(_('Must provide an user account!!!'))
            if values['password'] == '':
                raise osmanagers.OSManager.ValidationException(_('Must provide a password for the account!!!'))
            self._userAccount = values['userAccount']
            self._password = values['password']
        else:
            self._userAccount = ''
            self._password = ""

    def release(self, service):
        super(WinRandomPassManager, self).release(service)

    def processUserPassword(self, service, username, password):
        if username == self._userAccount:
            password = service.recoverValue('winOsRandomPass')

        return WindowsOsManager.processUserPassword(self, service, username, password)


    def genPassword(self, service):
        import random
        import string
        randomPass = service.recoverValue('winOsRandomPass')
        if randomPass is None:
            randomPass = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(16))
            service.storeValue('winOsRandomPass', randomPass)
            log.doLog(service, log.INFO, "Password set to \"{}\"".format(randomPass), log.OSMANAGER)
        return randomPass

    def infoVal(self, service):
        return 'rename:{0}\t{1}\t{2}\t{3}'.format(self.getName(service), self._userAccount, self._password, self.genPassword(service))

    def infoValue(self, service):
        return 'rename\r{0}\t{1}\t{2}\t{3}'.format(self.getName(service), self._userAccount, self._password, self.genPassword(service))

    def marshal(self):
        base = super(WinRandomPassManager, self).marshal()
        '''
        Serializes the os manager data so we can store it in database
        '''
        return '\t'.join(['v1', self._userAccount, CryptoManager.manager().encrypt(self._password), base.encode('hex')])

    def unmarshal(self, s):
        data = s.split('\t')
        if data[0] == 'v1':
            self._userAccount = data[1]
            self._password = CryptoManager.manager().decrypt(data[2])
            super(WinRandomPassManager, self).unmarshal(data[3].decode('hex'))

    def valuesDict(self):
        dic = super(WinRandomPassManager, self).valuesDict()
        dic['userAccount'] = self._userAccount
        dic['password'] = self._password
        return dic
Example #7
0
class TSNXTransport(Transport):
    """
    Provides access via NX 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
    """
    typeName = _('NX v3.5')
    typeType = 'TSNXTransport'
    typeDescription = _('NX protocol v3.5. Tunneled connection.')
    iconFile = 'nx.png'
    protocol = protocols.NX
    group = 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)
    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)

    useEmptyCreds = gui.CheckBoxField(label=_('Empty creds'), order=3, tooltip=_('If checked, the credentials used to connect will be emtpy'), tab=gui.CREDENTIALS_TAB)
    fixedName = gui.TextField(label=_('Username'), order=4, tooltip=_('If not empty, this username will be always used as credential'), tab=gui.CREDENTIALS_TAB)
    fixedPassword = gui.PasswordField(label=_('Password'), order=5, tooltip=_('If not empty, this password will be always used as credential'), tab=gui.CREDENTIALS_TAB)
    listenPort = gui.NumericField(label=_('Listening port'), length=5, order=6, tooltip=_('Listening port of NX (ssh) at client machine'), defvalue='22')
    connection = gui.ChoiceField(label=_('Connection'), order=7, tooltip=_('Connection speed for this transport (quality)'),
                                 values=[
                                     {'id': 'modem', 'text': 'modem'},
                                     {'id': 'isdn', 'text': 'isdn'},
                                     {'id': 'adsl', 'text': 'adsl'},
                                     {'id': 'wan', 'text': 'wan'},
                                     {'id': 'lan', 'text': 'lan'},
    ], tab=gui.PARAMETERS_TAB)
    session = gui.ChoiceField(label=_('Session'), order=8, tooltip=_('Desktop session'),
                              values=[
                                  {'id': 'gnome', 'text': 'gnome'},
                                  {'id': 'kde', 'text': 'kde'},
                                  {'id': 'cde', 'text': 'cde'},
    ], tab=gui.PARAMETERS_TAB)
    cacheDisk = gui.ChoiceField(label=_('Disk Cache'), order=9, tooltip=_('Cache size en Mb stored at disk'),
                                values=[
                                    {'id': '0', 'text': '0 Mb'},
                                    {'id': '32', 'text': '32 Mb'},
                                    {'id': '64', 'text': '64 Mb'},
                                    {'id': '128', 'text': '128 Mb'},
                                    {'id': '256', 'text': '256 Mb'},
                                    {'id': '512', 'text': '512 Mb'},
    ], tab=gui.PARAMETERS_TAB)
    cacheMem = gui.ChoiceField(label=_('Memory Cache'), order=10, tooltip=_('Cache size en Mb kept at memory'),
                               values=[
                                   {'id': '4', 'text': '4 Mb'},
                                   {'id': '8', 'text': '8 Mb'},
                                   {'id': '16', 'text': '16 Mb'},
                                   {'id': '32', 'text': '32 Mb'},
                                   {'id': '64', 'text': '64 Mb'},
                                   {'id': '128', 'text': '128 Mb'},
    ], tab=gui.PARAMETERS_TAB)

    def __init__(self, environment, values=None):
        super(TSNXTransport, self).__init__(environment, values)
        if values is not None:
            if values['tunnelServer'].find(':') == -1:
                raise Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))
            self._tunnelServer = values['tunnelServer']
            self._tunnelCheckServer = values['tunnelCheckServer']
            self._useEmptyCreds = gui.strToBool(values['useEmptyCreds'])
            self._fixedName = values['fixedName']
            self._fixedPassword = values['fixedPassword']
            self._listenPort = values['listenPort']
            self._connection = values['connection']
            self._session = values['session']
            self._cacheDisk = values['cacheDisk']
            self._cacheMem = values['cacheMem']
        else:
            self._tunnelServer = ''
            self._tunnelCheckServer = ''
            self._useEmptyCreds = ''
            self._fixedName = ''
            self._fixedPassword = ''
            self._listenPort = ''
            self._connection = ''
            self._session = ''
            self._cacheDisk = ''
            self._cacheMem = ''

    def marshal(self):
        """
        Serializes the transport data so we can store it in database
        """
        return str.join('\t', ['v1', gui.boolToStr(self._useEmptyCreds), self._fixedName, self._fixedPassword, self._listenPort,
                               self._connection, self._session, self._cacheDisk, self._cacheMem, self._tunnelServer, self._tunnelCheckServer])

    def unmarshal(self, string):
        data = string.split('\t')
        if data[0] == 'v1':
            self._useEmptyCreds = gui.strToBool(data[1])
            self._fixedName, self._fixedPassword, self._listenPort, self._connection, self._session, self._cacheDisk, self._cacheMem, self._tunnelServer, self._tunnelCheckServer = data[2:]

    def valuesDict(self):
        return {
            'useEmptyCreds': gui.boolToStr(self._useEmptyCreds),
            'fixedName': self._fixedName,
            'fixedPassword': self._fixedPassword,
            'listenPort': self._listenPort,
            'connection': self._connection,
            'session': self._session,
            'cacheDisk': self._cacheDisk,
            'cacheMem': self._cacheMem,
            'tunnelServer': self._tunnelServer,
            'tunnelCheckServer': self._tunnelCheckServer
        }

    def isAvailableFor(self, userService, ip):
        """
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        """
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache.get(ip)
        if ready is None:
            # Check again for readyness
            if self.testServer(userService, ip, self._listenPort) is True:
                self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache.put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def getScript(self, script):
        with open(os.path.join(os.path.dirname(__file__), script)) as f:
            data = f.read()
        return data

    def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
        prefs = user.prefs('nx')

        username = user.getUsernameForAuth()
        proc = username.split('@')
        username = proc[0]
        if self._fixedName is not '':
            username = self._fixedName
        if self._fixedPassword is not '':
            password = self._fixedPassword
        if self._useEmptyCreds is True:
            username, password = '', ''

        tunpass = ''.join(random.choice(string.letters + string.digits) for _i in range(12))
        tunuser = TicketStore.create(tunpass)

        sshServer = self._tunnelServer
        if ':' not in sshServer:
            sshServer += ':443'

        sshHost, sshPort = sshServer.split(':')

        logger.debug('Username generated: {0}, password: {1}'.format(tunuser, tunpass))

        width, height = CommonPrefs.getWidthHeight(prefs)
        # Fix username/password acording to os manager
        username, password = userService.processUserPassword(username, password)

        m = {
            'ip': ip,
            'tunUser': tunuser,
            'tunPass': tunpass,
            'tunHost': sshHost,
            'tunPort': sshPort,
            'password': password,
            'port': self._listenPort
        }

        r = NXFile(username=username, password=password, width=width, height=height)
        r.host = '{address}'
        r.port = '{port}'
        r.connection = self._connection
        r.desktop = self._session
        r.cachedisk = self._cacheDisk
        r.cachemem = self._cacheMem

        os = {
            OsDetector.Windows: 'windows',
            OsDetector.Linux: 'linux',
            OsDetector.Macintosh: 'macosx'

        }.get(os['OS'])

        if os is None:
            return super(self.__class__, self).getUDSTransportScript(userService, transport, ip, os, user, password, request)

        return self.getScript('scripts/{}/tunnel.py'.format(os)).format(
            r=r,
            m=DictAsObj(m),
        )
Example #8
0
class TX2GOTransport(BaseX2GOTransport):
    '''
    Provides access via SPICE 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
    '''
    typeName = _('X2Go Transport Experimental (tunneled)')
    typeType = 'TX2GOTransport'
    typeDescription = _('X2Go Transport for tunneled connection (EXPERIMENTAL)')
    group = 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)

    fixedName = BaseX2GOTransport.fixedName
    # fullScreen = BaseX2GOTransport.fullScreen
    desktopType = BaseX2GOTransport.desktopType
    sound = BaseX2GOTransport.sound
    exports = BaseX2GOTransport.exports
    speed = BaseX2GOTransport.speed

    soundType = BaseX2GOTransport.soundType
    keyboardLayout = BaseX2GOTransport.keyboardLayout
    pack = BaseX2GOTransport.pack
    quality = BaseX2GOTransport.quality


    def initialize(self, values):
        if values is not None:
            if values['tunnelServer'].count(':') != 1:
                raise BaseX2GOTransport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))

    def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
        prefs = user.prefs('nx')

        ci = self.getConnectionInfo(userService, user, password)
        username = ci['username']

        priv, pub = self.getAndPushKey(username, userService)

        width, height = CommonPrefs.getWidthHeight(prefs)

        logger.debug('')

        xf = x2gofile.getTemplate(
            speed=self.speed.value,
            pack=self.pack.value,
            quality=self.quality.value,
            sound=self.sound.isTrue(),
            soundSystem=self.sound.value,
            windowManager=self.desktopType.value,
            exports=self.exports.isTrue(),
            width=width,
            height=height,
            user=username
        )

        tunpass = ''.join(random.choice(string.letters + string.digits) for _i in range(12))
        tunuser = TicketStore.create(tunpass)

        sshHost, sshPort = self.tunnelServer.value.split(':')

        # data
        data = {
            'os': os['OS'],
            'ip': ip,
            'port': 22,
            'tunUser': tunuser,
            'tunPass': tunpass,
            'tunHost': sshHost,
            'tunPort': sshPort,
            'username': username,
            'key': priv,
            'width': width,
            'height': height,
            'printers': True,
            'drives': self.exports.isTrue(),
            'fullScreen': width == -1 or height == -1,
            'this_server': request.build_absolute_uri('/'),
            'xf': xf
        }

        m = tools.DictAsObj(data)

        os = {
            OsDetector.Windows: 'windows',
            OsDetector.Linux: 'linux',
            # OsDetector.Macintosh: 'macosx'
        }.get(m.os)

        if os is None:
            return super(self.__class__, self).getUDSTransportScript(userService, transport, ip, os, user, password, request)

        return self.getScript('scripts/{}/tunnel.py'.format(os)).format(m=m)
Example #9
0
class TSNXTransport(Transport):
    '''
    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
    '''
    typeName = _('NX Transport (tunneled)')
    typeType = 'TSNXTransport'
    typeDescription = _('NX Transport for tunneled connection')
    iconFile = 'nx.png'
    needsJava = True  # If this transport needs java for rendering
    supportedOss = ['Windows', 'Macintosh', 'Linux']
    protocol = protocols.NX

    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)'))
    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)'))

    useEmptyCreds = gui.CheckBoxField(label=_('Empty creds'), order=3, tooltip=_('If checked, the credentials used to connect will be emtpy'))
    fixedName = gui.TextField(label=_('Username'), order=4, tooltip=_('If not empty, this username will be always used as credential'))
    fixedPassword = gui.PasswordField(label=_('Password'), order=5, tooltip=_('If not empty, this password will be always used as credential'))
    listenPort = gui.NumericField(label=_('Listening port'), length=5, order=6, tooltip=_('Listening port of NX (ssh) at client machine'), defvalue='22')
    connection = gui.ChoiceField(label=_('Connection'), order=7, tooltip=_('Connection speed for this transport (quality)'),
                                 values=[
                                     {'id': 'modem', 'text': 'modem'},
                                     {'id': 'isdn', 'text': 'isdn'},
                                     {'id': 'adsl', 'text': 'adsl'},
                                     {'id': 'wan', 'text': 'wan'},
                                     {'id': 'lan', 'text': 'lan'},
    ])
    session = gui.ChoiceField(label=_('Session'), order=8, tooltip=_('Desktop session'),
                              values=[
                                  {'id': 'gnome', 'text': 'gnome'},
                                  {'id': 'kde', 'text': 'kde'},
                                  {'id': 'cde', 'text': 'cde'},
    ])
    cacheDisk = gui.ChoiceField(label=_('Disk Cache'), order=9, tooltip=_('Cache size en Mb stored at disk'),
                                values=[
                                    {'id': '0', 'text': '0 Mb'},
                                    {'id': '32', 'text': '32 Mb'},
                                    {'id': '64', 'text': '64 Mb'},
                                    {'id': '128', 'text': '128 Mb'},
                                    {'id': '256', 'text': '256 Mb'},
                                    {'id': '512', 'text': '512 Mb'},
    ])
    cacheMem = gui.ChoiceField(label=_('Memory Cache'), order=10, tooltip=_('Cache size en Mb kept at memory'),
                               values=[
                                   {'id': '4', 'text': '4 Mb'},
                                   {'id': '8', 'text': '8 Mb'},
                                   {'id': '16', 'text': '16 Mb'},
                                   {'id': '32', 'text': '32 Mb'},
                                   {'id': '64', 'text': '64 Mb'},
                                   {'id': '128', 'text': '128 Mb'},
    ])

    def __init__(self, environment, values=None):
        super(TSNXTransport, self).__init__(environment, values)
        if values != None:
            if values['tunnelServer'].find(':') == -1:
                raise Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))
            self._tunnelServer = values['tunnelServer']
            self._tunnelCheckServer = values['tunnelCheckServer']
            self._useEmptyCreds = gui.strToBool(values['useEmptyCreds'])
            self._fixedName = values['fixedName']
            self._fixedPassword = values['fixedPassword']
            self._listenPort = values['listenPort']
            self._connection = values['connection']
            self._session = values['session']
            self._cacheDisk = values['cacheDisk']
            self._cacheMem = values['cacheMem']
        else:
            self._tunnelServer = ''
            self._tunnelCheckServer = ''
            self._useEmptyCreds = ''
            self._fixedName = ''
            self._fixedPassword = ''
            self._listenPort = ''
            self._connection = ''
            self._session = ''
            self._cacheDisk = ''
            self._cacheMem = ''

    def marshal(self):
        '''
        Serializes the transport data so we can store it in database
        '''
        return str.join('\t', [ 'v1', gui.boolToStr(self._useEmptyCreds), self._fixedName, self._fixedPassword, self._listenPort,
                                self._connection, self._session, self._cacheDisk, self._cacheMem, self._tunnelServer, self._tunnelCheckServer ])

    def unmarshal(self, string):
        data = string.split('\t')
        if data[0] == 'v1':
            self._useEmptyCreds = gui.strToBool(data[1])
            self._fixedName, self._fixedPassword, self._listenPort, self._connection, self._session, self._cacheDisk, self._cacheMem, self._tunnelServer, self._tunnelCheckServer = data[2:]

    def valuesDict(self):
        return {
            'useEmptyCreds': gui.boolToStr(self._useEmptyCreds),
            'fixedName': self._fixedName,
            'fixedPassword': self._fixedPassword,
            'listenPort': self._listenPort,
            'connection': self._connection,
            'session': self._session,
            'cacheDisk': self._cacheDisk,
            'cacheMem': self._cacheMem,
            'tunnelServer': self._tunnelServer,
            'tunnelCheckServer': self._tunnelCheckServer
        }

    def isAvailableFor(self, ip):
        '''
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        '''
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache().get(ip)
        if ready is None:
            # Check again for readyness
            if connection.testServer(ip, self._listenPort) is True:
                self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache().put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def renderForHtml(self, userService, transport, ip, os, user, password):

        prefs = user.prefs('nx')

        username = user.getUsernameForAuth()
        proc = username.split('@')
        username = proc[0]
        if self._fixedName is not '':
            username = self._fixedName
        if self._fixedPassword is not '':
            password = self._fixedPassword
        if self._useEmptyCreds is True:
            username, password = '', ''

        width, height = CommonPrefs.getWidthHeight(prefs)
        cache = Cache('pam')

        tunuser = ''.join(random.choice(string.letters + string.digits) for _i in range(12)) + ("%f" % time.time()).split('.')[1]
        tunpass = ''.join(random.choice(string.letters + string.digits) for _i in range(12))
        cache.put(tunuser, tunpass, 60 * 10)  # Credential valid for ten minutes, and for 1 use only

        sshHost, sshPort = self._tunnelServer.split(':')

        logger.debug('Username generated: {0}, password: {1}'.format(tunuser, tunpass))
        tun = "{0} {1} {2} {3} {4} {5} {6}".format(tunuser, tunpass, sshHost, sshPort, ip, self._listenPort, '9')

        # Extra data
        extra = {
            'width': width,
            'height': height,
            'connection': self._connection,
            'session': self._session,
            'cacheDisk': self._cacheDisk,
            'cacheMem': self._cacheMem,
            'tun': tun
        }

        # Fix username/password acording to os manager
        username, password = userService.processUserPassword(username, password)

        return generateHtmlForNX(self, userService.uuid, transport.uuid, os, username, password, extra)

    def getHtmlComponent(self, theId, os, componentId):
        # We use helper to keep this clean
        return getHtmlComponent(self.__module__, componentId)
Example #10
0
class WinDomainOsManager(WindowsOsManager):
    typeName = _('Windows Domain OS Manager')
    typeType = 'WinDomainManager'
    typeDescription = _('Os Manager to control windows machines with domain.')
    iconFile = 'wosmanager.png'

    # Apart form data from windows os manager, we need also domain and credentials
    domain = gui.TextField(
        length=64,
        label=_('Domain'),
        order=1,
        tooltip=
        _('Domain to join machines to (use FQDN form, Netbios name not supported for most operations)'
          ),
        required=True)
    account = gui.TextField(
        length=64,
        label=_('Account'),
        order=2,
        tooltip=_('Account with rights to add machines to domain'),
        required=True)
    password = gui.PasswordField(length=64,
                                 label=_('Password'),
                                 order=3,
                                 tooltip=_('Password of the account'),
                                 required=True)
    ou = gui.TextField(
        length=64,
        label=_('OU'),
        order=4,
        tooltip=
        _('Organizational unit where to add machines in domain (check it before using it). i.e.: ou=My Machines,dc=mydomain,dc=local'
          ))
    grp = gui.TextField(
        length=64,
        label=_('Machine Group'),
        order=7,
        tooltip=
        _('Group to which add machines on creation. If empty, no group will be used. (experimental)'
          ),
        tab=_('Advanced'))
    removeOnExit = gui.CheckBoxField(
        label=_('Machine clean'),
        order=8,
        tooltip=
        _('If checked, UDS will try to remove the machine from the domain USING the provided credentials'
          ),
        tab=_('Advanced'),
        defvalue=gui.TRUE)
    serverHint = gui.TextField(
        length=64,
        label=_('Server Hint'),
        order=9,
        tooltip=_('In case of several AD servers, which one is preferred'),
        tab=_('Advanced'))
    ssl = gui.CheckBoxField(
        label=_('Use SSL'),
        order=10,
        tooltip=_(
            'If checked,  a ssl connection to Active Directory will be used'),
        tab=_('Advanced'))

    # Inherits base "onLogout"
    onLogout = WindowsOsManager.onLogout
    idle = WindowsOsManager.idle

    def __init__(self, environment, values):
        super(WinDomainOsManager, self).__init__(environment, values)
        if values is not None:
            if values['domain'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide a domain!'))
            # if values['domain'].find('.') == -1:
            #    raise osmanagers.OSManager.ValidationException(_('Must provide domain in FQDN'))
            if values['account'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide an account to add machines to domain!'))
            if values['account'].find('\\') != -1:
                raise osmanagers.OSManager.ValidationException(
                    _('DOM\\USER form is not allowed!'))
            if values['password'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide a password for the account!'))
            self._domain = values['domain']
            self._ou = values['ou'].strip()
            self._account = values['account']
            self._password = values['password']
            self._group = values['grp'].strip()
            self._serverHint = values['serverHint'].strip()
            self._ssl = 'y' if values['ssl'] else 'n'
            self._removeOnExit = 'y' if values['removeOnExit'] else 'n'
        else:
            self._domain = ""
            self._ou = ""
            self._account = ""
            self._password = ""
            self._group = ""
            self._serverHint = ""
            self._removeOnExit = 'n'
            self._ssl = 'n'

        # self._ou = self._ou.replace(' ', ''), do not remove spaces
        if self._domain != '' and self._ou != '':
            lpath = 'dc=' + ',dc='.join(
                (s.lower() for s in self._domain.split('.')))
            if self._ou.lower().find(lpath) == -1:
                self._ou += ',' + lpath

    def __getServerList(self):
        if self._serverHint != '':
            yield (self._serverHint, 389)

        for server in reversed(
                sorted(dns.resolver.query('_ldap._tcp.' + self._domain, 'SRV'),
                       key=lambda i: i.priority * 10000 + i.weight)):
            yield (six.text_type(server.target)[:-1], server.port)

    def __connectLdap(self, servers=None):
        """
        Tries to connect to LDAP
        Raises an exception if not found:
            dns.resolver.NXDOMAIN
            ldaputil.LDAPError
        """
        if servers is None:
            servers = self.__getServerList()

        account = self._account
        if account.find('@') == -1:
            account += '@' + self._domain

        _str = "No servers found"
        # And if not possible, try using NON-SSL
        for server in servers:
            port = server[1] if self._ssl != 'y' else -1
            ssl = self._ssl == 'y'
            try:
                return ldaputil.connection(account,
                                           self._password,
                                           server[0],
                                           port,
                                           ssl=ssl,
                                           timeout=10,
                                           debug=False)
            except Exception as e:
                _str = 'Error: {}'.format(e)

        raise ldaputil.LDAPError(_str)

    def __getGroup(self, l):
        base = ','.join(['DC=' + i for i in self._domain.split('.')])
        group = ldaputil.escape(self._group)
        try:
            obj = next(
                ldaputil.getAsDict(
                    l,
                    base,
                    "(&(objectClass=group)(|(cn={0})(sAMAccountName={0})))".
                    format(group), ['dn'],
                    sizeLimit=50))
        except StopIteration:
            obj = None

        if obj is None:
            return None

        return obj['dn']  # Returns the DN

    def __getMachine(self, l, machineName):
        # if self._ou:
        #     base = self._ou
        # else:
        base = ','.join(['DC=' + i for i in self._domain.split('.')])

        fltr = '(&(objectClass=computer)(sAMAccountName={}$))'.format(
            ldaputil.escape(machineName))
        try:
            obj = next(ldaputil.getAsDict(l, base, fltr, ['dn'], sizeLimit=50))
        except StopIteration:
            obj = None

        if obj is None:
            return None

        return obj['dn']  # Returns the DN

    def readyReceived(self, userService, data):
        # No group to add
        if self._group == '':
            return

        if not '.' in self._domain:
            logger.info(
                'Adding to a group for a non FQDN domain is not supported')
            return

        # The machine is on a AD for sure, and maybe they are not already sync
        servers = list(self.__getServerList())

        error = None
        for s in servers:
            try:
                l = self.__connectLdap(servers=(s, ))

                machine = self.__getMachine(l, userService.friendly_name)
                group = self.__getGroup(l)
                # #
                # Direct LDAP operation "modify", maybe this need to be added to ldaputil? :)
                # #
                l.modify_s(group, ((ldap.MOD_ADD, 'member',
                                    machine), ))  # @UndefinedVariable
                error = None
                break
            except dns.resolver.NXDOMAIN:  # No domain found, log it and pass
                logger.warning('Could not find _ldap._tcp.' + self._domain)
                log.doLog(
                    userService, log.WARN,
                    "Could not remove machine from domain (_ldap._tcp.{0} not found)"
                    .format(self._domain), log.OSMANAGER)
            except ldap.ALREADY_EXISTS:  # @UndefinedVariable
                # Already added this machine to this group, pass
                error = None
                break
            except ldaputil.LDAPError:
                logger.exception('Ldap Exception caught')
                error = "Could not add machine (invalid credentials? for {0})".format(
                    self._account)
            except Exception as e:
                error = "Could not add machine {} to group {}: {}".format(
                    userService.friendly_name, self._group, e)
                # logger.exception('Ldap Exception caught')

        if error is not None:
            log.doLog(userService, log.WARN, error, log.OSMANAGER)
            logger.error(error)

    def release(self, service):
        """
        service is a db user service object
        """
        super(WinDomainOsManager, self).release(service)

        # If no removal requested, just return
        if self._removeOnExit != 'y':
            return

        if not '.' in self._domain:
            logger.info('Releasing from a not FQDN domain is not supported')
            return

        try:
            l = self.__connectLdap()
        except dns.resolver.NXDOMAIN:  # No domain found, log it and pass
            logger.warning('Could not find _ldap._tcp.' + self._domain)
            log.doLog(
                service, log.WARN,
                "Could not remove machine from domain (_ldap._tcp.{0} not found)"
                .format(self._domain), log.OSMANAGER)
            return
        except ldaputil.LDAPError:
            logger.exception('Ldap Exception caught')
            log.doLog(
                service, log.WARN,
                "Could not remove machine from domain (invalid credentials for {0})"
                .format(self._account), log.OSMANAGER)
            return
        except Exception:
            logger.exception('Exception caught')
            return

        try:
            res = self.__getMachine(l, service.friendly_name)
            if res is None:
                raise Exception(
                    'Machine {} not found on AD (permissions?)'.format(
                        service.friendly_name))
            ldaputil.recursive_delete(l, res)
        except IndexError:
            logger.error('Error deleting {} from BASE {}'.format(
                service.friendly_name, self._ou))
        except Exception:
            logger.exception('Deleting from AD: ')

    def check(self):
        try:
            l = self.__connectLdap()
        except ldaputil.LDAPError as e:
            return _('Check error: {0}').format(self.__getLdapError(e))
        except dns.resolver.NXDOMAIN:
            return [
                True,
                _('Could not find server parameters (_ldap._tcp.{0} can\'t be resolved)'
                  ).format(self._domain)
            ]
        except Exception as e:
            logger.exception('Exception ')
            return [False, str(e)]

        try:
            l.search_st(self._ou, ldap.SCOPE_BASE)  # @UndefinedVariable
        except ldaputil.LDAPError as e:
            return _('Check error: {0}').format(self.__getLdapError(e))

        # Group
        if self._group != '':
            if self.__getGroup(l) is None:
                return _(
                    'Check Error: group "{}" not found (using "cn" to locate it)'
                ).format(self._group)

        return _('Server check was successful')

    @staticmethod
    def test(env, data):
        logger.debug('Test invoked')
        wd = None
        try:
            wd = WinDomainOsManager(env, data)
            logger.debug(wd)
            try:
                l = wd.__connectLdap()
            except ldaputil.LDAPError as e:
                return [
                    False,
                    _('Could not access AD using LDAP ({0})').format(
                        wd.__getLdapError(e))
                ]

            ou = wd._ou
            if ou == '':
                ou = 'cn=Computers,dc=' + ',dc='.join(wd._domain.split('.'))

            logger.debug('Checking {0} with ou {1}'.format(wd._domain, ou))
            r = l.search_st(ou, ldap.SCOPE_BASE)  # @UndefinedVariable
            logger.debug('Result of search: {0}'.format(r))

        except ldaputil.LDAPError:
            if wd._ou == '':
                return [
                    False,
                    _('The default path {0} for computers was not found!!!').
                    format(wd._ou)
                ]
            else:
                return [
                    False,
                    _('The ou path {0} was not found!!!').format(wd._ou)
                ]
        except dns.resolver.NXDOMAIN:
            return [
                True,
                _('Could not check parameters (_ldap._tcp.{0} can\'r be resolved)'
                  ).format(wd._domain)
            ]
        except Exception as e:
            logger.exception('Exception ')
            return [False, str(e)]

        return [True, _("All parameters seem to work fine.")]

    def infoVal(self, service):
        return 'domain:{0}\t{1}\t{2}\t{3}\t{4}'.format(self.getName(service),
                                                       self._domain, self._ou,
                                                       self._account,
                                                       self._password)

    def infoValue(self, service):
        return 'domain\r{0}\t{1}\t{2}\t{3}\t{4}'.format(
            self.getName(service), self._domain, self._ou, self._account,
            self._password)

    def marshal(self):
        base = super(WinDomainOsManager, self).marshal()
        '''
        Serializes the os manager data so we can store it in database
        '''
        return '\t'.join([
            'v4', self._domain, self._ou, self._account,
            CryptoManager.manager().encrypt(self._password),
            encoders.encode(base, 'hex', asText=True), self._group,
            self._serverHint, self._ssl, self._removeOnExit
        ]).encode('utf8')

    def unmarshal(self, s):
        data = s.decode('utf8').split('\t')
        if data[0] in ('v1', 'v2', 'v3', 'v4'):
            self._domain = data[1]
            self._ou = data[2]
            self._account = data[3]
            self._password = CryptoManager.manager().decrypt(data[4])

        if data[0] in ('v2', 'v3', 'v4'):
            self._group = data[6]
        else:
            self._group = ''

        if data[0] in ('v3', 'v4'):
            self._serverHint = data[7]
        else:
            self._serverHint = ''

        if data[0] == 'v4':
            self._ssl = data[8]
            self._removeOnExit = data[9]
        else:
            self._ssl = 'n'
            self._removeOnExit = 'y'

        super(WinDomainOsManager,
              self).unmarshal(encoders.decode(data[5], 'hex'))

    def valuesDict(self):
        dct = super(WinDomainOsManager, self).valuesDict()
        dct['domain'] = self._domain
        dct['ou'] = self._ou
        dct['account'] = self._account
        dct['password'] = self._password
        dct['grp'] = self._group
        dct['serverHint'] = self._serverHint
        dct['ssl'] = self._ssl == 'y'
        dct['removeOnExit'] = self._removeOnExit == 'y'
        return dct
Example #11
0
class SimpleLDAPAuthenticator(Authenticator):

    host = gui.TextField(length=64,
                         label=_('Host'),
                         order=1,
                         tooltip=_('Ldap Server IP or Hostname'),
                         required=True)
    port = gui.NumericField(
        length=5,
        label=_('Port'),
        defvalue='389',
        order=2,
        tooltip=_('Ldap port (usually 389 for non ssl and 636 for ssl)'),
        required=True)
    ssl = gui.CheckBoxField(
        label=_('Use SSL'),
        order=3,
        tooltip=
        _('If checked, the connection will be ssl, using port 636 instead of 389'
          ))
    username = gui.TextField(
        length=64,
        label=_('Ldap User'),
        order=4,
        tooltip=_('Username with read privileges on the base selected'),
        required=True,
        tab=gui.CREDENTIALS_TAB)
    password = gui.PasswordField(lenth=32,
                                 label=_('Password'),
                                 order=5,
                                 tooltip=_('Password of the ldap user'),
                                 required=True,
                                 tab=gui.CREDENTIALS_TAB)
    timeout = gui.NumericField(
        length=3,
        label=_('Timeout'),
        defvalue='10',
        order=6,
        tooltip=_('Timeout in seconds of connection to LDAP'),
        required=True,
        minValue=1)
    ldapBase = gui.TextField(
        length=64,
        label=_('Base'),
        order=7,
        tooltip=_('Common search base (used for "users" and "groups")'),
        required=True,
        tab=_('Ldap info'))
    userClass = gui.TextField(
        length=64,
        label=_('User class'),
        defvalue='posixAccount',
        order=8,
        tooltip=_('Class for LDAP users (normally posixAccount)'),
        required=True,
        tab=_('Ldap info'))
    userIdAttr = gui.TextField(
        length=64,
        label=_('User Id Attr'),
        defvalue='uid',
        order=9,
        tooltip=_('Attribute that contains the user id'),
        required=True,
        tab=_('Ldap info'))
    userNameAttr = gui.TextField(
        length=64,
        label=_('User Name Attr'),
        defvalue='uid',
        order=10,
        tooltip=
        _('Attributes that contains the user name (list of comma separated values)'
          ),
        required=True,
        tab=_('Ldap info'))
    groupClass = gui.TextField(
        length=64,
        label=_('Group class'),
        defvalue='posixGroup',
        order=11,
        tooltip=_('Class for LDAP groups (normally poxisGroup)'),
        required=True,
        tab=_('Ldap info'))
    groupIdAttr = gui.TextField(
        length=64,
        label=_('Group Id Attr'),
        defvalue='cn',
        order=12,
        tooltip=_('Attribute that contains the group id'),
        required=True,
        tab=_('Ldap info'))
    memberAttr = gui.TextField(
        length=64,
        label=_('Group membership attr'),
        defvalue='memberUid',
        order=13,
        tooltip=_(
            'Attribute of the group that contains the users belonging to it'),
        required=True,
        tab=_('Ldap info'))

    typeName = _('SimpleLDAP Authenticator')
    typeType = 'SimpleLdapAuthenticator'
    typeDescription = _('Simple LDAP authenticator')
    iconFile = 'auth.png'

    # If it has and external source where to get "new" users (groups must be declared inside UDS)
    isExternalSource = True
    # If we need to enter the password for this user
    needsPassword = False
    # Label for username field
    userNameLabel = _('Username')
    # Label for group field
    groupNameLabel = _("Group")
    # Label for password field
    passwordLabel = _("Password")

    def __init__(self, dbAuth, environment, values=None):
        super(SimpleLDAPAuthenticator, self).__init__(dbAuth, environment,
                                                      values)
        if values is not None:
            self._host = values['host']
            self._port = values['port']
            self._ssl = gui.strToBool(values['ssl'])
            self._username = values['username']
            self._password = values['password']
            self._timeout = values['timeout']
            self._ldapBase = values['ldapBase']
            self._userClass = values['userClass']
            self._groupClass = values['groupClass']
            self._userIdAttr = values['userIdAttr']
            self._groupIdAttr = values['groupIdAttr']
            self._memberAttr = values['memberAttr']
            self._userNameAttr = values['userNameAttr'].replace(
                ' ', '')  # Removes white spaces
        else:
            self._host = None
            self._port = None
            self._ssl = None
            self._username = None
            self._password = None
            self._timeout = None
            self._ldapBase = None
            self._userClass = None
            self._groupClass = None
            self._userIdAttr = None
            self._groupIdAttr = None
            self._memberAttr = None
            self._userNameAttr = None
        self._connection = None

    def valuesDict(self):
        return {
            'host': self._host,
            'port': self._port,
            'ssl': gui.boolToStr(self._ssl),
            'username': self._username,
            'password': self._password,
            'timeout': self._timeout,
            'ldapBase': self._ldapBase,
            'userClass': self._userClass,
            'groupClass': self._groupClass,
            'userIdAttr': self._userIdAttr,
            'groupIdAttr': self._groupIdAttr,
            'memberAttr': self._memberAttr,
            'userNameAttr': self._userNameAttr
        }

    def __str__(self):
        return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, groupClass = {6}, userIdAttr = {7}, groupIdAttr = {8}, memberAttr = {9}, userName attr = {10}".format(
            self._username, self._password, self._host, self._port,
            self._ldapBase, self._userClass, self._groupClass,
            self._userIdAttr, self._groupIdAttr, self._memberAttr,
            self._userNameAttr)

    def marshal(self):
        return '\t'.join([
            'v1', self._host, self._port,
            gui.boolToStr(self._ssl), self._username, self._password,
            self._timeout, self._ldapBase, self._userClass, self._groupClass,
            self._userIdAttr, self._groupIdAttr, self._memberAttr,
            self._userNameAttr
        ])

    def unmarshal(self, str_):
        data = str_.split('\t')
        if data[0] == 'v1':
            logger.debug("Data: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._groupClass, self._userIdAttr, self._groupIdAttr, self._memberAttr, self._userNameAttr = data[
                1:]
            self._ssl = gui.strToBool(self._ssl)

    def __connection(self, username=None, password=None):
        if self._connection is None or username is not None:  # We want this method also to check credentials
            if isinstance(username, six.text_type):
                username = username.encode('utf8')
            if isinstance(password, six.text_type):
                password = password.encode('utf8')
            l = None
            cache = False
            try:
                # ldap.set_option(ldap.OPT_DEBUG_LEVEL, 9)
                ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,
                                ldap.OPT_X_TLS_NEVER)
                schema = self._ssl and 'ldaps' or 'ldap'
                port = self._port != '389' and ':' + self._port or ''
                uri = "%s://%s%s" % (schema, self._host, port)
                logger.debug('Ldap uri: {0}'.format(uri))
                l = ldap.initialize(uri=uri)
                l.network_timeout = l.timeout = int(self._timeout)
                l.protocol_version = ldap.VERSION3

                if username is None:
                    cache = True
                    username = self._username
                    password = self._password

                l.simple_bind_s(who=username, cred=password)
            except ldap.LDAPError as e:
                str_ = _('Ldap connection error: ')
                if isinstance(e.message, dict):
                    str_ += ', '.join(e.message.get('info', ''),
                                      e.message.get('desc'))
                else:
                    str_ += six.text_type(e)
                raise Exception(str_)
            if cache is True:
                self._connection = l
            else:
                return l  # Do not cache nor overwrite "global" connection
        return self._connection

    def __getUser(self, username):
        try:
            con = self.__connection()
            filter_ = '(&(objectClass=%s)(%s=%s))' % (
                self._userClass, self._userIdAttr,
                ldap.filter.escape_filter_chars(username, 0))
            attrlist = [
                i.encode('utf-8')
                for i in self._userNameAttr.split(',') + [self._userIdAttr]
            ]
            logger.debug('Getuser filter_: {0}, attr list: {1}'.format(
                filter_, attrlist))
            res = con.search_ext_s(base=self._ldapBase,
                                   scope=ldap.SCOPE_SUBTREE,
                                   filterstr=filter_,
                                   attrlist=attrlist,
                                   sizelimit=LDAP_RESULT_LIMIT)[0]
            usr = dict((k, '') for k in attrlist)
            usr.update(res[1])
            usr.update({'dn': res[0], '_id': username})
            logger.debug('Usr: {0}'.format(usr))
            return usr
        except Exception:
            logger.exception('Exception:')
            return None

    def __getGroup(self, groupName):
        try:
            con = self.__connection()
            filter_ = '(&(objectClass=%s)(%s=%s))' % (
                self._groupClass, self._groupIdAttr, groupName)
            attrlist = [self._memberAttr.encode('utf-8')]
            logger.debug('Getgroup filter_: {0}, attr list {1}'.format(
                filter_, attrlist))
            res = con.search_ext_s(base=self._ldapBase,
                                   scope=ldap.SCOPE_SUBTREE,
                                   filterstr=filter_,
                                   attrlist=attrlist,
                                   sizelimit=LDAP_RESULT_LIMIT * 10)[0]
            grp = dict((k, ['']) for k in attrlist)
            grp.update(res[1])
            grp.update({'dn': res[0], '_id': groupName})
            logger.debug('Group: {0}'.format(grp))
            return grp
        except Exception:
            logger.exception('Exception:')
            return None

    def __getGroups(self, usr):
        try:
            con = self.__connection()
            filter_ = '(&(objectClass=%s)(|(%s=%s)(%s=%s)))' % (
                self._groupClass, self._memberAttr, usr['_id'],
                self._memberAttr, usr['dn'])
            logger.debug('Filter: {0}'.format(filter_))
            res = con.search_ext_s(base=self._ldapBase,
                                   scope=ldap.SCOPE_SUBTREE,
                                   filterstr=filter_,
                                   attrlist=[self._groupIdAttr.encode('utf8')],
                                   sizelimit=LDAP_RESULT_LIMIT * 10)
            groups = {}
            for g in res:
                v = g[1][self._groupIdAttr]
                if type(v) is not list:
                    v = [v]
                for gg in v:
                    groups[str(gg)] = g[0]
            logger.debug('Groups: {0}'.format(groups))
            return groups

        except Exception:
            logger.exception('Exception at __getGroups')
            return {}

    def __getUserRealName(self, usr):
        '''
        Tries to extract the real name for this user. Will return all atttributes (joint)
        specified in _userNameAttr (comma separated).
        '''
        return ' '.join([(type(usr.get(id_, '')) is list and ' '.join(
            (str(k) for k in usr.get(id_, ''))) or str(usr.get(id_, '')))
                         for id_ in self._userNameAttr.split(',')]).strip()

    def authenticate(self, username, credentials, groupsManager):
        '''
        Must authenticate the user.
        We can have to different situations here:
           1.- The authenticator is external source, what means that users may be unknown to system before callig this
           2.- The authenticator isn't external source, what means that users have been manually added to system and are known before this call
        We receive the username, the credentials used (normally password, but can be a public key or something related to pk) and a group manager.
        The group manager is responsible for letting know the authenticator which groups we currently has active.
        @see: uds.core.auths.GroupsManager
        '''
        try:
            # Locate the user at LDAP
            usr = self.__getUser(username)

            if usr is None:
                return False

            # Let's see first if it credentials are fine
            self.__connection(
                usr['dn'],
                credentials)  # Will raise an exception if it can't connect

            groupsManager.validate(self.__getGroups(usr).keys())

            return True

        except Exception:
            return False

    def createUser(self, usrData):
        '''
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, realName, comments, state & password
        @return:  Raises an exception (AuthException) it things didn't went fine
        '''
        res = self.__getUser(usrData['name'])
        if res is None:
            raise AuthenticatorException(_('Username not found'))
        # Fills back realName field
        usrData['real_name'] = self.__getUserRealName(res)

    def getRealName(self, username):
        '''
        Tries to get the real name of an user
        '''
        res = self.__getUser(username)
        if res is None:
            return username
        return self.__getUserRealName(res)

    def modifyUser(self, usrData):
        '''
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        Modify user has no reason on external sources, so it will never be used (probably)
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, realName, comments, state & password
        @return:  Raises an exception it things don't goes fine
        '''
        return self.createUser(usrData)

    def createGroup(self, groupData):
        '''
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        External sources already has its own groups and, at most, it can check if it exists on external source before accepting it
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @params groupData: a dict that has, at least, name, comments and active
        @return:  Raises an exception it things don't goes fine
        '''
        res = self.__getGroup(groupData['name'])
        if res is None:
            raise AuthenticatorException(_('Group not found'))

    def getGroups(self, username, groupsManager):
        '''
        Looks for the real groups to which the specified user belongs
        Updates groups manager with valid groups
        Remember to override it in derived authentication if needed (external auths will need this, for internal authenticators this is never used)
        '''
        user = self.__getUser(username)
        if user is None:
            raise AuthenticatorException(_('Username not found'))
        groupsManager.validate(self.__getGroups(user).keys())

    def searchUsers(self, pattern):
        try:
            con = self.__connection()
            res = []
            for r in con.search_ext_s(
                    base=self._ldapBase,
                    scope=ldap.SCOPE_SUBTREE,
                    filterstr='(&(objectClass=%s)(%s=%s*))' %
                (self._userClass, self._userIdAttr, pattern),
                    sizelimit=LDAP_RESULT_LIMIT):
                usrId = r[1].get(self._userIdAttr, '')
                usrId = type(usrId) == list and usrId[0] or usrId
                res.append({'id': usrId, 'name': self.__getUserRealName(r[1])})
            return res
        except Exception:
            logger.exception("Exception: ")
            raise AuthenticatorException(
                _('Too many results, be more specific'))

    def searchGroups(self, pattern):
        try:
            con = self.__connection()
            res = []
            for r in con.search_ext_s(
                    base=self._ldapBase,
                    scope=ldap.SCOPE_SUBTREE,
                    filterstr='(&(objectClass=%s)(%s=%s*))' %
                (self._groupClass, self._groupIdAttr, pattern),
                    sizelimit=LDAP_RESULT_LIMIT):
                grpId = r[1].get(self._groupIdAttr, '')
                grpId = type(grpId) == list and grpId[0] or grpId
                res.append({'id': grpId, 'name': grpId})
            return res
        except Exception:
            logger.exception("Exception: ")
            raise AuthenticatorException(
                _('Too many results, be more specific'))

    @staticmethod
    def test(env, data):
        try:
            auth = SimpleLDAPAuthenticator(None, env, data)
            return auth.testConnection()
        except Exception as e:
            logger.error(
                "Exception found testing Simple LDAP auth {0}: {1}".format(
                    e.__class__, e))
            return [False, "Error testing connection"]

    def testConnection(self):
        try:
            con = self.__connection()
        except Exception as e:
            return [False, str(e)]

        try:
            con.search_s(base=self._ldapBase, scope=ldap.SCOPE_BASE)
        except Exception:
            return [False, _('Ldap search base is incorrect')]

        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(objectClass=%s)' %
                                     self._userClass,
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap user class seems to be incorrect (no user found by that class)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(objectClass=%s)' %
                                     self._groupClass,
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap group class seems to be incorrect (no group found by that class)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(%s=*)' % self._userIdAttr,
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap user id attribute seems to be incorrect (no user found by that attribute)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(%s=*)' % self._groupIdAttr,
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap group id attribute seems to be incorrect (no group found by that attribute)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        # Now test objectclass and attribute of users
        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(&(objectClass=%s)(%s=*))' %
                                     (self._userClass, self._userIdAttr),
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap user class or user id attr is probably wrong (can\'t find any user with both conditions)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        # And group part, with membership
        try:
            res = con.search_ext_s(base=self._ldapBase,
                                   scope=ldap.SCOPE_SUBTREE,
                                   filterstr='(&(objectClass=%s)(%s=*))' %
                                   (self._groupClass, self._groupIdAttr),
                                   attrlist=[self._memberAttr.encode('utf-8')])
            if len(res) == 0:
                raise Exception(
                    _('Ldap group class or group id attr is probably wrong (can\'t find any group with both conditions)'
                      ))
            ok = False
            for r in res:
                if self._memberAttr in r[1]:
                    ok = True
                    break
            if ok is False:
                raise Exception(
                    _('Can\'t locate any group with the membership attribute specified'
                      ))
        except Exception as e:
            return [False, six.text_type(e)]

        return [
            True,
            _("Connection params seem correct, test was succesfully executed")
        ]
Example #12
0
class LinuxRandomPassManager(LinuxOsManager):
    typeName = _('Linux Random Password OS Manager')
    typeType = 'LinRandomPasswordManager'
    typeDescription = _(
        'Os Manager to control linux machines, with user password set randomly.'
    )
    iconFile = 'losmanager.png'

    # Apart form data from linux os manager, we need also domain and credentials
    userAccount = gui.TextField(length=64,
                                label=_('Account'),
                                order=2,
                                tooltip=_('User account to change password'),
                                required=True)

    # Inherits base "onLogout"
    onLogout = LinuxOsManager.onLogout
    idle = LinuxOsManager.idle

    def __init__(self, environment, values):
        super(LinuxRandomPassManager, self).__init__(environment, values)
        if values is not None:
            if values['userAccount'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide an user account!!!'))
            self._userAccount = values['userAccount']
        else:
            self._userAccount = ''

    def release(self, service):
        super(LinuxRandomPassManager, self).release(service)

    def processUserPassword(self, service, username, password):
        if username == self._userAccount:
            return [username, service.recoverValue('linOsRandomPass')]
        return [username, password]

    def genPassword(self, service):
        import random
        import string
        randomPass = service.recoverValue('linOsRandomPass')
        if randomPass is None:
            randomPass = ''.join(
                random.choice(string.ascii_letters + string.digits)
                for _ in range(16))
            service.storeValue('linOsRandomPass', randomPass)
            log.doLog(service, log.INFO,
                      "Password set to \"{}\"".format(randomPass),
                      log.OSMANAGER)

        return randomPass

    def infoVal(self, service):
        return 'rename:{0}\t{1}\t\t{2}'.format(self.getName(service),
                                               self._userAccount,
                                               self.genPassword(service))

    def infoValue(self, service):
        return 'rename\r{0}\t{1}\t\t{2}'.format(self.getName(service),
                                                self._userAccount,
                                                self.genPassword(service))

    def marshal(self):
        base = super(LinuxRandomPassManager, self).marshal()
        '''
        Serializes the os manager data so we can store it in database
        '''
        return '\t'.join([
            'v1', self._userAccount,
            encoders.encode(base, 'hex', asText=True)
        ]).encode('utf8')

    def unmarshal(self, s):
        data = s.decode('utf8').split('\t')
        if data[0] == 'v1':
            self._userAccount = data[1]
            super(LinuxRandomPassManager,
                  self).unmarshal(encoders.decode(data[2], 'hex'))

    def valuesDict(self):
        dic = super(LinuxRandomPassManager, self).valuesDict()
        dic['userAccount'] = self._userAccount
        return dic
Example #13
0
class BaseX2GOTransport(Transport):
    """
    Provides access via X2GO 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
    """
    iconFile = 'x2go.png'
    protocol = protocols.X2GO
    supportedOss = (OsDetector.Linux, OsDetector.Windows)

    fixedName = gui.TextField(
        order=2,
        label=_('Username'),
        tooltip=_(
            'If not empty, this username will be always used as credential'),
        tab=gui.CREDENTIALS_TAB)

    fullScreen = gui.CheckBoxField(
        order=10,
        label=_('Show fullscreen'),
        tooltip=_('If checked, viewer will be shown on fullscreen mode-'),
        tab=gui.PARAMETERS_TAB)

    desktopType = gui.ChoiceField(
        label=_('Desktop'),
        order=11,
        tooltip=_('Desktop session'),
        values=[
            {
                'id': 'XFCE',
                'text': 'Xfce'
            },
            {
                'id': 'MATE',
                'text': 'Mate'
            },
            {
                'id': 'LXDE',
                'text': 'Lxde'
            },
            {
                'id': 'GNOME',
                'text': 'Gnome (see docs)'
            },
            {
                'id': 'KDE',
                'text': 'Kde (see docs)'
            },
            # {'id': 'UNITY', 'text': 'Unity (see docs)'},
            {
                'id': 'gnome-session-cinnamon',
                'text': 'Cinnamon 1.4 (see docs)'
            },
            {
                'id': 'gnome-session-cinnamon2d',
                'text': 'Cinnamon 2.2 (see docs)'
            },
            {
                'id': 'UDSVAPP',
                'text': 'UDS vAPP'
            },
        ],
        tab=gui.PARAMETERS_TAB)

    customCmd = gui.TextField(
        order=12,
        label=_('vAPP'),
        tooltip=
        _('If UDS vAPP is selected as "Desktop", the FULL PATH of the app to be executed. If UDS vAPP is not selected, this field will be ignored.'
          ),
        tab=gui.PARAMETERS_TAB)

    sound = gui.CheckBoxField(order=13,
                              label=_('Enable sound'),
                              tooltip=_('If checked, sound will be available'),
                              defvalue=gui.TRUE,
                              tab=gui.PARAMETERS_TAB)

    exports = gui.CheckBoxField(
        order=14,
        label=_('Redirect home folder'),
        tooltip=
        _('If checked, user home folder will be redirected. (On linux, also redirects /media)'
          ),
        defvalue=gui.FALSE,
        tab=gui.PARAMETERS_TAB)

    speed = gui.ChoiceField(label=_('Speed'),
                            order=15,
                            tooltip=_('Connection speed'),
                            defvalue='3',
                            values=[
                                {
                                    'id': '0',
                                    'text': 'MODEM'
                                },
                                {
                                    'id': '1',
                                    'text': 'ISDN'
                                },
                                {
                                    'id': '2',
                                    'text': 'ADSL'
                                },
                                {
                                    'id': '3',
                                    'text': 'WAN'
                                },
                                {
                                    'id': '4',
                                    'text': 'LAN'
                                },
                            ],
                            tab=gui.PARAMETERS_TAB)

    soundType = gui.ChoiceField(label=_('Sound'),
                                order=30,
                                tooltip=_('Sound server'),
                                defvalue='pulse',
                                values=[
                                    {
                                        'id': 'pulse',
                                        'text': 'Pulse'
                                    },
                                    {
                                        'id': 'esd',
                                        'text': 'ESD'
                                    },
                                ],
                                tab=gui.ADVANCED_TAB)

    keyboardLayout = gui.TextField(
        label=_('Keyboard'),
        order=31,
        tooltip=_(
            'Keyboard layout (es, us, fr, ...). Empty value means autodetect.'
        ),
        defvalue='',
        tab=gui.ADVANCED_TAB)
    # 'nopack', '8', '64', '256', '512', '4k', '32k', '64k', '256k', '2m', '16m'
    # '256-rdp', '256-rdp-compressed', '32k-rdp', '32k-rdp-compressed', '64k-rdp'
    # '64k-rdp-compressed', '16m-rdp', '16m-rdp-compressed'
    # 'rfb-hextile', 'rfb-tight', 'rfb-tight-compressed'
    # '8-tight', '64-tight', '256-tight', '512-tight', '4k-tight', '32k-tight'
    # '64k-tight', '256k-tight', '2m-tight', '16m-tight'
    # '8-jpeg-%', '64-jpeg', '256-jpeg', '512-jpeg', '4k-jpeg', '32k-jpeg'
    # '64k-jpeg', '256k-jpeg', '2m-jpeg', '16m-jpeg-%'
    # '8-png-jpeg-%', '64-png-jpeg', '256-png-jpeg', '512-png-jpeg', '4k-png-jpeg'
    # '32k-png-jpeg', '64k-png-jpeg', '256k-png-jpeg', '2m-png-jpeg', '16m-png-jpeg-%'
    # '8-png-%', '64-png', '256-png', '512-png', '4k-png'
    # '32k-png', '64k-png', '256k-png', '2m-png', '16m-png-%'
    # '16m-rgb-%', '16m-rle-%'
    pack = gui.TextField(label=_('Pack'),
                         order=32,
                         tooltip=_('Pack format. Change with care!'),
                         defvalue='16m-jpeg',
                         tab=gui.ADVANCED_TAB)

    quality = gui.NumericField(
        label=_('Quality'),
        order=33,
        tooltip=_('Quality value used on some pack formats.'),
        length=1,
        defvalue='6',
        minValue=1,
        maxValue=9,
        required=True,
        tab=gui.ADVANCED_TAB)

    def isAvailableFor(self, userService, ip):
        """
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        """
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache.get(ip)
        if ready is None:
            # Check again for ready
            if connection.testServer(ip, '22') is True:
                self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache.put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def processedUser(self, userService, userName):
        v = self.processUserPassword(userService, userName, '')
        return v['username']

    def processUserPassword(self, service, user, password):
        username = user.getUsernameForAuth()

        if self.fixedName.value != '':
            username = self.fixedName.value

        # Fix username/password acording to os manager
        username, password = service.processUserPassword(username, password)

        return {
            'protocol': self.protocol,
            'username': username,
            'password': ''
        }

    def getConnectionInfo(
        self, service, user, password
    ):  # Password is ignored in this transport, auth is done using SSH
        return self.processUserPassword(service, user, password)

    def genKeyPairForSsh(self):
        """
        Generates a key pair for use with x2go
        The private part is used by client
        the public part must be "appended" to authorized_keys if it is not already added.
        If .ssh folder does not exists, it must be created
        if authorized_keys does not exists, it must be created
        On key adition, we can look for every key that has a "UDS@X2GOCLIENT" as comment, so we can remove them before adding new ones

        Windows (tested):
            C:\Program Files (x86)\\x2goclient>x2goclient.exe --session-conf=c:/temp/sessions --session=UDS/test-session --close-disconnect --hide --no-menu
        Linux (tested):
            HOME=[temporal folder, where we create a .x2goclient folder and a sessions inside] pyhoca-cli -P UDS/test-session
        """
        key = paramiko.RSAKey.generate(SSH_KEY_LENGTH)
        privFile = six.StringIO()
        key.write_private_key(privFile)
        priv = privFile.getvalue()

        pub = key.get_base64(
        )  # 'ssh-rsa {} UDS@X2GOCLIENT'.format(key.get_base64())
        return priv, pub

    def getAuthorizeScript(self, user, pubKey):
        return self.getScript('scripts/authorize.py').replace(
            '__USER__', user).replace('__KEY__', pubKey)

    def getAndPushKey(self, user, userService):
        priv, pub = self.genKeyPairForSsh()
        authScript = self.getAuthorizeScript(user, pub)
        userServiceManager().sendScript(userService, authScript)
        return priv, pub

    def getScript(self, script):
        with open(os.path.join(os.path.dirname(__file__), script)) as f:
            data = f.read()
        return data
Example #14
0
class NXTransport(Transport):
    '''
    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
    '''
    typeName = _('NX Transport (direct)')
    typeType = 'NXTransport'
    typeDescription = _('NX Transport for direct connection')
    iconFile = 'nx.png'
    needsJava = True  # If this transport needs java for rendering
    protocol = protocols.NX

    useEmptyCreds = gui.CheckBoxField(
        label=_('Empty creds'),
        order=1,
        tooltip=_('If checked, the credentials used to connect will be emtpy'))
    fixedName = gui.TextField(
        label=_('Username'),
        order=2,
        tooltip=_(
            'If not empty, this username will be always used as credential'))
    fixedPassword = gui.PasswordField(
        label=_('Password'),
        order=3,
        tooltip=_(
            'If not empty, this password will be always used as credential'))
    listenPort = gui.NumericField(
        label=_('Listening port'),
        length=5,
        order=4,
        tooltip=_('Listening port of NX (ssh) at client machine'),
        defvalue='22')
    connection = gui.ChoiceField(
        label=_('Connection'),
        order=6,
        tooltip=_('Connection speed for this transport (quality)'),
        values=[{
            'id': 'modem',
            'text': 'modem'
        }, {
            'id': 'isdn',
            'text': 'isdn'
        }, {
            'id': 'adsl',
            'text': 'adsl'
        }, {
            'id': 'wan',
            'text': 'wan'
        }, {
            'id': 'lan',
            'text': 'lan'
        }])
    session = gui.ChoiceField(label=_('Session'),
                              order=7,
                              tooltip=_('Desktop session'),
                              values=[
                                  {
                                      'id': 'gnome',
                                      'text': 'gnome'
                                  },
                                  {
                                      'id': 'kde',
                                      'text': 'kde'
                                  },
                                  {
                                      'id': 'cde',
                                      'text': 'cde'
                                  },
                              ])
    cacheDisk = gui.ChoiceField(label=_('Disk Cache'),
                                order=8,
                                tooltip=_('Cache size en Mb stored at disk'),
                                values=[
                                    {
                                        'id': '0',
                                        'text': '0 Mb'
                                    },
                                    {
                                        'id': '32',
                                        'text': '32 Mb'
                                    },
                                    {
                                        'id': '64',
                                        'text': '64 Mb'
                                    },
                                    {
                                        'id': '128',
                                        'text': '128 Mb'
                                    },
                                    {
                                        'id': '256',
                                        'text': '256 Mb'
                                    },
                                    {
                                        'id': '512',
                                        'text': '512 Mb'
                                    },
                                ])
    cacheMem = gui.ChoiceField(label=_('Memory Cache'),
                               order=9,
                               tooltip=_('Cache size en Mb kept at memory'),
                               values=[
                                   {
                                       'id': '4',
                                       'text': '4 Mb'
                                   },
                                   {
                                       'id': '8',
                                       'text': '8 Mb'
                                   },
                                   {
                                       'id': '16',
                                       'text': '16 Mb'
                                   },
                                   {
                                       'id': '32',
                                       'text': '32 Mb'
                                   },
                                   {
                                       'id': '64',
                                       'text': '64 Mb'
                                   },
                                   {
                                       'id': '128',
                                       'text': '128 Mb'
                                   },
                               ])

    def __init__(self, environment, values=None):
        super(NXTransport, self).__init__(environment, values)
        if values is not None:
            self._useEmptyCreds = gui.strToBool(values['useEmptyCreds'])
            self._fixedName = values['fixedName']
            self._fixedPassword = values['fixedPassword']
            self._listenPort = values['listenPort']
            self._connection = values['connection']
            self._session = values['session']
            self._cacheDisk = values['cacheDisk']
            self._cacheMem = values['cacheMem']
        else:
            self._useEmptyCreds = ''
            self._fixedName = ''
            self._fixedPassword = ''
            self._listenPort = ''
            self._connection = ''
            self._session = ''
            self._cacheDisk = ''
            self._cacheMem = ''

    def marshal(self):
        '''
        Serializes the transport data so we can store it in database
        '''
        return str.join('\t', [
            'v1',
            gui.boolToStr(self._useEmptyCreds), self._fixedName,
            self._fixedPassword, self._listenPort, self._connection,
            self._session, self._cacheDisk, self._cacheMem
        ])

    def unmarshal(self, string):
        data = string.split('\t')
        if data[0] == 'v1':
            self._useEmptyCreds = gui.strToBool(data[1])
            self._fixedName, self._fixedPassword, self._listenPort, self._connection, self._session, self._cacheDisk, self._cacheMem = data[
                2:]

    def valuesDict(self):
        return {
            'useEmptyCreds': gui.boolToStr(self._useEmptyCreds),
            'fixedName': self._fixedName,
            'fixedPassword': self._fixedPassword,
            'listenPort': self._listenPort,
            'connection': self._connection,
            'session': self._session,
            'cacheDisk': self._cacheDisk,
            'cacheMem': self._cacheMem
        }

    def isAvailableFor(self, ip):
        '''
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        '''
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache().get(ip)
        if ready is None:
            # Check again for readyness
            if connection.testServer(ip, self._listenPort) is True:
                self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache().put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def renderForHtml(self, userService, transport, ip, os, user, password):

        prefs = user.prefs('nx')

        username = user.getUsernameForAuth()
        proc = username.split('@')
        username = proc[0]
        if self._fixedName is not '':
            username = self._fixedName
        if self._fixedPassword is not '':
            password = self._fixedPassword
        if self._useEmptyCreds is True:
            username, password = '', ''

        # We have the credentials right now, let os manager

        width, height = CommonPrefs.getWidthHeight(prefs)

        # Extra data
        extra = {
            'width': width,
            'height': height,
            'port': self._listenPort,
            'connection': self._connection,
            'session': self._session,
            'cacheDisk': self._cacheDisk,
            'cacheMem': self._cacheMem
        }

        # Fix username/password acording to os manager
        username, password = userService.processUserPassword(
            username, password)

        return generateHtmlForNX(self, userService.uuid, transport.uuid, ip,
                                 os, username, password, extra)

    def getHtmlComponent(self, theId, os, componentId):
        # We use helper to keep this clean
        return getHtmlComponent(self.__module__, componentId)
Example #15
0
class WinDomainOsManager(WindowsOsManager):
    typeName = _('Windows Domain OS Manager')
    typeType = 'WinDomainManager'
    typeDescription = _('Os Manager to control windows machines with domain.')
    iconFile = 'wosmanager.png'

    # Apart form data from windows os manager, we need also domain and credentials
    domain = gui.TextField(
        length=64,
        label=_('Domain'),
        order=1,
        tooltip=
        _('Domain to join machines to (use FQDN form, Netbios name not supported for most operations)'
          ),
        required=True)
    account = gui.TextField(
        length=64,
        label=_('Account'),
        order=2,
        tooltip=_('Account with rights to add machines to domain'),
        required=True)
    password = gui.PasswordField(length=64,
                                 label=_('Password'),
                                 order=3,
                                 tooltip=_('Password of the account'),
                                 required=True)
    ou = gui.TextField(
        length=64,
        label=_('OU'),
        order=4,
        tooltip=
        _('Organizational unit where to add machines in domain (check it before using it). i.e.: ou=My Machines,dc=mydomain,dc=local'
          ))
    grp = gui.TextField(
        length=64,
        label=_('Machine Group'),
        order=7,
        tooltip=
        _('Group to which add machines on creation. If empty, no group will be used. (experimental)'
          ),
        tab=_('Advanced'))
    serverHint = gui.TextField(
        length=64,
        label=_('Server Hint'),
        order=8,
        tooltip=
        _('In case of several AD servers, which one is prefered for first access'
          ),
        tab=_('Advanced'))
    # Inherits base "onLogout"
    onLogout = WindowsOsManager.onLogout
    idle = WindowsOsManager.idle

    def __init__(self, environment, values):
        super(WinDomainOsManager, self).__init__(environment, values)
        if values is not None:
            if values['domain'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide a domain!'))
            # if values['domain'].find('.') == -1:
            #    raise osmanagers.OSManager.ValidationException(_('Must provide domain in FQDN'))
            if values['account'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide an account to add machines to domain!'))
            if values['account'].find('\\') != -1:
                raise osmanagers.OSManager.ValidationException(
                    _('DOM\\USER form is not allowed!'))
            if values['password'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide a password for the account!'))
            self._domain = values['domain']
            self._ou = values['ou'].strip()
            self._account = values['account']
            self._password = values['password']
            self._group = values['grp'].strip()
            self._serverHint = values['serverHint'].strip()
        else:
            self._domain = ""
            self._ou = ""
            self._account = ""
            self._password = ""
            self._group = ""
            self._serverHint = ""

        # self._ou = self._ou.replace(' ', ''), do not remove spaces
        if self._domain != '' and self._ou != '':
            lpath = 'dc=' + ',dc='.join(
                (s.lower() for s in self._domain.split('.')))
            if self._ou.lower().find(lpath) == -1:
                self._ou += ',' + lpath

    def __getLdapError(self, e):
        logger.debug('Ldap Error: {0} {1}'.format(e, e.message))
        _str = ''
        if type(e.message) == dict:
            # _str += e.message.has_key('info') and e.message['info'] + ',' or ''
            _str += e.message.get('desc', '')
        else:
            _str += str(e)
        return _str

    def __getServerList(self):
        if self._serverHint != '':
            yield (self._serverHint, 389)

        for server in reversed(
                sorted(dns.resolver.query('_ldap._tcp.' + self._domain, 'SRV'),
                       key=lambda i: i.priority * 10000 + i.weight)):
            yield (six.text_type(server.target)[:-1], server.port)

    def __connectLdap(self, servers=None):
        '''
        Tries to connect to LDAP
        Raises an exception if not found:
            dns.resolver.NXDOMAIN
            ldap.LDAPError
        '''
        if servers is None:
            servers = self.__getServerList()

        _str = "No servers found"
        for server in servers:
            _str = ''

            try:
                uri = "%s://%s:%d" % ('ldap', server[0], server[1])
                logger.debug('URI: {0}'.format(uri))

                ldap.set_option(
                    ldap.OPT_X_TLS_REQUIRE_CERT,
                    ldap.OPT_X_TLS_NEVER)  # Disable certificate check
                l = ldap.initialize(uri=uri)
                l.set_option(ldap.OPT_REFERRALS, 0)
                l.network_timeout = l.timeout = 5
                l.protocol_version = ldap.VERSION3

                account = self._account
                if account.find('@') == -1:
                    account += '@' + self._domain

                logger.debug('Account data: {0}, {1}, {2}, {3}'.format(
                    self._account, self._domain, account, self._password))

                l.simple_bind_s(who=account, cred=self._password)

                return l
            except ldap.LDAPError as e:
                _str = self.__getLdapError(e)

        raise ldap.LDAPError(_str)

    def __getGroup(self, l):
        base = ','.join(['DC=' + i for i in self._domain.split('.')])
        group = self._group.replace('\\',
                                    '\\\\').replace('(',
                                                    '\\(').replace(')', '\\)')

        res = l.search_ext_s(
            base=base,
            scope=ldap.SCOPE_SUBTREE,
            filterstr="(&(objectClass=group)(|(cn={0})(sAMAccountName={0})))".
            format(group),
            attrlist=[b'dn'])
        if res[0] is None:
            return None

        return res[0][0]  # Returns the DN

    def __getMachine(self, l, machineName):
        if self._ou:
            ou = self._ou
        else:
            ou = ','.join(['DC=' + i for i in self._domain.split('.')])

        fltr = '(&(objectClass=computer)(sAMAccountName={}$))'.format(
            machineName)
        res = l.search_ext_s(base=ou,
                             scope=ldap.SCOPE_SUBTREE,
                             filterstr=fltr,
                             attrlist=[b'dn'])
        if res[0] is None:
            return None

        return res[0][0]  # Returns the DN

    def readyReceived(self, userService, data):
        # No group to add
        if self._group == '':
            return

        if not '.' in self._domain:
            logger.info(
                'Adding to a group for a non FQDN domain is not supported')
            return

        # The machine is on a AD for sure, and maybe they are not already sync
        servers = list(self.__getServerList())

        error = None
        for s in servers:
            try:
                l = self.__connectLdap(servers=(s, ))

                machine = self.__getMachine(l, userService.friendly_name)
                group = self.__getGroup(l)
                l.modify_s(group, ((ldap.MOD_ADD, 'member', machine), ))
                error = None
                break
            except dns.resolver.NXDOMAIN:  # No domain found, log it and pass
                logger.warn('Could not find _ldap._tcp.' + self._domain)
                log.doLog(
                    userService, log.WARN,
                    "Could not remove machine from domain (_ldap._tcp.{0} not found)"
                    .format(self._domain), log.OSMANAGER)
            except ldap.ALREADY_EXISTS:
                # Already added this machine to this group, pass
                error = None
                break
            except ldap.LDAPError:
                logger.exception('Ldap Exception caught')
                error = "Could not remove machine from domain (invalid credentials for {0})".format(
                    self._account)
            except Exception as e:
                error = "Could not add machine {} to group {}: {}".format(
                    userService.friendly_name, self._group, e)
                # logger.exception('Ldap Exception caught')

        if error is not None:
            log.doLog(userService, log.WARN, error, log.OSMANAGER)
            logger.error(error)

    def release(self, service):
        '''
        service is a db user service object
        '''
        super(WinDomainOsManager, self).release(service)

        if not '.' in self._domain:
            logger.info('Releasing from a not FQDN domain is not supported')
            return

        try:
            l = self.__connectLdap()
        except dns.resolver.NXDOMAIN:  # No domain found, log it and pass
            logger.warn('Could not find _ldap._tcp.' + self._domain)
            log.doLog(
                service, log.WARN,
                "Could not remove machine from domain (_ldap._tcp.{0} not found)"
                .format(self._domain), log.OSMANAGER)
            return
        except ldap.LDAPError:
            logger.exception('Ldap Exception caught')
            log.doLog(
                service, log.WARN,
                "Could not remove machine from domain (invalid credentials for {0})"
                .format(self._account), log.OSMANAGER)
            return
        except Exception:
            logger.exception('Exception caught')
            return

        try:
            res = self.__getMachine(l, service.friendly_name)
            if res is None:
                raise Exception(
                    'Machine {} not found on AD (permissions?)'.format(
                        service.friendly_name))
            l.delete_s(res)  # Remove by DN, SYNC
        except IndexError:
            logger.error('Error deleting {} from BASE {}'.format(
                service.friendly_name, self._ou))
        except Exception:
            logger.exception('Deleting from AD: ')

    def check(self):
        try:
            l = self.__connectLdap()
        except ldap.LDAPError as e:
            return _('Check error: {0}').format(self.__getLdapError(e))
        except dns.resolver.NXDOMAIN:
            return [
                True,
                _('Could not find server parameters (_ldap._tcp.{0} can\'t be resolved)'
                  ).format(self._domain)
            ]
        except Exception as e:
            logger.exception('Exception ')
            return [False, str(e)]

        try:
            l.search_st(self._ou, ldap.SCOPE_BASE)
        except ldap.LDAPError as e:
            return _('Check error: {0}').format(self.__getLdapError(e))

        # Group
        if self._group != '':
            if self.__getGroup(l) is None:
                return _(
                    'Check Error: group "{}" not found (using "cn" to locate it)'
                ).format(self._group)

        return _('Server check was successful')

    @staticmethod
    def test(env, data):
        logger.debug('Test invoked')
        try:
            wd = WinDomainOsManager(env, data)
            logger.debug(wd)
            try:
                l = wd.__connectLdap()
            except ldap.LDAPError as e:
                return [
                    False,
                    _('Could not access AD using LDAP ({0})').format(
                        wd.__getLdapError(e))
                ]

            ou = wd._ou
            if ou == '':
                ou = 'cn=Computers,dc=' + ',dc='.join(wd._domain.split('.'))

            logger.debug('Checking {0} with ou {1}'.format(wd._domain, ou))
            r = l.search_st(ou, ldap.SCOPE_BASE)
            logger.debug('Result of search: {0}'.format(r))

        except ldap.LDAPError:
            if wd._ou == '':
                return [
                    False,
                    _('The default path {0} for computers was not found!!!').
                    format(ou)
                ]
            else:
                return [
                    False,
                    _('The ou path {0} was not found!!!').format(ou)
                ]
        except dns.resolver.NXDOMAIN:
            return [
                True,
                _('Could not check parameters (_ldap._tcp.{0} can\'r be resolved)'
                  ).format(wd._domain)
            ]
        except Exception as e:
            logger.exception('Exception ')
            return [False, str(e)]

        return [True, _("All parameters seem to work fine.")]

    def infoVal(self, service):
        return 'domain:{0}\t{1}\t{2}\t{3}\t{4}'.format(self.getName(service),
                                                       self._domain, self._ou,
                                                       self._account,
                                                       self._password)

    def infoValue(self, service):
        return 'domain\r{0}\t{1}\t{2}\t{3}\t{4}'.format(
            self.getName(service), self._domain, self._ou, self._account,
            self._password)

    def marshal(self):
        base = super(WinDomainOsManager, self).marshal()
        '''
        Serializes the os manager data so we can store it in database
        '''
        return '\t'.join([
            'v3', self._domain, self._ou, self._account,
            CryptoManager.manager().encrypt(self._password),
            base.encode('hex'), self._group, self._serverHint
        ])

    def unmarshal(self, s):
        data = s.split('\t')
        if data[0] in ('v1', 'v2', 'v3'):
            self._domain = data[1]
            self._ou = data[2]
            self._account = data[3]
            self._password = CryptoManager.manager().decrypt(data[4])

        if data[0] in ('v2', 'v3'):
            self._group = data[6]
        else:
            self._group = ''

        if data[0] == 'v3':
            self._serverHint = data[7]
        else:
            self._serverHint = ''

        super(WinDomainOsManager, self).unmarshal(data[5].decode('hex'))

    def valuesDict(self):
        dct = super(WinDomainOsManager, self).valuesDict()
        dct['domain'] = self._domain
        dct['ou'] = self._ou
        dct['account'] = self._account
        dct['password'] = self._password
        dct['grp'] = self._group
        dct['serverHint'] = self._serverHint
        return dct
Example #16
0
class HTML5RDPTransport(Transport):
    """
    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
    """
    typeName = _('HTML5 RDP')
    typeType = 'HTML5RDPTransport'
    typeDescription = _('RDP protocol using HTML5 client')
    iconFile = 'html5.png'

    ownLink = True
    supportedOss = OsDetector.allOss
    protocol = protocols.RDP
    group = TUNNELED_GROUP

    guacamoleServer = gui.TextField(
        label=_('Tunnel Server'),
        order=1,
        tooltip=
        _('Host of the tunnel server (use http/https & port if needed) as accesible from users'
          ),
        defvalue='https://',
        length=64,
        required=True,
        tab=gui.TUNNEL_TAB)
    useEmptyCreds = gui.CheckBoxField(
        label=_('Empty creds'),
        order=2,
        tooltip=_('If checked, the credentials used to connect will be emtpy'),
        tab=gui.CREDENTIALS_TAB)
    fixedName = gui.TextField(
        label=_('Username'),
        order=3,
        tooltip=_(
            'If not empty, this username will be always used as credential'),
        tab=gui.CREDENTIALS_TAB)
    fixedPassword = gui.PasswordField(
        label=_('Password'),
        order=4,
        tooltip=_(
            'If not empty, this password will be always used as credential'),
        tab=gui.CREDENTIALS_TAB)
    withoutDomain = gui.CheckBoxField(
        label=_('Without Domain'),
        order=5,
        tooltip=
        _('If checked, the domain part will always be emptied (to connecto to xrdp for example is needed)'
          ),
        tab=gui.CREDENTIALS_TAB)
    fixedDomain = gui.TextField(
        label=_('Domain'),
        order=6,
        tooltip=
        _('If not empty, this domain will be always used as credential (used as DOMAIN\\user)'
          ),
        tab=gui.CREDENTIALS_TAB)
    wallpaper = gui.CheckBoxField(
        label=_('Show wallpaper'),
        order=20,
        tooltip=
        _('If checked, the wallpaper and themes will be shown on machine (better user experience, more bandwidth)'
          ),
        tab=gui.PARAMETERS_TAB)
    desktopComp = gui.CheckBoxField(
        label=_('Allow Desk.Comp.'),
        order=22,
        tooltip=_('If checked, desktop composition will be allowed'),
        tab=gui.PARAMETERS_TAB)
    smooth = gui.CheckBoxField(
        label=_('Font Smoothing'),
        order=23,
        tooltip=_(
            'If checked, fonts smoothing will be allowed (windows clients only)'
        ),
        tab=gui.PARAMETERS_TAB)
    enableAudio = gui.CheckBoxField(
        label=_('Enable Audio'),
        order=24,
        tooltip=
        _('If checked, the audio will be redirected to client (if client browser supports it)'
          ),
        tab=gui.PARAMETERS_TAB)
    enablePrinting = gui.CheckBoxField(
        label=_('Enable Printing'),
        order=25,
        tooltip=
        _('If checked, the printing will be redirected to client (if client browser supports it)'
          ),
        tab=gui.PARAMETERS_TAB)
    enableFileSharing = gui.CheckBoxField(
        label=_('Enable File Sharing'),
        order=8,
        tooltip=
        _('If checked, the user will be able to upload/download files (if client browser supports it)'
          ),
        tab=gui.PARAMETERS_TAB)
    serverLayout = gui.ChoiceField(
        order=26,
        label=_('Layout'),
        tooltip=_('Keyboards Layout of server'),
        required=True,
        values=[
            gui.choiceItem('-', 'default'),
            gui.choiceItem('en-us-qwerty', _('English (US) keyboard')),
            gui.choiceItem('de-de-qwertz', _('German keyboard (qwertz)')),
            gui.choiceItem('fr-fr-azerty', _('French keyboard (azerty)')),
            gui.choiceItem('it-it-qwerty', _('Italian keyboard')),
            gui.choiceItem('sv-se-qwerty', _('Swedish keyboard')),
            gui.choiceItem('failsafe', _('Failsafe')),
        ],
        defvalue='-',
        tab=gui.PARAMETERS_TAB)
    security = gui.ChoiceField(
        order=27,
        label=_('Security'),
        tooltip=_('Connection security mode for Guacamole RDP connection'),
        required=True,
        values=[
            gui.choiceItem(
                'any', _('Any (Allow the server to choose the type of auth)')),
            gui.choiceItem(
                'rdp',
                _('RDP (Standard RDP encryption. Should be supported by all servers)'
                  )),
            gui.choiceItem(
                'nla',
                _('NLA (Network Layer authentication. Requires VALID username&password, or connection will fail)'
                  )),
            gui.choiceItem('tls',
                           _('TLS (Transport Security Layer encryption)')),
        ],
        defvalue='rdp',
        tab=gui.PARAMETERS_TAB)

    ticketValidity = gui.NumericField(
        length=3,
        label=_('Ticket Validity'),
        defvalue='60',
        order=90,
        tooltip=
        _('Allowed time, in seconds, for HTML5 client to reload data from UDS Broker. The default value of 60 is recommended.'
          ),
        required=True,
        minValue=60,
        tab=gui.ADVANCED_TAB)

    def initialize(self, values):
        if values is None:
            return
        self.guacamoleServer.value = self.guacamoleServer.value.strip()
        if self.guacamoleServer.value[0:4] != 'http':
            raise Transport.ValidationException(
                _('The server must be http or https'))
        if self.useEmptyCreds.isTrue() and self.security.value != 'rdp':
            raise Transport.ValidationException(
                _('Empty credentials (on Credentials tab) is only allowed with Security level (on Parameters tab) set to "RDP"'
                  ))

    # Same check as normal RDP transport
    def isAvailableFor(self, userService, ip):
        """
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        """
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache.get(ip)
        if ready is None:
            # Check again for readyness
            if self.testServer(userService, ip, '3389') is True:
                self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache.put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def processedUser(self, userService, userName):
        v = self.processUserPassword(userService, userName, '')
        return v['username']

    def processUserPassword(self, service, user, password):
        username = user.getUsernameForAuth()

        if self.fixedName.value != '':
            username = self.fixedName.value

        proc = username.split('@')
        if len(proc) > 1:
            domain = proc[1]
        else:
            domain = ''
        username = proc[0]

        if self.fixedPassword.value != '':
            password = self.fixedPassword.value
        if self.fixedDomain.value != '':
            domain = self.fixedDomain.value
        if self.useEmptyCreds.isTrue():
            username, password, domain = '', '', ''

        if self.withoutDomain.isTrue():
            domain = ''

        if '.' in domain:  # Dotter domain form
            username = username + '@' + domain
            domain = ''

        # Fix username/password acording to os manager
        username, password = service.processUserPassword(username, password)

        return {
            'protocol': self.protocol,
            'username': username,
            'password': password,
            'domain': domain
        }

    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('Index'))))
Example #17
0
class X2GOTransport(Transport):
    '''
    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
    '''
    typeName = _('X2GO Transport (direct)')
    typeType = 'X2GOTransport'
    typeDescription = _('X2GO Transport for direct connection')
    iconFile = 'x2go.png'
    protocol = protocols.NX

    useEmptyCreds = gui.CheckBoxField(label=_('Empty creds'), order=1, tooltip=_('If checked, the credentials used to connect will be emtpy'))
    fixedName = gui.TextField(label=_('Username'), order=2, tooltip=_('If not empty, this username will be always used as credential'))
    fixedPassword = gui.PasswordField(label=_('Password'), order=3, tooltip=_('If not empty, this password will be always used as credential'))
    listenPort = gui.NumericField(label=_('Listening port'), length=5, order=4, tooltip=_('Listening port of NX (ssh) at client machine'), defvalue='22')
    connection = gui.ChoiceField(label=_('Connection'), order=6, tooltip=_('Connection speed for this transport (quality)'),
                                 values=[
                                     {'id': 'modem', 'text': 'modem'},
                                     {'id': 'isdn', 'text': 'isdn'},
                                     {'id': 'adsl', 'text': 'adsl'},
                                     {'id': 'wan', 'text': 'wan'},
                                     {'id': 'lan', 'text': 'lan'}
    ])
    session = gui.ChoiceField(label=_('Session'), order=7, tooltip=_('Desktop session'),
                              values=[
                                  {'id': 'gnome', 'text': 'gnome'},
                                  {'id': 'kde', 'text': 'kde'},
                                  {'id': 'cde', 'text': 'cde'},
    ])
    cacheDisk = gui.ChoiceField(label=_('Disk Cache'), order=8, tooltip=_('Cache size en Mb stored at disk'),
                                values=[
                                    {'id': '0', 'text': '0 Mb'},
                                    {'id': '32', 'text': '32 Mb'},
                                    {'id': '64', 'text': '64 Mb'},
                                    {'id': '128', 'text': '128 Mb'},
                                    {'id': '256', 'text': '256 Mb'},
                                    {'id': '512', 'text': '512 Mb'},
    ])
    cacheMem = gui.ChoiceField(label=_('Memory Cache'), order=9, tooltip=_('Cache size en Mb kept at memory'),
                               values=[
                                       {'id': '4', 'text': '4 Mb'},
                                       {'id': '8', 'text': '8 Mb'},
                                       {'id': '16', 'text': '16 Mb'},
                                       {'id': '32', 'text': '32 Mb'},
                                       {'id': '64', 'text': '64 Mb'},
                                       {'id': '128', 'text': '128 Mb'},
    ])

    def initialize(self, values):
        if values is None:
            return
        # Just pass over in fact

    def isAvailableFor(self, ip):
        '''
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        '''
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache().get(ip)
        if ready is None:
            # Check again for readyness
            if connection.testServer(ip, self.listenPort.value) is True:
                self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache().put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
        logger.debug('Getting X2Go Transport info')

        prefs = user.prefs('nx')

        username = user.getUsernameForAuth()
        proc = username.split('@')
        username = proc[0]
        if self.fixedName.value != '':
            username = self.fixedName.value
        if self.fixedPassword.value != '':
            password = self.fixedPassword.value
        if self.useEmptyCreds.isTrue():
            username, password = '', ''

        # We have the credentials right now, let os manager

        width, height = CommonPrefs.getWidthHeight(prefs)

        # Fix username/password acording to os manager
        username, password = userService.processUserPassword(username, password)

        # data
        data = {
            'username': username,
            'password': password,
            'width': width,
            'height': height,
            'port': self.listenPort.value,
            'connection': self.connection.value,
            'session': self.session.value,
            'cacheDisk': self.cacheDisk.value,
            'cacheMem': self.cacheMem.value
        }

        return '''
from PyQt4 import QtCore, QtGui
import six
from uds import osDetector

data = {data}
osname = {os}

QtGui.QMessageBox.critical(parent, 'Notice ' + osDetector.getOs(), six.text_type(data), QtGui.QMessageBox.Ok)
'''.format(data=data, os=os)
Example #18
0
class RegexLdap(auths.Authenticator):

    host = gui.TextField(length=64,
                         label=_('Host'),
                         order=1,
                         tooltip=_('Ldap Server Host'),
                         required=True)
    port = gui.NumericField(
        length=5,
        label=_('Port'),
        defvalue='389',
        order=2,
        tooltip=_('Ldap port (usually 389 for non ssl and 636 for ssl)'),
        required=True)
    ssl = gui.CheckBoxField(
        label=_('Use SSL'),
        order=3,
        tooltip=
        _('If checked, the connection will be ssl, using port 636 instead of 389'
          ))
    username = gui.TextField(
        length=64,
        label=_('User'),
        order=4,
        tooltip=_('Username with read privileges on the base selected'),
        required=True,
        tab=gui.CREDENTIALS_TAB)
    password = gui.PasswordField(lenth=32,
                                 label=_('Password'),
                                 order=5,
                                 tooltip=_('Password of the ldap user'),
                                 required=True,
                                 tab=gui.CREDENTIALS_TAB)
    timeout = gui.NumericField(
        length=3,
        label=_('Timeout'),
        defvalue='10',
        order=6,
        tooltip=_('Timeout in seconds of connection to LDAP'),
        required=True,
        minValue=1)

    ldapBase = gui.TextField(
        length=64,
        label=_('Base'),
        order=7,
        tooltip=_('Common search base (used for "users" and "groups")'),
        required=True,
        tab=_('Ldap info'))
    userClass = gui.TextField(
        length=64,
        label=_('User class'),
        defvalue='posixAccount',
        order=8,
        tooltip=_('Class for LDAP users (normally posixAccount)'),
        required=True,
        tab=_('Ldap info'))
    userIdAttr = gui.TextField(
        length=64,
        label=_('User Id Attr'),
        defvalue='uid',
        order=9,
        tooltip=_('Attribute that contains the user id'),
        required=True,
        tab=_('Ldap info'))
    userNameAttr = gui.TextField(
        length=640,
        label=_('User Name Attr'),
        multiline=2,
        defvalue='uid',
        order=10,
        tooltip=
        _('Attributes that contains the user name (list of comma separated values)'
          ),
        required=True,
        tab=_('Ldap info'))
    groupNameAttr = gui.TextField(
        length=640,
        label=_('Group Name Attr'),
        multiline=2,
        defvalue='cn',
        order=11,
        tooltip=_('Attribute that contains the group name'),
        required=True,
        tab=_('Ldap info'))
    # regex = gui.TextField(length=64, label = _('Regular Exp. for groups'), defvalue = '^(.*)', order = 12, tooltip = _('Regular Expression to extract the group name'), required = True)

    altClass = gui.TextField(
        length=64,
        label=_('Alt. class'),
        defvalue='',
        order=20,
        tooltip=
        _('Class for LDAP objects that will be also checked for groups retrieval (normally empty)'
          ),
        required=False,
        tab=_('Advanced'))

    typeName = _('Regex LDAP Authenticator')
    typeType = 'RegexLdapAuthenticator'
    typeDescription = _('Regular Expressions LDAP authenticator')
    iconFile = 'auth.png'

    # If it has and external source where to get "new" users (groups must be declared inside UDS)
    isExternalSource = True
    # If we need to enter the password for this user
    needsPassword = False
    # Label for username field
    userNameLabel = _('Username')
    # Label for group field
    groupNameLabel = _("Group")
    # Label for password field
    passwordLabel = _("Password")

    def __init__(self, dbAuth, environment, values=None):
        super(RegexLdap, self).__init__(dbAuth, environment, values)
        if values is not None:
            self.__validateField(values['userNameAttr'],
                                 str(self.userNameAttr.label))
            self.__validateField(values['userIdAttr'],
                                 str(self.userIdAttr.label))
            self.__validateField(values['groupNameAttr'],
                                 str(self.groupNameAttr.label))

            self._host = values['host']
            self._port = values['port']
            self._ssl = gui.strToBool(values['ssl'])
            self._username = values['username']
            self._password = values['password']
            self._timeout = values['timeout']
            self._ldapBase = values['ldapBase']
            self._userClass = values['userClass']
            self._userIdAttr = values['userIdAttr']
            self._groupNameAttr = values['groupNameAttr']
            # self._regex = values['regex']
            self._userNameAttr = values['userNameAttr']
            self._altClass = values['altClass']
        else:
            self._host = None
            self._port = None
            self._ssl = None
            self._username = None
            self._password = None
            self._timeout = None
            self._ldapBase = None
            self._userClass = None
            self._userIdAttr = None
            self._groupNameAttr = None
            # self._regex = None
            self._userNameAttr = None
            self._altClass = None

        self._connection = None

    def __validateField(self, field, fieldLabel):
        '''
        Validates the multi line fields refering to attributes
        '''
        for line in field.splitlines():
            if line.find('=') != -1:
                _, pattern = line.split('=')[0:2]
                if pattern.find('(') == -1:
                    pattern = '(' + pattern + ')'
                try:
                    re.search(pattern, '')
                except:
                    raise auths.Authenticator.ValidationException(
                        'Invalid pattern in {0}: {1}'.format(fieldLabel, line))

    def __getAttrsFromField(self, field):
        res = []
        for line in field.splitlines():
            equalPos = line.find('=')
            if equalPos != -1:
                attr = line[:equalPos]
            else:
                attr = line
            res.append(attr.encode('utf-8'))
        return res

    def __processField(self, field, attributes):
        res = []
        logger.debug('Attributes: {}'.format(attributes))
        for line in field.splitlines():
            equalPos = line.find('=')
            if equalPos == -1:
                line = line + '=(.*)'
                equalPos = line.find('=')
            attr, pattern = (line[:equalPos], line[equalPos + 1:])
            attr = attr.lower()
            # if pattern do not have groups, define one with full re
            if pattern.find('(') == -1:
                pattern = '(' + pattern + ')'
            val = attributes.get(attr, [])
            if type(val) is not list:  # May we have a single value
                val = [val]

            logger.debug('Pattern: {0}'.format(pattern))

            for vv in val:
                try:
                    v = vv.decode('utf-8')
                    logger.debug('v, vv: {}, {}'.format(v, vv))
                    srch = re.search(pattern, v, re.IGNORECASE)
                    logger.debug("Found against {0}: {1} ".format(
                        v, srch.groups()))
                    if srch is None:
                        continue
                    res.append(''.join(srch.groups()))
                except Exception:
                    pass  # Ignore exceptions here
        return res

    def valuesDict(self):
        return {
            'host': self._host,
            'port': self._port,
            'ssl': gui.boolToStr(self._ssl),
            'username': self._username,
            'password': self._password,
            'timeout': self._timeout,
            'ldapBase': self._ldapBase,
            'userClass': self._userClass,
            'userIdAttr': self._userIdAttr,
            'groupNameAttr': self._groupNameAttr,
            'userNameAttr': self._userNameAttr,
            'altClass': self._altClass,
        }

    def __str__(self):
        return "Ldap Auth: {}:{}@{}:{}, base = {}, userClass = {}, userIdAttr = {}, groupNameAttr = {}, userName attr = {}, altClass={}".format(
            self._username, self._password, self._host, self._port,
            self._ldapBase, self._userClass, self._userIdAttr,
            self._groupNameAttr, self._userNameAttr, self._altClass)

    def marshal(self):
        return '\t'.join([
            'v3', self._host, self._port,
            gui.boolToStr(self._ssl), self._username, self._password,
            self._timeout, self._ldapBase, self._userClass, self._userIdAttr,
            self._groupNameAttr, self._userNameAttr, self._altClass
        ])

    def unmarshal(self, val):
        data = val.split('\t')
        if data[0] == 'v1':
            logger.debug("Data: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, \
                self._timeout, self._ldapBase, self._userClass, self._userIdAttr, \
                self._groupNameAttr, _regex, self._userNameAttr = data[1:]
            self._ssl = gui.strToBool(self._ssl)
            self._groupNameAttr = self._groupNameAttr + '=' + _regex
            self._userNameAttr = '\n'.join(self._userNameAttr.split(','))
        elif data[0] == 'v2':
            logger.debug("Data v2: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, \
                self._timeout, self._ldapBase, self._userClass, self._userIdAttr, \
                self._groupNameAttr, self._userNameAttr = data[1:]
            self._ssl = gui.strToBool(self._ssl)
        elif data[0] == 'v3':
            logger.debug("Data v3: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, \
                self._timeout, self._ldapBase, self._userClass, self._userIdAttr, \
                self._groupNameAttr, self._userNameAttr, self._altClass = data[1:]
            self._ssl = gui.strToBool(self._ssl)

    def __connection(self, username=None, password=None):
        if self._connection is None or username is not None:  # We want this method also to check credentials
            l = None
            cache = False
            try:
                if password is not None:
                    password = password.encode('utf-8')

                # ldap.set_option(ldap.OPT_DEBUG_LEVEL, 9)
                ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,
                                ldap.OPT_X_TLS_NEVER)
                schema = self._ssl and 'ldaps' or 'ldap'
                port = self._port != '389' and ':' + self._port or ''
                uri = "%s://%s%s" % (schema, self._host, port)
                logger.debug('Ldap uri: {0}'.format(uri))
                l = ldap.initialize(uri=uri)
                l.set_option(ldap.OPT_REFERRALS, 0)
                l.network_timeout = l.timeout = int(self._timeout)
                l.protocol_version = ldap.VERSION3

                if username is None:
                    cache = True
                    username = self._username
                    password = self._password

                l.simple_bind_s(who=username, cred=password)
            except ldap.LDAPError, e:
                str_ = _('Ldap connection error: ')
                if type(e.message) == dict:
                    str_ += 'info' in e.message and e.message[
                        'info'] + ',' or ''
                    str_ += 'desc' in e.message and e.message['desc'] or ''
                else:
                    str_ += str(e)
                raise Exception(str_)
            if cache is True:
                self._connection = l
            else:
                return l  # Do not cache nor overwrite "global" connection
        return self._connection
Example #19
0
class SimpleLDAPAuthenticator(Authenticator):

    host = gui.TextField(length=64, label=_('Host'), order=1, tooltip=_('Ldap Server IP or Hostname'), required=True)
    port = gui.NumericField(length=5, label=_('Port'), defvalue='389', order=2, tooltip=_('Ldap port (usually 389 for non ssl and 636 for ssl)'), required=True)
    ssl = gui.CheckBoxField(label=_('Use SSL'), order=3, tooltip=_('If checked, the connection will be ssl, using port 636 instead of 389'))
    username = gui.TextField(length=64, label=_('Ldap User'), order=4, tooltip=_('Username with read privileges on the base selected'), required=True)
    password = gui.PasswordField(lenth=32, label=_('Password'), order=5, tooltip=_('Password of the ldap user'), required=True)
    timeout = gui.NumericField(length=3, label=_('Timeout'), defvalue='10', order=6, tooltip=_('Timeout in seconds of connection to LDAP'), required=True, minValue=1)
    ldapBase = gui.TextField(length=64, label=_('Base'), order=7, tooltip=_('Common search base (used for "users" and "groups")'), required=True)
    userClass = gui.TextField(length=64, label=_('User class'), defvalue='posixAccount', order=8, tooltip=_('Class for LDAP users (normally posixAccount)'), required=True)
    userIdAttr = gui.TextField(length=64, label=_('User Id Attr'), defvalue='uid', order=9, tooltip=_('Attribute that contains the user id'), required=True)
    userNameAttr = gui.TextField(length=64, label=_('User Name Attr'), defvalue='uid', order=10, tooltip=_('Attributes that contains the user name (list of comma separated values)'), required=True)
    groupClass = gui.TextField(length=64, label=_('Group class'), defvalue='posixGroup', order=11, tooltip=_('Class for LDAP groups (normally poxisGroup)'), required=True)
    groupIdAttr = gui.TextField(length=64, label=_('Group Id Attr'), defvalue='cn', order=12, tooltip=_('Attribute that contains the group id'), required=True)
    memberAttr = gui.TextField(length=64, label=_('Group membership attr'), defvalue='memberUid', order=13, tooltip=_('Attribute of the group that contains the users belonging to it'), required=True)

    typeName = _('SimpleLDAP Authenticator')
    typeType = 'SimpleLdapAuthenticator'
    typeDescription = _('Simple LDAP authenticator')
    iconFile = 'auth.png'

    # If it has and external source where to get "new" users (groups must be declared inside UDS)
    isExternalSource = True
    # If we need to enter the password for this user
    needsPassword = False
    # Label for username field
    userNameLabel = _('Username')
    # Label for group field
    groupNameLabel = _("Group")
    # Label for password field
    passwordLabel = _("Password")

    def __init__(self, dbAuth, environment, values=None):
        super(SimpleLDAPAuthenticator, self).__init__(dbAuth, environment, values)
        if values is not None:
            self._host = values['host']
            self._port = values['port']
            self._ssl = gui.strToBool(values['ssl'])
            self._username = values['username']
            self._password = values['password']
            self._timeout = values['timeout']
            self._ldapBase = values['ldapBase']
            self._userClass = values['userClass']
            self._groupClass = values['groupClass']
            self._userIdAttr = values['userIdAttr']
            self._groupIdAttr = values['groupIdAttr']
            self._memberAttr = values['memberAttr']
            self._userNameAttr = values['userNameAttr'].replace(' ', '')  # Removes white spaces
        else:
            self._host = None
            self._port = None
            self._ssl = None
            self._username = None
            self._password = None
            self._timeout = None
            self._ldapBase = None
            self._userClass = None
            self._groupClass = None
            self._userIdAttr = None
            self._groupIdAttr = None
            self._memberAttr = None
            self._userNameAttr = None
        self._connection = None

    def valuesDict(self):
        return {
            'host': self._host, 'port': self._port, 'ssl': gui.boolToStr(self._ssl),
            'username': self._username, 'password': self._password, 'timeout': self._timeout,
            'ldapBase': self._ldapBase, 'userClass': self._userClass, 'groupClass': self._groupClass,
            'userIdAttr': self._userIdAttr, 'groupIdAttr': self._groupIdAttr, 'memberAttr': self._memberAttr,
            'userNameAttr': self._userNameAttr
        }

    def __str__(self):
        return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, groupClass = {6}, userIdAttr = {7}, groupIdAttr = {8}, memberAttr = {9}, userName attr = {10}".format(
            self._username, self._password, self._host, self._port, self._ldapBase, self._userClass, self._groupClass, self._userIdAttr, self._groupIdAttr, self._memberAttr,
            self._userNameAttr)

    def marshal(self):
        return '\t'.join(['v1',
                          self._host, self._port, gui.boolToStr(self._ssl), self._username, self._password, self._timeout,
                          self._ldapBase, self._userClass, self._groupClass, self._userIdAttr, self._groupIdAttr, self._memberAttr, self._userNameAttr])

    def unmarshal(self, str_):
        data = str_.split('\t')
        if data[0] == 'v1':
            logger.debug("Data: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._groupClass, self._userIdAttr, self._groupIdAttr, self._memberAttr, self._userNameAttr = data[1:]
            self._ssl = gui.strToBool(self._ssl)

    def __connection(self, username=None, password=None):
        if self._connection is None or username is not None:  # We want this method also to check credentials
            if isinstance(username, six.text_type):
                username = username.encode('utf8')
            if isinstance(password, six.text_type):
                password = password.encode('utf8')
            l = None
            cache = False
            try:
                # ldap.set_option(ldap.OPT_DEBUG_LEVEL, 9)
                ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
                schema = self._ssl and 'ldaps' or 'ldap'
                port = self._port != '389' and ':' + self._port or ''
                uri = "%s://%s%s" % (schema, self._host, port)
                logger.debug('Ldap uri: {0}'.format(uri))
                l = ldap.initialize(uri=uri)
                l.network_timeout = l.timeout = int(self._timeout)
                l.protocol_version = ldap.VERSION3

                if username is None:
                    cache = True
                    username = self._username
                    password = self._password

                l.simple_bind_s(who=username, cred=password)
            except ldap.LDAPError, e:
                str_ = _('Ldap connection error: ')
                if type(e.message) == dict:
                    str_ += 'info' in e.message and e.message['info'] + ',' or ''
                    str_ += 'desc' in e.message and e.message['desc'] or ''
                else:
                    str_ += str_(e)
                raise Exception(str_)
            if cache is True:
                self._connection = l
            else:
                return l  # Do not cache nor overwrite "global" connection
        return self._connection
Example #20
0
class TRDPTransport(BaseRDPTransport):
    '''
    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
    '''
    typeName = _('RDP')
    typeType = 'TSRDPTransport'
    typeDescription = _('RDP Protocol. Tunneled connection.')
    needsJava = True  # If this transport needs java for rendering
    protocol = protocols.RDP
    group = 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)
    # 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)

    useEmptyCreds = BaseRDPTransport.useEmptyCreds
    fixedName = BaseRDPTransport.fixedName
    fixedPassword = BaseRDPTransport.fixedPassword
    withoutDomain = BaseRDPTransport.withoutDomain
    fixedDomain = BaseRDPTransport.fixedDomain
    allowSmartcards = BaseRDPTransport.allowSmartcards
    allowPrinters = BaseRDPTransport.allowPrinters
    allowDrives = BaseRDPTransport.allowDrives
    allowSerials = BaseRDPTransport.allowSerials
    allowClipboard = BaseRDPTransport.allowClipboard
    allowAudio = BaseRDPTransport.allowAudio

    wallpaper = BaseRDPTransport.wallpaper
    multimon = BaseRDPTransport.multimon
    aero = BaseRDPTransport.aero
    smooth = BaseRDPTransport.smooth
    credssp = BaseRDPTransport.credssp

    screenSize = BaseRDPTransport.screenSize
    colorDepth = BaseRDPTransport.colorDepth

    alsa = BaseRDPTransport.alsa
    multimedia = BaseRDPTransport.multimedia
    redirectHome = BaseRDPTransport.redirectHome
    printerString = BaseRDPTransport.printerString
    smartcardString = BaseRDPTransport.smartcardString
    customParameters = BaseRDPTransport.customParameters

    def initialize(self, values):
        if values is not None:
            if values['tunnelServer'].count(':') != 1:
                raise Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))

    def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
        # We use helper to keep this clean
        # prefs = user.prefs('rdp')

        ci = self.getConnectionInfo(userService, user, password)
        username, password, domain = ci['username'], ci['password'], ci['domain']

        # width, height = CommonPrefs.getWidthHeight(prefs)
        # depth = CommonPrefs.getDepth(prefs)
        width, height = self.screenSize.value.split('x')
        depth = self.colorDepth.value

        tunpass = ''.join(random.choice(string.letters + string.digits) for _i in range(12))
        tunuser = TicketStore.create(tunpass)

        sshHost, sshPort = self.tunnelServer.value.split(':')

        logger.debug('Username generated: {0}, password: {1}'.format(tunuser, tunpass))

        r = RDPFile(width == '-1' or height == '-1', width, height, depth, target=os['OS'])
        r.enablecredsspsupport = ci.get('sso', self.credssp.isTrue())
        r.address = '{address}'
        r.username = username
        r.password = password
        r.domain = domain
        r.redirectPrinters = self.allowPrinters.isTrue()
        r.redirectSmartcards = self.allowSmartcards.isTrue()
        r.redirectDrives = self.allowDrives.value
        r.redirectHome = self.redirectHome.isTrue()
        r.redirectSerials = self.allowSerials.isTrue()
        r.enableClipboard = self.allowClipboard.isTrue()
        r.redirectAudio = self.allowAudio.isTrue()
        r.showWallpaper = self.wallpaper.isTrue()
        r.multimon = self.multimon.isTrue()
        r.desktopComposition = self.aero.isTrue()
        r.smoothFonts = self.smooth.isTrue()
        r.multimedia = self.multimedia.isTrue()
        r.alsa = self.alsa.isTrue()
        r.smartcardString = self.smartcardString.value
        r.printerString = self.printerString.value
        r.linuxCustomParameters = self.customParameters.value

        # data
#         data = {
#             'os': os['OS'],
#             'ip': ip,
#             'tunUser': tunuser,
#             'tunPass': tunpass,
#             'tunHost': sshHost,
#             'tunPort': sshPort,
#             'tunWait': self.tunnelWait.num(),
#             'username': username,
#             'password': password,
#             'hasCredentials': username != '' and password != '',
#             'domain': domain,
#             'width': width,
#             'height': height,
#             'depth': depth,
#             'printers': self.allowPrinters.isTrue(),
#             'smartcards': self.allowSmartcards.isTrue(),
#             'drives': self.allowDrives.isTrue(),
#             'serials': self.allowSerials.isTrue(),
#             'compression': True,
#             'wallpaper': self.wallpaper.isTrue(),
#             'multimon': self.multimon.isTrue(),
#             'fullScreen': width == -1 or height == -1,
#             'this_server': request.build_absolute_uri('/'),
#             'r': r,
#         }

        os = {
            OsDetector.Windows: 'windows',
            OsDetector.Linux: 'linux',
            OsDetector.Macintosh: 'macosx'

        }.get(os['OS'])

        if os is None:
            return super(self.__class__, self).getUDSTransportScript(userService, transport, ip, os, user, password, request)


        sp = {
            'tunUser': tunuser,
            'tunPass': tunpass,
            'tunHost': sshHost,
            'tunPort': sshPort,
            'tunWait': self.tunnelWait.num(),
            'ip': ip,
            'password': password,
            'this_server': request.build_absolute_uri('/'),
        }

        m = tools.DictAsObj(data)

        return self.getScript('scripts/{}/tunnel.py', os, sp)
Example #21
0
class NXTransport(Transport):
    """
    Provides access via NX 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
    """
    typeName = _('NX v3.5')
    typeType = 'NXTransport'
    typeDescription = _('NX Protocol v3.5. Direct connection.')
    iconFile = 'nx.png'
    protocol = protocols.NX

    useEmptyCreds = gui.CheckBoxField(
        label=_('Empty creds'),
        order=1,
        tooltip=_('If checked, the credentials used to connect will be emtpy'),
        tab=gui.CREDENTIALS_TAB)
    fixedName = gui.TextField(
        label=_('Username'),
        order=2,
        tooltip=_(
            'If not empty, this username will be always used as credential'),
        tab=gui.CREDENTIALS_TAB)
    fixedPassword = gui.PasswordField(
        label=_('Password'),
        order=3,
        tooltip=_(
            'If not empty, this password will be always used as credential'),
        tab=gui.CREDENTIALS_TAB)
    listenPort = gui.NumericField(
        label=_('Listening port'),
        length=5,
        order=4,
        tooltip=_('Listening port of NX (ssh) at client machine'),
        defvalue='22')
    connection = gui.ChoiceField(
        label=_('Connection'),
        order=6,
        tooltip=_('Connection speed for this transport (quality)'),
        values=[{
            'id': 'modem',
            'text': 'modem'
        }, {
            'id': 'isdn',
            'text': 'isdn'
        }, {
            'id': 'adsl',
            'text': 'adsl'
        }, {
            'id': 'wan',
            'text': 'wan'
        }, {
            'id': 'lan',
            'text': 'lan'
        }],
        tab=gui.PARAMETERS_TAB)
    session = gui.ChoiceField(label=_('Session'),
                              order=7,
                              tooltip=_('Desktop session'),
                              values=[
                                  {
                                      'id': 'gnome',
                                      'text': 'gnome'
                                  },
                                  {
                                      'id': 'kde',
                                      'text': 'kde'
                                  },
                                  {
                                      'id': 'cde',
                                      'text': 'cde'
                                  },
                              ],
                              tab=gui.PARAMETERS_TAB)
    cacheDisk = gui.ChoiceField(label=_('Disk Cache'),
                                order=8,
                                tooltip=_('Cache size en Mb stored at disk'),
                                values=[
                                    {
                                        'id': '0',
                                        'text': '0 Mb'
                                    },
                                    {
                                        'id': '32',
                                        'text': '32 Mb'
                                    },
                                    {
                                        'id': '64',
                                        'text': '64 Mb'
                                    },
                                    {
                                        'id': '128',
                                        'text': '128 Mb'
                                    },
                                    {
                                        'id': '256',
                                        'text': '256 Mb'
                                    },
                                    {
                                        'id': '512',
                                        'text': '512 Mb'
                                    },
                                ],
                                tab=gui.PARAMETERS_TAB)
    cacheMem = gui.ChoiceField(label=_('Memory Cache'),
                               order=9,
                               tooltip=_('Cache size en Mb kept at memory'),
                               values=[
                                   {
                                       'id': '4',
                                       'text': '4 Mb'
                                   },
                                   {
                                       'id': '8',
                                       'text': '8 Mb'
                                   },
                                   {
                                       'id': '16',
                                       'text': '16 Mb'
                                   },
                                   {
                                       'id': '32',
                                       'text': '32 Mb'
                                   },
                                   {
                                       'id': '64',
                                       'text': '64 Mb'
                                   },
                                   {
                                       'id': '128',
                                       'text': '128 Mb'
                                   },
                               ],
                               tab=gui.PARAMETERS_TAB)

    def __init__(self, environment, values=None):
        super(NXTransport, self).__init__(environment, values)
        if values is not None:
            self._useEmptyCreds = gui.strToBool(values['useEmptyCreds'])
            self._fixedName = values['fixedName']
            self._fixedPassword = values['fixedPassword']
            self._listenPort = values['listenPort']
            self._connection = values['connection']
            self._session = values['session']
            self._cacheDisk = values['cacheDisk']
            self._cacheMem = values['cacheMem']
        else:
            self._useEmptyCreds = ''
            self._fixedName = ''
            self._fixedPassword = ''
            self._listenPort = ''
            self._connection = ''
            self._session = ''
            self._cacheDisk = ''
            self._cacheMem = ''

    def marshal(self):
        """
        Serializes the transport data so we can store it in database
        """
        return str.join('\t', [
            'v1',
            gui.boolToStr(self._useEmptyCreds), self._fixedName,
            self._fixedPassword, self._listenPort, self._connection,
            self._session, self._cacheDisk, self._cacheMem
        ]).encode('utf8')

    def unmarshal(self, val):
        data = val.decode('utf8').split('\t')
        if data[0] == 'v1':
            self._useEmptyCreds = gui.strToBool(data[1])
            self._fixedName, self._fixedPassword, self._listenPort, self._connection, self._session, self._cacheDisk, self._cacheMem = data[
                2:]

    def valuesDict(self):
        return {
            'useEmptyCreds': gui.boolToStr(self._useEmptyCreds),
            'fixedName': self._fixedName,
            'fixedPassword': self._fixedPassword,
            'listenPort': self._listenPort,
            'connection': self._connection,
            'session': self._session,
            'cacheDisk': self._cacheDisk,
            'cacheMem': self._cacheMem
        }

    def isAvailableFor(self, userService, ip):
        """
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        """
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache.get(ip)
        if ready is None:
            # Check again for readyness
            if self.testServer(userService, ip, self._listenPort) is True:
                self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache.put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def getScript(self, script):
        with open(os.path.join(os.path.dirname(__file__), script)) as f:
            data = f.read()
        return data

    def getUDSTransportScript(self, userService, transport, ip, os, user,
                              password, request):
        prefs = user.prefs('nx')

        username = user.getUsernameForAuth()
        proc = username.split('@')
        username = proc[0]
        if self._fixedName is not '':
            username = self._fixedName
        if self._fixedPassword is not '':
            password = self._fixedPassword
        if self._useEmptyCreds is True:
            username, password = '', ''

        # We have the credentials right now, let os manager

        width, height = CommonPrefs.getWidthHeight(prefs)

        # Fix username/password acording to os manager
        username, password = userService.processUserPassword(
            username, password)

        r = NXFile(username=username,
                   password=password,
                   width=width,
                   height=height)
        r.host = ip
        r.port = self._listenPort
        r.connection = self._connection
        r.desktop = self._session
        r.cachedisk = self._cacheDisk
        r.cachemem = self._cacheMem

        os = {
            OsDetector.Windows: 'windows',
            OsDetector.Linux: 'linux',
            OsDetector.Macintosh: 'macosx'
        }.get(os['OS'])

        if os is None:
            return super(self.__class__,
                         self).getUDSTransportScript(userService, transport,
                                                     ip, os, user, password,
                                                     request)

        return self.getScript('scripts/{}/direct.py'.format(os)).format(r=r)
Example #22
0
class BaseRDPTransport(Transport):
    '''
    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
    '''
    iconFile = 'rdp.png'
    protocol = protocols.RDP

    useEmptyCreds = gui.CheckBoxField(
        label=_('Empty creds'),
        order=1,
        tooltip=_('If checked, the credentials used to connect will be emtpy'))
    fixedName = gui.TextField(
        label=_('Username'),
        order=2,
        tooltip=_(
            'If not empty, this username will be always used as credential'))
    fixedPassword = gui.PasswordField(
        label=_('Password'),
        order=3,
        tooltip=_(
            'If not empty, this password will be always used as credential'))
    withoutDomain = gui.CheckBoxField(
        label=_('Without Domain'),
        order=4,
        tooltip=
        _('If checked, the domain part will always be emptied (to connecto to xrdp for example is needed)'
          ))
    fixedDomain = gui.TextField(
        label=_('Domain'),
        order=5,
        tooltip=
        _('If not empty, this domain will be always used as credential (used as DOMAIN\\user)'
          ))
    allowSmartcards = gui.CheckBoxField(
        label=_('Allow Smartcards'),
        order=6,
        tooltip=_(
            'If checked, this transport will allow the use of smartcards'))
    allowPrinters = gui.CheckBoxField(
        label=_('Allow Printers'),
        order=7,
        tooltip=_(
            'If checked, this transport will allow the use of user printers'))
    allowDrives = gui.CheckBoxField(
        label=_('Allow Drives'),
        order=8,
        tooltip=_(
            'If checked, this transport will allow the use of user drives'))
    allowSerials = gui.CheckBoxField(
        label=_('Allow Serials'),
        order=9,
        tooltip=_(
            'If checked, this transport will allow the use of user serial ports'
        ))
    wallpaper = gui.CheckBoxField(
        label=_('Show wallpaper'),
        order=10,
        tooltip=
        _('If checked, the wallpaper and themes will be shown on machine (better user experience, more bandwidth)'
          ))
    multimon = gui.CheckBoxField(
        label=_('Multiple monitors'),
        order=10,
        tooltip=
        _('If checked, all client monitors will be used for displaying (only works on windows clients)'
          ))

    def isAvailableFor(self, ip):
        '''
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        '''
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache().get(ip)
        if ready is None:
            # Check again for ready
            if connection.testServer(ip, '3389') is True:
                self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache().put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def processedUser(self, userService, userName):
        v = self.processUserPassword(userService, userName, '')
        return v['username']

    def processUserPassword(self, service, user, password):
        username = user.getUsernameForAuth()

        if self.fixedName.value is not '':
            username = self.fixedName.value

        proc = username.split('@')
        if len(proc) > 1:
            domain = proc[1]
        else:
            domain = ''
        username = proc[0]

        if self.fixedPassword.value is not '':
            password = self.fixedPassword.value
        if self.fixedDomain.value is not '':
            domain = self.fixedDomain.value
        if self.useEmptyCreds.isTrue():
            username, password, domain = '', '', ''

        if self.withoutDomain.isTrue():
            domain = ''

        if '.' in domain:  # Dotter domain form
            username = username + '@' + domain
            domain = ''

        # Fix username/password acording to os manager
        username, password = service.processUserPassword(username, password)

        return {
            'protocol': self.protocol,
            'username': username,
            'password': password,
            'domain': domain
        }

    def getConnectionInfo(self, service, user, password):
        return self.processUserPassword(service, user, password)

    def getScript(self, script):
        with open(os.path.join(os.path.dirname(__file__), script)) as f:
            data = f.read()
        return data
Example #23
0
class SimpleLDAPAuthenticator(Authenticator):

    host = gui.TextField(length=64,
                         label=_('Host'),
                         order=1,
                         tooltip=_('Ldap Server IP or Hostname'),
                         required=True)
    port = gui.NumericField(
        length=5,
        label=_('Port'),
        defvalue='389',
        order=2,
        tooltip=_('Ldap port (usually 389 for non ssl and 636 for ssl)'),
        required=True)
    ssl = gui.CheckBoxField(
        label=_('Use SSL'),
        order=3,
        tooltip=
        _('If checked, the connection will be ssl, using port 636 instead of 389'
          ))
    username = gui.TextField(
        length=64,
        label=_('Ldap User'),
        order=4,
        tooltip=_('Username with read privileges on the base selected'),
        required=True,
        tab=gui.CREDENTIALS_TAB)
    password = gui.PasswordField(lenth=32,
                                 label=_('Password'),
                                 order=5,
                                 tooltip=_('Password of the ldap user'),
                                 required=True,
                                 tab=gui.CREDENTIALS_TAB)
    timeout = gui.NumericField(
        length=3,
        label=_('Timeout'),
        defvalue='10',
        order=6,
        tooltip=_('Timeout in seconds of connection to LDAP'),
        required=True,
        minValue=1)
    ldapBase = gui.TextField(
        length=64,
        label=_('Base'),
        order=7,
        tooltip=_('Common search base (used for "users" and "groups")'),
        required=True,
        tab=_('Ldap info'))
    userClass = gui.TextField(
        length=64,
        label=_('User class'),
        defvalue='posixAccount',
        order=8,
        tooltip=_('Class for LDAP users (normally posixAccount)'),
        required=True,
        tab=_('Ldap info'))
    userIdAttr = gui.TextField(
        length=64,
        label=_('User Id Attr'),
        defvalue='uid',
        order=9,
        tooltip=_('Attribute that contains the user id'),
        required=True,
        tab=_('Ldap info'))
    userNameAttr = gui.TextField(
        length=64,
        label=_('User Name Attr'),
        defvalue='uid',
        order=10,
        tooltip=
        _('Attributes that contains the user name (list of comma separated values)'
          ),
        required=True,
        tab=_('Ldap info'))
    groupClass = gui.TextField(
        length=64,
        label=_('Group class'),
        defvalue='posixGroup',
        order=11,
        tooltip=_('Class for LDAP groups (normally poxisGroup)'),
        required=True,
        tab=_('Ldap info'))
    groupIdAttr = gui.TextField(
        length=64,
        label=_('Group Id Attr'),
        defvalue='cn',
        order=12,
        tooltip=_('Attribute that contains the group id'),
        required=True,
        tab=_('Ldap info'))
    memberAttr = gui.TextField(
        length=64,
        label=_('Group membership attr'),
        defvalue='memberUid',
        order=13,
        tooltip=_(
            'Attribute of the group that contains the users belonging to it'),
        required=True,
        tab=_('Ldap info'))

    typeName = _('SimpleLDAP Authenticator')
    typeType = 'SimpleLdapAuthenticator'
    typeDescription = _('Simple LDAP authenticator')
    iconFile = 'auth.png'

    # If it has and external source where to get "new" users (groups must be declared inside UDS)
    isExternalSource = True
    # If we need to enter the password for this user
    needsPassword = False
    # Label for username field
    userNameLabel = _('Username')
    # Label for group field
    groupNameLabel = _("Group")
    # Label for password field
    passwordLabel = _("Password")

    def __init__(self, dbAuth, environment, values=None):
        super(SimpleLDAPAuthenticator, self).__init__(dbAuth, environment,
                                                      values)
        if values is not None:
            self._host = values['host']
            self._port = values['port']
            self._ssl = gui.strToBool(values['ssl'])
            self._username = values['username']
            self._password = values['password']
            self._timeout = values['timeout']
            self._ldapBase = values['ldapBase']
            self._userClass = values['userClass']
            self._groupClass = values['groupClass']
            self._userIdAttr = values['userIdAttr']
            self._groupIdAttr = values['groupIdAttr']
            self._memberAttr = values['memberAttr']
            self._userNameAttr = values['userNameAttr'].replace(
                ' ', '')  # Removes white spaces
        else:
            self._host = None
            self._port = None
            self._ssl = None
            self._username = None
            self._password = None
            self._timeout = None
            self._ldapBase = None
            self._userClass = None
            self._groupClass = None
            self._userIdAttr = None
            self._groupIdAttr = None
            self._memberAttr = None
            self._userNameAttr = None
        self._connection = None

    def valuesDict(self):
        return {
            'host': self._host,
            'port': self._port,
            'ssl': gui.boolToStr(self._ssl),
            'username': self._username,
            'password': self._password,
            'timeout': self._timeout,
            'ldapBase': self._ldapBase,
            'userClass': self._userClass,
            'groupClass': self._groupClass,
            'userIdAttr': self._userIdAttr,
            'groupIdAttr': self._groupIdAttr,
            'memberAttr': self._memberAttr,
            'userNameAttr': self._userNameAttr
        }

    def __str__(self):
        return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, groupClass = {6}, userIdAttr = {7}, groupIdAttr = {8}, memberAttr = {9}, userName attr = {10}".format(
            self._username, self._password, self._host, self._port,
            self._ldapBase, self._userClass, self._groupClass,
            self._userIdAttr, self._groupIdAttr, self._memberAttr,
            self._userNameAttr)

    def marshal(self):
        return '\t'.join([
            'v1', self._host, self._port,
            gui.boolToStr(self._ssl), self._username, self._password,
            self._timeout, self._ldapBase, self._userClass, self._groupClass,
            self._userIdAttr, self._groupIdAttr, self._memberAttr,
            self._userNameAttr
        ]).encode('utf8')

    def unmarshal(self, str_):
        data = str_.decode('utf8').split('\t')
        if data[0] == 'v1':
            logger.debug("Data: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._groupClass, self._userIdAttr, self._groupIdAttr, self._memberAttr, self._userNameAttr = data[
                1:]
            self._ssl = gui.strToBool(self._ssl)

    def __connection(self, username=None, password=None):
        """
        Tries to connect to ldap. If username is None, it tries to connect using user provided credentials.
        @return: Connection established
        @raise exception: If connection could not be established
        """
        if self._connection is None:  # We want this method also to check credentials
            self._connection = ldaputil.connection(self._username,
                                                   self._password,
                                                   self._host,
                                                   port=self._port,
                                                   ssl=self._ssl,
                                                   timeout=self._timeout,
                                                   debug=False)

        return self._connection

    def __connectAs(self, username, password):
        return ldaputil.connection(username,
                                   password,
                                   self._host,
                                   ssl=self._ssl,
                                   timeout=self._timeout,
                                   debug=False)

    def __getUser(self, username):
        """
        Searchs for the username and returns its LDAP entry
        @param username: username to search, using user provided parameters at configuration to map search entries.
        @return: None if username is not found, an dictionary of LDAP entry attributes if found.
        @note: Active directory users contains the groups it belongs to in "memberOf" attribute
        """
        return ldaputil.getFirst(
            con=self.__connection(),
            base=self._ldapBase,
            objectClass=self._userClass,
            field=self._userIdAttr,
            value=username,
            attributes=[
                i for i in self._userNameAttr.split(',') + [self._userIdAttr]
            ],
            sizeLimit=LDAP_RESULT_LIMIT)

    def __getGroup(self, groupName):
        """
        Searchs for the groupName and returns its LDAP entry
        @param groupName: group name to search, using user provided parameters at configuration to map search entries.
        @return: None if group name is not found, an dictionary of LDAP entry attributes if found.
        """
        return ldaputil.getFirst(con=self.__connection(),
                                 base=self.__getLdapBase(),
                                 objectClass=self._groupClass,
                                 field=self._groupIdAttr,
                                 value=groupName,
                                 attributes=[self._memberAttr],
                                 sizeLimit=LDAP_RESULT_LIMIT)

    def __getGroups(self, usr):
        try:
            groups = {}

            filter_ = '(&(objectClass=%s)(|(%s=%s)(%s=%s)))' % (
                self._groupClass, self._memberAttr, usr['_id'],
                self._memberAttr, usr['dn'])
            fld = self._groupIdAttr

            for d in ldaputil.getAsDict(con=self.__connection(),
                                        base=self.__getLdapBase(),
                                        ldapFilter=_filter,
                                        attrList=[fld],
                                        sizeLimit=10 * LDAP_RESULT_LIMIT):
                if fld in d:
                    for k in d[fld]:
                        groups.add(k)

            logger.debug('Groups: {0}'.format(groups))
            return groups

        except Exception:
            logger.exception('Exception at __getGroups')
            return {}

    def __getUserRealName(self, usr):
        '''
        Tries to extract the real name for this user. Will return all atttributes (joint)
        specified in _userNameAttr (comma separated).
        '''
        return ' '.join([(type(usr.get(id_, '')) is list and ' '.join(
            (str(k) for k in usr.get(id_, ''))) or str(usr.get(id_, '')))
                         for id_ in self._userNameAttr.split(',')]).strip()

    def authenticate(self, username, credentials, groupsManager):
        '''
        Must authenticate the user.
        We can have to different situations here:
           1.- The authenticator is external source, what means that users may be unknown to system before callig this
           2.- The authenticator isn't external source, what means that users have been manually added to system and are known before this call
        We receive the username, the credentials used (normally password, but can be a public key or something related to pk) and a group manager.
        The group manager is responsible for letting know the authenticator which groups we currently has active.
        @see: uds.core.auths.GroupsManager
        '''
        try:
            # Locate the user at LDAP
            usr = self.__getUser(username)

            if usr is None:
                return False

            # Let's see first if it credentials are fine
            self.__connectAs(
                usrAD['dn'],
                credentials)  # Will raise an exception if it can't connect

            groupsManager.validate(self.__getGroups(usr))

            return True

        except Exception:
            return False

    def createUser(self, usrData):
        '''
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, realName, comments, state & password
        @return:  Raises an exception (AuthException) it things didn't went fine
        '''
        res = self.__getUser(usrData['name'])
        if res is None:
            raise AuthenticatorException(_('Username not found'))
        # Fills back realName field
        usrData['real_name'] = self.__getUserRealName(res)

    def getRealName(self, username):
        '''
        Tries to get the real name of an user
        '''
        res = self.__getUser(username)
        if res is None:
            return username
        return self.__getUserRealName(res)

    def modifyUser(self, usrData):
        '''
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        Modify user has no reason on external sources, so it will never be used (probably)
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, realName, comments, state & password
        @return:  Raises an exception it things don't goes fine
        '''
        return self.createUser(usrData)

    def createGroup(self, groupData):
        '''
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        External sources already has its own groups and, at most, it can check if it exists on external source before accepting it
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @params groupData: a dict that has, at least, name, comments and active
        @return:  Raises an exception it things don't goes fine
        '''
        res = self.__getGroup(groupData['name'])
        if res is None:
            raise AuthenticatorException(_('Group not found'))

    def getGroups(self, username, groupsManager):
        '''
        Looks for the real groups to which the specified user belongs
        Updates groups manager with valid groups
        Remember to override it in derived authentication if needed (external auths will need this, for internal authenticators this is never used)
        '''
        user = self.__getUser(username)
        if user is None:
            raise AuthenticatorException(_('Username not found'))
        groupsManager.validate(self.__getGroups(user))

    def searchUsers(self, pattern):
        try:
            fld = self._userIdAttr
            res = []
            for r in ldaputil.getAsDict(
                    con=self.__connection(),
                    base=self.__getLdapBase(),
                    ldapFilter='(&(objectClass=%s)(%s=%s*))' %
                (self._userClass, self._userIdAttr, pattern),
                    attrList=[fld, self._userNameAttr],
                    sizeLimit=LDAP_RESULT_LIMIT):
                res.append({
                    'id': r[fld][0],  # Ignore @...
                    'name': self.__getUserRealName(r)
                })

            return res
        except Exception:
            logger.exception("Exception: ")
            raise AuthenticatorException(
                _('Too many results, be more specific'))

    def searchGroups(self, pattern):
        try:
            fld = self._groupIdAttr
            res = []
            for r in ldaputil.getAsDict(
                    con=self.__connection(),
                    base=self.__getLdapBase(),
                    ldapFilter='(&(objectClass=%s)(%s=%s*))' %
                (self._groupClass, self._groupIdAttr, pattern),
                    attrList=[fld, 'memberOf', 'description'],
                    sizeLimit=LDAP_RESULT_LIMIT):
                res.append({'id': r[fld][0], 'name': r['description'][0]})

            return res
        except Exception:
            logger.exception("Exception: ")
            raise AuthenticatorException(
                _('Too many results, be more specific'))

    @staticmethod
    def test(env, data):
        try:
            auth = SimpleLDAPAuthenticator(None, env, data)
            return auth.testConnection()
        except Exception as e:
            logger.error(
                "Exception found testing Simple LDAP auth {0}: {1}".format(
                    e.__class__, e))
            return [False, "Error testing connection"]

    def testConnection(self):
        try:
            con = self.__connection()
        except Exception as e:
            return [False, str(e)]

        try:
            con.search_s(base=self._ldapBase, scope=ldap.SCOPE_BASE)
        except Exception:
            return [False, _('Ldap search base is incorrect')]

        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(objectClass=%s)' %
                                     self._userClass,
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap user class seems to be incorrect (no user found by that class)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(objectClass=%s)' %
                                     self._groupClass,
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap group class seems to be incorrect (no group found by that class)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(%s=*)' % self._userIdAttr,
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap user id attribute seems to be incorrect (no user found by that attribute)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(%s=*)' % self._groupIdAttr,
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap group id attribute seems to be incorrect (no group found by that attribute)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        # Now test objectclass and attribute of users
        try:
            if len(
                    con.search_ext_s(base=self._ldapBase,
                                     scope=ldap.SCOPE_SUBTREE,
                                     filterstr='(&(objectClass=%s)(%s=*))' %
                                     (self._userClass, self._userIdAttr),
                                     sizelimit=1)) == 1:
                raise Exception()
            return [
                False,
                _('Ldap user class or user id attr is probably wrong (can\'t find any user with both conditions)'
                  )
            ]
        except Exception as e:
            # If found 1 or more, all right
            pass

        # And group part, with membership
        try:
            res = con.search_ext_s(base=self._ldapBase,
                                   scope=ldap.SCOPE_SUBTREE,
                                   filterstr='(&(objectClass=%s)(%s=*))' %
                                   (self._groupClass, self._groupIdAttr),
                                   attrlist=[self._memberAttr.encode('utf-8')])
            if len(res) == 0:
                raise Exception(
                    _('Ldap group class or group id attr is probably wrong (can\'t find any group with both conditions)'
                      ))
            ok = False
            for r in res:
                if self._memberAttr in r[1]:
                    ok = True
                    break
            if ok is False:
                raise Exception(
                    _('Can\'t locate any group with the membership attribute specified'
                      ))
        except Exception as e:
            return [False, str(e)]

        return [
            True,
            _("Connection params seem correct, test was succesfully executed")
        ]
Example #24
0
class WinDomainOsManager(WindowsOsManager):
    typeName = _('Windows Domain OS Manager')
    typeType = 'WinDomainManager'
    typeDescription = _('Os Manager to control windows machines with domain.')
    iconFile = 'wosmanager.png'

    # Apart form data from windows os manager, we need also domain and credentials
    domain = gui.TextField(
        length=64,
        label=_('Domain'),
        order=1,
        tooltip=
        _('Domain to join machines to (use FQDN form, Netbios name not allowed)'
          ),
        required=True)
    account = gui.TextField(
        length=64,
        label=_('Account'),
        order=2,
        tooltip=_('Account with rights to add machines to domain'),
        required=True)
    password = gui.PasswordField(length=64,
                                 label=_('Password'),
                                 order=3,
                                 tooltip=_('Password of the account'),
                                 required=True)
    ou = gui.TextField(
        length=64,
        label=_('OU'),
        order=4,
        tooltip=
        _('Organizational unit where to add machines in domain (check it before using it). i.e.: ou=My Machines,dc=mydomain,dc=local'
          ))
    # Inherits base "onLogout"
    onLogout = WindowsOsManager.onLogout
    idle = WindowsOsManager.idle

    def __init__(self, environment, values):
        super(WinDomainOsManager, self).__init__(environment, values)
        if values is not None:
            if values['domain'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide a domain!'))
            # if values['domain'].find('.') == -1:
            #    raise osmanagers.OSManager.ValidationException(_('Must provide domain in FQDN'))
            if values['account'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide an account to add machines to domain!'))
            if values['account'].find('\\') != -1:
                raise osmanagers.OSManager.ValidationException(
                    _('DOM\\USER form is not allowed!'))
            if values['password'] == '':
                raise osmanagers.OSManager.ValidationException(
                    _('Must provide a password for the account!'))
            self._domain = values['domain']
            self._ou = values['ou'].strip()
            self._account = values['account']
            self._password = values['password']
        else:
            self._domain = ""
            self._ou = ""
            self._account = ""
            self._password = ""

        # self._ou = self._ou.replace(' ', ''), do not remove spaces
        if self._domain != '' and self._ou != '':
            lpath = 'dc=' + ',dc='.join(
                (s.lower() for s in self._domain.split('.')))
            if self._ou.lower().find(lpath) == -1:
                self._ou += ',' + lpath

    def __getLdapError(self, e):
        logger.debug('Ldap Error: {0} {1}'.format(e, e.message))
        _str = ''
        if type(e.message) == dict:
            # _str += e.message.has_key('info') and e.message['info'] + ',' or ''
            _str += e.message.get('desc', '')
        else:
            _str += str(e)
        return _str

    def __connectLdap(self):
        '''
        Tries to connect to LDAP
        Raises an exception if not found:
            dns.resolver.NXDOMAIN
            ldap.LDAPError
        '''
        servers = reversed(
            sorted(dns.resolver.query('_ldap._tcp.' + self._domain, 'SRV'),
                   key=lambda i: i.priority * 10000 + i.weight))

        for server in servers:

            _str = ''

            try:
                uri = "%s://%s:%d" % ('ldap', str(
                    server.target)[:-1], server.port)
                logger.debug('URI: {0}'.format(uri))

                ldap.set_option(
                    ldap.OPT_X_TLS_REQUIRE_CERT,
                    ldap.OPT_X_TLS_NEVER)  # Disable certificate check
                l = ldap.initialize(uri=uri)
                l.set_option(ldap.OPT_REFERRALS, 0)
                l.network_timeout = l.timeout = 5
                l.protocol_version = ldap.VERSION3

                account = self._account
                if account.find('@') == -1:
                    account += '@' + self._domain

                logger.debug('Account data: {0}, {1}, {2}, {3}'.format(
                    self._account, self._domain, account, self._password))

                l.simple_bind_s(who=account, cred=self._password)

                return l
            except ldap.LDAPError as e:
                _str = self.__getLdapError(e)

        raise ldap.LDAPError(_str)

    def release(self, service):
        '''
        service is a db user service object
        '''
        super(WinDomainOsManager, self).release(service)

        if not '.' in self._domain:
            logger.info('Releasing from a not FQDN domain is not supported')
            return

        try:
            l = self.__connectLdap()
        except dns.resolver.NXDOMAIN:  # No domain found, log it and pass
            logger.warn('Could not find _ldap._tcp.' + self._domain)
            log.doLog(
                service, log.WARN,
                "Could not remove machine from domain (_ldap._tcp.{0} not found)"
                .format(self._domain), log.OSMANAGER)
        except ldap.LDAPError:
            logger.exception('Ldap Exception caught')
            log.doLog(
                service, log.WARN,
                "Could not remove machine from domain (invalid credentials for {0})"
                .format(self._account), log.OSMANAGER)

        try:
            if self._ou:
                ou = self._ou
            else:
                ou = ','.join(['DC=' + i for i in self._domain.split('.')])
            fltr = '(&(objectClass=computer)(sAMAccountName={}$))'.format(
                service.friendly_name)
            res = l.search_ext_s(base=ou,
                                 scope=ldap.SCOPE_SUBTREE,
                                 filterstr=fltr)[0]
            l.delete_s(res[0])  # Remove by DN, SYNC
        except IndexError:
            logger.error('Error deleting {} from BASE {}'.format(
                service.friendly_name, ou))
        except Exception:
            logger.exception('Deleting from AD: ')

    def check(self):
        try:
            l = self.__connectLdap()
        except ldap.LDAPError as e:
            return _('Check error: {0}').format(self.__getLdapError(e))
        except dns.resolver.NXDOMAIN:
            return [
                True,
                _('Could not find server parameters (_ldap._tcp.{0} can\'t be resolved)'
                  ).format(self._domain)
            ]
        except Exception as e:
            logger.exception('Exception ')
            return [False, str(e)]
        try:
            l.search_st(self._ou, ldap.SCOPE_BASE)
        except ldap.LDAPError as e:
            return _('Check error: {0}').format(self.__getLdapError(e))

        return _('Server check was successful')

    @staticmethod
    def test(env, data):
        logger.debug('Test invoked')
        try:
            wd = WinDomainOsManager(env, data)
            logger.debug(wd)
            try:
                l = wd.__connectLdap()
            except ldap.LDAPError as e:
                return [
                    False,
                    _('Could not access AD using LDAP ({0})').format(
                        wd.__getLdapError(e))
                ]

            ou = wd._ou
            if ou == '':
                ou = 'cn=Computers,dc=' + ',dc='.join(wd._domain.split('.'))

            logger.debug('Checking {0} with ou {1}'.format(wd._domain, ou))
            r = l.search_st(ou, ldap.SCOPE_BASE)
            logger.debug('Result of search: {0}'.format(r))

        except ldap.LDAPError:
            if wd._ou == '':
                return [
                    False,
                    _('The default path {0} for computers was not found!!!').
                    format(ou)
                ]
            else:
                return [
                    False,
                    _('The ou path {0} was not found!!!').format(ou)
                ]
        except dns.resolver.NXDOMAIN:
            return [
                True,
                _('Could not check parameters (_ldap._tcp.{0} can\'r be resolved)'
                  ).format(wd._domain)
            ]
        except Exception as e:
            logger.exception('Exception ')
            return [False, str(e)]

        return [True, _("All parameters seem to work fine.")]

    def infoVal(self, service):
        return 'domain:{0}\t{1}\t{2}\t{3}\t{4}'.format(self.getName(service),
                                                       self._domain, self._ou,
                                                       self._account,
                                                       self._password)

    def infoValue(self, service):
        return 'domain\r{0}\t{1}\t{2}\t{3}\t{4}'.format(
            self.getName(service), self._domain, self._ou, self._account,
            self._password)

    def marshal(self):
        base = super(WinDomainOsManager, self).marshal()
        '''
        Serializes the os manager data so we can store it in database
        '''
        return '\t'.join([
            'v1', self._domain, self._ou, self._account,
            CryptoManager.manager().encrypt(self._password),
            base.encode('hex')
        ])

    def unmarshal(self, s):
        data = s.split('\t')
        if data[0] == 'v1':
            self._domain = data[1]
            self._ou = data[2]
            self._account = data[3]
            self._password = CryptoManager.manager().decrypt(data[4])
            super(WinDomainOsManager, self).unmarshal(data[5].decode('hex'))

    def valuesDict(self):
        dct = super(WinDomainOsManager, self).valuesDict()
        dct['domain'] = self._domain
        dct['ou'] = self._ou
        dct['account'] = self._account
        dct['password'] = self._password
        return dct
Example #25
0
class TSRDPTransport(BaseRDPTransport):
    '''
    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
    '''
    typeName = _('RDP Transport (tunneled)')
    typeType = 'TSRDPTransport'
    typeDescription = _('RDP Transport for tunneled connection')
    iconFile = 'rdp.png'
    needsJava = True  # If this transport needs java for rendering
    protocol = protocols.RDP

    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)'))
    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)'))

    useEmptyCreds = BaseRDPTransport.useEmptyCreds
    fixedName = BaseRDPTransport.fixedName
    fixedPassword = BaseRDPTransport.fixedPassword
    withoutDomain = BaseRDPTransport.withoutDomain
    fixedDomain = BaseRDPTransport.fixedDomain
    allowSmartcards = BaseRDPTransport.allowSmartcards
    allowPrinters = BaseRDPTransport.allowPrinters
    allowDrives = BaseRDPTransport.allowDrives
    allowSerials = BaseRDPTransport.allowSerials
    wallpaper = BaseRDPTransport.wallpaper
    multimon = BaseRDPTransport.multimon

    def initialize(self, values):
        if values is not None:
            if values['tunnelServer'].count(':') != 1:
                raise Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))

    def windowsScript(self, m):
        return self.getScript('scripts/windows/tunnel.py').format(m=m)

    def macOsXScript(self, m):
        return self.getScript('scripts/macosx/tunnel.py').format(m=m)

    def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
        # We use helper to keep this clean
        prefs = user.prefs('rdp')

        ci = self.getConnectionInfo(userService, user, password)
        username, password, domain = ci['username'], ci['password'], ci['domain']

        width, height = CommonPrefs.getWidthHeight(prefs)
        depth = CommonPrefs.getDepth(prefs)

        tunpass = ''.join(random.choice(string.letters + string.digits) for _i in range(12))
        tunuser = TicketStore.create(tunpass)

        sshHost, sshPort = self.tunnelServer.value.split(':')

        logger.debug('Username generated: {0}, password: {1}'.format(tunuser, tunpass))

        r = RDPFile(width == -1 or height == -1, width, height, depth, target=os['OS'])
        r.address = '{address}'
        r.username = username
        r.password = password
        r.domain = domain
        r.redirectPrinters = self.allowPrinters.isTrue()
        r.redirectSmartcards = self.allowSmartcards.isTrue()
        r.redirectDrives = self.allowDrives.isTrue()
        r.redirectSerials = self.allowSerials.isTrue()
        r.showWallpaper = self.wallpaper.isTrue()
        r.multimon = self.multimon.isTrue()


        # data
        data = {
            'os': os['OS'],
            'ip': ip,
            'tunUser': tunuser,
            'tunPass': tunpass,
            'tunHost': sshHost,
            'tunPort': sshPort,
            'username': username,
            'password': password,
            'hasCredentials': username != '' and password != '',
            'domain': domain,
            'width': width,
            'height': height,
            'depth': depth,
            'printers': self.allowPrinters.isTrue(),
            'smartcards': self.allowSmartcards.isTrue(),
            'drives': self.allowDrives.isTrue(),
            'serials': self.allowSerials.isTrue(),
            'compression': True,
            'wallpaper': self.wallpaper.isTrue(),
            'multimon': self.multimon.isTrue(),
            'fullScreen': width == -1 or height == -1,
            'this_server': request.build_absolute_uri('/'),
            'r': r,
        }

        m = tools.DictAsObj(data)

        if m.domain != '':
            m.usernameWithDomain = '{}\\\\{}'.format(m.domain, m.username)
        else:
            m.usernameWithDomain = m.username

        if m.os == OsDetector.Windows:
            r.password = '******'

        os = {
            OsDetector.Windows: 'windows',
            OsDetector.Linux: 'linux',
            OsDetector.Macintosh: 'macosx'

        }.get(m.os)

        if os is None:
            return super(TSRDPTransport, self).getUDSTransportScript(self, userService, transport, ip, os, user, password, request)

        return self.getScript('scripts/{}/tunnel.py'.format(os)).format(m=m)
Example #26
0
class BaseRDPTransport(Transport):
    """
    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
    """
    iconFile = 'rdp.png'
    protocol = 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)
    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)
    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(
        label=_('Allow Drives'),
        order=22,
        tooltip=_('Local drives redirection allowed'),
        defvalue='false',
        values=[
            {
                'id': 'false',
                'text': 'None'
            },
            {
                'id': 'dynamic',
                'text': 'Only PnP drives'
            },
            {
                'id': 'true',
                'text': 'All drives'
            },
        ],
        tab=gui.PARAMETERS_TAB)

    allowSerials = gui.CheckBoxField(
        label=_('Allow Serials'),
        order=23,
        tooltip=_(
            'If checked, this transport will allow the use of user serial ports'
        ),
        tab=gui.PARAMETERS_TAB)
    allowClipboard = gui.CheckBoxField(
        label=_('Enable clipboard'),
        order=24,
        tooltip=_('If checked, copy-paste functions will be allowed'),
        tab=gui.PARAMETERS_TAB,
        defvalue=gui.TRUE)
    allowAudio = gui.CheckBoxField(
        label=_('Enable sound'),
        order=25,
        tooltip=_('If checked, sound will be redirected.'),
        tab=gui.PARAMETERS_TAB,
        defvalue=gui.TRUE)
    credssp = gui.CheckBoxField(
        label=_('Credssp Support'),
        order=26,
        tooltip=_('If checked, will enable Credentials Provider Support)'),
        tab=gui.PARAMETERS_TAB,
        defvalue=gui.TRUE)

    screenSize = gui.ChoiceField(label=_('Screen Size'),
                                 order=30,
                                 tooltip=_('Screen size for this transport'),
                                 defvalue='-1x-1',
                                 values=[
                                     {
                                         'id': '640x480',
                                         'text': '640x480'
                                     },
                                     {
                                         'id': '800x600',
                                         'text': '800x600'
                                     },
                                     {
                                         'id': '1024x768',
                                         'text': '1024x768'
                                     },
                                     {
                                         'id': '1366x768',
                                         'text': '1366x768'
                                     },
                                     {
                                         'id': '1920x1080',
                                         'text': '1920x1080'
                                     },
                                     {
                                         'id': '-1x-1',
                                         'text': 'Full screen'
                                     },
                                 ],
                                 tab=gui.DISPLAY_TAB)

    colorDepth = gui.ChoiceField(label=_('Color depth'),
                                 order=31,
                                 tooltip=_('Color depth for this connection'),
                                 defvalue='24',
                                 values=[
                                     {
                                         'id': '8',
                                         'text': '8'
                                     },
                                     {
                                         'id': '16',
                                         'text': '16'
                                     },
                                     {
                                         'id': '24',
                                         'text': '24'
                                     },
                                     {
                                         'id': '32',
                                         'text': '32'
                                     },
                                 ],
                                 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)
    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')
    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')
    smartcardString = gui.TextField(
        label=_('Smartcard string'),
        order=44,
        tooltip=
        _('If smartcard is checked, the smartcard string used with xfreerdp client'
          ),
        tab='Linux Client')
    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')

    def isAvailableFor(self, userService, ip):
        """
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        """
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache.get(ip)
        if ready is None:
            # Check again for ready
            if self.testServer(userService, ip, '3389') is True:
                self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache.put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def processedUser(self, userService, userName):
        v = self.processUserPassword(userService, userName, '')
        return v['username']

    def processUserPassword(self, service, user, password):
        username = user.getUsernameForAuth()

        if self.fixedName.value != '':
            username = self.fixedName.value

        proc = username.split('@')
        if len(proc) > 1:
            domain = proc[1]
        else:
            domain = ''
        username = proc[0]

        if self.fixedPassword.value != '':
            password = self.fixedPassword.value
        if self.fixedDomain.value != '':
            domain = self.fixedDomain.value
        if self.useEmptyCreds.isTrue():
            username, password, domain = '', '', ''

        if self.withoutDomain.isTrue():
            domain = ''

        if domain != '':  # If has domain
            if '.' in domain:  # Dotter domain form
                username = username + '@' + domain
                domain = ''
            else:  # In case of a NETBIOS domain (not recomended), join it so processUserPassword can deal with it
                username = domain + '\\' + username
                domain = ''

        # Temporal "fix" to check if we do something on processUserPassword

        # Fix username/password acording to os manager
        username, password = service.processUserPassword(username, password)

        # Recover domain name if needed
        if '\\' in username:
            username, domain = username.split('\\')

        return {
            'protocol': self.protocol,
            'username': username,
            'password': password,
            'domain': domain
        }

    def getConnectionInfo(self, service, user, password):
        return self.processUserPassword(service, user, password)

    def getScript(self, scriptName, osName, params) -> Tuple[str, str, dict]:
        # Reads script
        scriptName = scriptName.format(osName)
        with open(os.path.join(os.path.dirname(__file__), scriptName)) as f:
            script = f.read()
        # Reads signature
        with open(
                os.path.join(os.path.dirname(__file__),
                             scriptName + '.signature')) as f:
            signature = f.read()
        return script, signature, params
Example #27
0
class RegexLdap(auths.Authenticator):

    host = gui.TextField(length=64, label=_('Host'), order=1, tooltip=_('Ldap Server Host'), required=True)
    port = gui.NumericField(length=5, label=_('Port'), defvalue='389', order=2, tooltip=_('Ldap port (usually 389 for non ssl and 636 for ssl)'), required=True)
    ssl = gui.CheckBoxField(label=_('Use SSL'), order=3, tooltip=_('If checked, the connection will be ssl, using port 636 instead of 389'))
    username = gui.TextField(length=64, label=_('User'), order=4, tooltip=_('Username with read privileges on the base selected'), required=True, tab=gui.CREDENTIALS_TAB)
    password = gui.PasswordField(lenth=32, label=_('Password'), order=5, tooltip=_('Password of the ldap user'), required=True, tab=gui.CREDENTIALS_TAB)
    timeout = gui.NumericField(length=3, label=_('Timeout'), defvalue='10', order=6, tooltip=_('Timeout in seconds of connection to LDAP'), required=True, minValue=1)

    ldapBase = gui.TextField(length=64, label=_('Base'), order=7, tooltip=_('Common search base (used for "users" and "groups")'), required=True, tab=_('Ldap info'))
    userClass = gui.TextField(length=64, label=_('User class'), defvalue='posixAccount', order=8, tooltip=_('Class for LDAP users (normally posixAccount)'), required=True, tab=_('Ldap info'))
    userIdAttr = gui.TextField(length=64, label=_('User Id Attr'), defvalue='uid', order=9, tooltip=_('Attribute that contains the user id'), required=True, tab=_('Ldap info'))
    userNameAttr = gui.TextField(length=640, label=_('User Name Attr'), multiline=2, defvalue='uid', order=10, tooltip=_('Attributes that contains the user name (list of comma separated values)'), required=True, tab=_('Ldap info'))
    groupNameAttr = gui.TextField(length=640, label=_('Group Name Attr'), multiline=2, defvalue='cn', order=11, tooltip=_('Attribute that contains the group name'), required=True, tab=_('Ldap info'))
    # regex = gui.TextField(length=64, label = _('Regular Exp. for groups'), defvalue = '^(.*)', order = 12, tooltip = _('Regular Expression to extract the group name'), required = True)

    altClass = gui.TextField(length=64, label=_('Alt. class'), defvalue='', order=20, tooltip=_('Class for LDAP objects that will be also checked for groups retrieval (normally empty)'), required=False, tab=_('Advanced'))

    typeName = _('Regex LDAP Authenticator')
    typeType = 'RegexLdapAuthenticator'
    typeDescription = _('Regular Expressions LDAP authenticator')
    iconFile = 'auth.png'

    # If it has and external source where to get "new" users (groups must be declared inside UDS)
    isExternalSource = True
    # If we need to enter the password for this user
    needsPassword = False
    # Label for username field
    userNameLabel = _('Username')
    # Label for group field
    groupNameLabel = _("Group")
    # Label for password field
    passwordLabel = _("Password")

    def __init__(self, dbAuth, environment, values=None):
        super(RegexLdap, self).__init__(dbAuth, environment, values)
        if values is not None:
            self.__validateField(values['userNameAttr'], str(self.userNameAttr.label))
            self.__validateField(values['userIdAttr'], str(self.userIdAttr.label))
            self.__validateField(values['groupNameAttr'], str(self.groupNameAttr.label))

            self._host = values['host']
            self._port = values['port']
            self._ssl = gui.strToBool(values['ssl'])
            self._username = values['username']
            self._password = values['password']
            self._timeout = values['timeout']
            self._ldapBase = values['ldapBase']
            self._userClass = values['userClass']
            self._userIdAttr = values['userIdAttr']
            self._groupNameAttr = values['groupNameAttr']
            # self._regex = values['regex']
            self._userNameAttr = values['userNameAttr']
            self._altClass = values['altClass']
        else:
            self._host = None
            self._port = None
            self._ssl = None
            self._username = None
            self._password = None
            self._timeout = None
            self._ldapBase = None
            self._userClass = None
            self._userIdAttr = None
            self._groupNameAttr = None
            # self._regex = None
            self._userNameAttr = None
            self._altClass = None

        self._connection = None

    def __validateField(self, field, fieldLabel):
        """
        Validates the multi line fields refering to attributes
        """
        for line in field.splitlines():
            if line.find('=') != -1:
                _, pattern = line.split('=')[0:2]
                if pattern.find('(') == -1:
                    pattern = '(' + pattern + ')'
                try:
                    re.search(pattern, '')
                except Exception:
                    raise auths.Authenticator.ValidationException('Invalid pattern in {0}: {1}'.format(fieldLabel, line))

    def __getAttrsFromField(self, field):
        res = []
        for line in field.splitlines():
            equalPos = line.find('=')
            if equalPos != -1:
                attr = line[:equalPos]
            else:
                attr = line
            res.append(attr)
        return res

    def __processField(self, field, attributes):
        res = []
        logger.debug('Attributes: {}'.format(attributes))
        for line in field.splitlines():
            equalPos = line.find('=')
            if equalPos == -1:
                line += '=(.*)'
                equalPos = line.find('=')
            attr, pattern = (line[:equalPos], line[equalPos + 1:])
            attr = attr.lower()
            # if pattern do not have groups, define one with full re
            if pattern.find('(') == -1:
                pattern = '(' + pattern + ')'
            val = attributes.get(attr, [])
            if type(val) is not list:  # May we have a single value
                val = [val]

            logger.debug('Pattern: {0}'.format(pattern))

            for v in val:
                try:
                    srch = re.search(pattern, v, re.IGNORECASE)
                    logger.debug("Found against {0}: {1} ".format(v, srch.groups()))
                    if srch is None:
                        continue
                    res.append(''.join(srch.groups()))
                except Exception:
                    pass  # Ignore exceptions here
        logger.debug('Res: {}'.format(res))
        return res

    def valuesDict(self):
        return {
            'host': self._host, 'port': self._port, 'ssl': gui.boolToStr(self._ssl),
            'username': self._username, 'password': self._password, 'timeout': self._timeout,
            'ldapBase': self._ldapBase, 'userClass': self._userClass,
            'userIdAttr': self._userIdAttr, 'groupNameAttr': self._groupNameAttr,
            'userNameAttr': self._userNameAttr, 'altClass': self._altClass,
        }

    def __str__(self):
        return "Ldap Auth: {}:{}@{}:{}, base = {}, userClass = {}, userIdAttr = {}, groupNameAttr = {}, userName attr = {}, altClass={}".format(
               self._username, self._password, self._host, self._port, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr,
               self._userNameAttr, self._altClass)

    def marshal(self):
        return '\t'.join([
            'v3',
            self._host, self._port, gui.boolToStr(self._ssl), self._username, self._password,
            self._timeout, self._ldapBase, self._userClass, self._userIdAttr,
            self._groupNameAttr, self._userNameAttr, self._altClass
        ]).encode('utf8')

    def unmarshal(self, val):
        data = val.decode('utf8').split('\t')
        if data[0] == 'v1':
            logger.debug("Data: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, \
                self._timeout, self._ldapBase, self._userClass, self._userIdAttr, \
                self._groupNameAttr, _regex, self._userNameAttr = data[1:]
            self._ssl = gui.strToBool(self._ssl)
            self._groupNameAttr = self._groupNameAttr + '=' + _regex
            self._userNameAttr = '\n'.join(self._userNameAttr.split(','))
        elif data[0] == 'v2':
            logger.debug("Data v2: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, \
                self._timeout, self._ldapBase, self._userClass, self._userIdAttr, \
                self._groupNameAttr, self._userNameAttr = data[1:]
            self._ssl = gui.strToBool(self._ssl)
        elif data[0] == 'v3':
            logger.debug("Data v3: {0}".format(data[1:]))
            self._host, self._port, self._ssl, self._username, self._password, \
                self._timeout, self._ldapBase, self._userClass, self._userIdAttr, \
                self._groupNameAttr, self._userNameAttr, self._altClass = data[1:]
            self._ssl = gui.strToBool(self._ssl)

    def __connection(self):
        """
        Tries to connect to ldap. If username is None, it tries to connect using user provided credentials.
        @return: Connection established
        @raise exception: If connection could not be established
        """
        if self._connection is None:  # We want this method also to check credentials
            self._connection = ldaputil.connection(self._username, self._password, self._host, port=self._port, ssl=self._ssl, timeout=self._timeout, debug=False)

        return self._connection

    def __connectAs(self, username, password):
        return ldaputil.connection(username, password, self._host, ssl=self._ssl, timeout=self._timeout, debug=False)

    def __getUser(self, username):
        """
        Searchs for the username and returns its LDAP entry
        @param username: username to search, using user provided parameters at configuration to map search entries.
        @return: None if username is not found, an dictionary of LDAP entry attributes if found.
        @note: Active directory users contains the groups it belongs to in "memberOf" attribute
        """
        return ldaputil.getFirst(
            con=self.__connection(),
            base=self._ldapBase,
            objectClass=self._userClass,
            field=self._userIdAttr,
            value=username,
            attributes=[self._userIdAttr] + self.__getAttrsFromField(self._userNameAttr) + self.__getAttrsFromField(self._groupNameAttr),
            sizeLimit=LDAP_RESULT_LIMIT
        )

    def __getGroups(self, usr):
        return self.__processField(self._groupNameAttr, usr)

    def __getUserRealName(self, usr):
        return ' '.join(self.__processField(self._userNameAttr, usr))

    def authenticate(self, username, credentials, groupsManager):
        """
        Must authenticate the user.
        We can have to different situations here:
           1.- The authenticator is external source, what means that users may be unknown to system before callig this
           2.- The authenticator isn't external source, what means that users have been manually added to system and are known before this call
        We receive the username, the credentials used (normally password, but can be a public key or something related to pk) and a group manager.
        The group manager is responsible for letting know the authenticator which groups we currently has active.
        @see: uds.core.auths.GroupsManager
        """
        try:
            # Locate the user at LDAP
            usr = self.__getUser(username)

            if usr is None:
                return False

            # Let's see first if it credentials are fine
            self.__connectAs(usr['dn'], credentials)  # Will raise an exception if it can't connect

            groupsManager.validate(self.__getGroups(usr))

            return True

        except Exception:
            return False

    def createUser(self, usrData):
        """
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        External sources already has the user  cause they are managed externally, so, it can at most test if the users exists on external source
        before accepting it.
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, real_name, comments, state & password
        @return:  Raises an exception (AuthException) it things didn't went fine
        """
        res = self.__getUser(usrData['name'])
        if res is None:
            raise AuthenticatorException(_('Username not found'))
        # Fills back realName field
        usrData['real_name'] = self.__getUserRealName(res)

    def getRealName(self, username):
        """
        Tries to get the real name of an user
        """
        res = self.__getUser(username)
        if res is None:
            return username
        return self.__getUserRealName(res)

    def modifyUser(self, usrData):
        """
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        Modify user has no reason on external sources, so it will never be used (probably)
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @param usrData: Contains data received from user directly, that is, a dictionary with at least: name, realName, comments, state & password
        @return:  Raises an exception it things doesn't go fine
        """
        return self.createUser(usrData)

    def createGroup(self, groupData):
        """
        We must override this method in authenticators not based on external sources (i.e. database users, text file users, etc..)
        External sources already has its own groups and, at most, it can check if it exists on external source before accepting it
        Groups are only used in case of internal users (non external sources) that must know to witch groups this user belongs to
        @params groupData: a dict that has, at least, name, comments and active
        @return:  Raises an exception it things doesn't go fine
        """
        pass

    def getGroups(self, username, groupsManager):
        """
        Looks for the real groups to which the specified user belongs
        Updates groups manager with valid groups
        Remember to override it in derived authentication if needed (external auths will need this, for internal authenticators this is never used)
        """
        user = self.__getUser(username)
        if user is None:
            raise AuthenticatorException(_('Username not found'))
        groups = self.__getGroups(user)
        groupsManager.validate(groups)

    def searchUsers(self, pattern):
        try:
            res = []
            for r in ldaputil.getAsDict(
                con=self.__connection(),
                base=self._ldapBase,
                ldapFilter='(&(&(objectClass={})({}={}*)))'.format(self._userClass, self._userIdAttr, ldaputil.escape(pattern)),
                attrList=None,  # All attrs
                sizeLimit=LDAP_RESULT_LIMIT
            ):
                logger.debug('R: {0}'.format(r))
                res.append({
                    'id': r.get(self._userIdAttr.lower(), '')[0],
                    'name': self.__getUserRealName(r)
                })
            logger.debug(res)
            return res
        except Exception:
            logger.exception("Exception: ")
            raise AuthenticatorException(_('Too many results, be more specific'))

    @staticmethod
    def test(env, data):
        try:
            auth = RegexLdap(None, env, data)
            return auth.testConnection()
        except Exception as e:
            logger.error("Exception found testing Simple LDAP auth {0}: {1}".format(e.__class__, e))
            return [False, "Error testing connection"]

    def testConnection(self):
        try:
            con = self.__connection()
        except Exception as e:
            return [False, str(e)]

        try:
            con.search_s(base=self._ldapBase, scope=ldap.SCOPE_BASE)
        except Exception:
            return [False, _('Ldap search base is incorrect')]

        try:
            if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(objectClass=%s)' % self._userClass, sizelimit=1)) == 1:
                raise Exception()
            return [False, _('Ldap user class seems to be incorrect (no user found by that class)')]
        except Exception:
            # If found 1 or more, all right
            pass

        # Now test objectclass and attribute of users
        try:
            if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(&(objectClass=%s)(%s=*))' % (self._userClass, self._userIdAttr), sizelimit=1)) == 1:
                raise Exception()
            return [False, _('Ldap user id attr is probably wrong (can\'t find any user with both conditions)')]
        except Exception:
            # If found 1 or more, all right
            pass

        for grpNameAttr in self._groupNameAttr.split('\n'):
            vals = grpNameAttr.split('=')[0]
            if vals == 'dn':
                continue
            try:
                if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(%s=*)' % vals, sizelimit=1)) == 1:
                    continue
            except Exception:
                continue
            return [False, _('Ldap group id attribute seems to be incorrect (no group found by that attribute)')]

        # Now try to test regular expression to see if it matches anything (
        try:
            # Check the existence of at least a () grouping
            # Check validity of regular expression (try to compile it)
            # this only right now
            pass
        except Exception:
            pass

        return [True, _("Connection params seem correct, test was succesfully executed")]
Example #28
0
class TSPICETransport(BaseSpiceTransport):
    '''
    Provides access via SPICE 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
    '''
    typeName = _('SPICE Transport (tunneled)')
    typeType = 'TSSPICETransport'
    typeDescription = _(
        'SPICE Transport for tunneled connection  (EXPERIMENTAL)')
    protocol = protocols.SPICE
    group = 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)

    serverCertificate = BaseSpiceTransport.serverCertificate
    fullScreen = BaseSpiceTransport.fullScreen
    usbShare = BaseSpiceTransport.usbShare
    autoNewUsbShare = BaseSpiceTransport.autoNewUsbShare
    smartCardRedirect = BaseSpiceTransport.smartCardRedirect

    def initialize(self, values):
        if values is not None:
            if values['tunnelServer'].count(':') != 1:
                raise Transport.ValidationException(
                    _('Must use HOST:PORT in Tunnel Server Field'))

    def getUDSTransportScript(self, userService, transport, ip, os, user,
                              password, request):
        userServiceInstance = userService.getInstance()

        # Spice connection
        con = userServiceInstance.getConsoleConnection()
        port, secure_port = con['port'], con['secure_port']
        port = -1 if port is None else port
        secure_port = -1 if secure_port is None else secure_port

        # Ticket
        tunpass = ''.join(
            random.choice(string.letters + string.digits) for _i in range(12))
        tunuser = TicketStore.create(tunpass)

        sshHost, sshPort = self.tunnelServer.value.split(':')

        r = RemoteViewerFile('127.0.0.1',
                             '{port}',
                             '{secure_port}',
                             con['ticket']['value'],
                             self.serverCertificate.value,
                             con['cert_subject'],
                             fullscreen=self.fullScreen.isTrue())
        r.usb_auto_share = self.usbShare.isTrue()
        r.new_usb_auto_share = self.autoNewUsbShare.isTrue()
        r.smartcard = self.smartCardRedirect.isTrue()

        m = tools.DictAsObj({
            'r': r,
            'tunUser': tunuser,
            'tunPass': tunpass,
            'tunHost': sshHost,
            'tunPort': sshPort,
            'ip': con['address'],
            'port': port,
            'secure_port': secure_port
        })

        os = {
            OsDetector.Windows: 'windows',
            OsDetector.Linux: 'linux',
            OsDetector.Macintosh: 'macosx'
        }.get(os.OS)

        if os is None:
            return super(self.__class__,
                         self).getUDSTransportScript(userService, transport,
                                                     ip, os, user, password,
                                                     request)

        return self.getScript('scripts/{}/tunnel.py'.format(os)).format(m=m)
Example #29
0
class BaseRDPTransport(Transport):
    '''
    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
    '''
    iconFile = 'rdp.png'
    protocol = 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)
    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=16, tooltip=_('If checked, this transport will allow the use of smartcards'), tab=gui.PARAMETERS_TAB)
    allowPrinters = gui.CheckBoxField(label=_('Allow Printers'), order=17, tooltip=_('If checked, this transport will allow the use of user printers'), tab=gui.PARAMETERS_TAB)
    allowDrives = gui.CheckBoxField(label=_('Allow Drives'), order=18, tooltip=_('If checked, this transport will allow the use of user drives'), tab=gui.PARAMETERS_TAB)
    allowSerials = gui.CheckBoxField(label=_('Allow Serials'), order=19, tooltip=_('If checked, this transport will allow the use of user serial ports'), tab=gui.PARAMETERS_TAB)
    wallpaper = gui.CheckBoxField(label=_('Show wallpaper'), order=20, tooltip=_('If checked, the wallpaper and themes will be shown on machine (better user experience, more bandwidth)'), tab=gui.PARAMETERS_TAB)
    multimon = gui.CheckBoxField(label=_('Multiple monitors'), order=21, tooltip=_('If checked, all client monitors will be used for displaying (only works on windows clients)'), tab=gui.PARAMETERS_TAB)
    aero = gui.CheckBoxField(label=_('Allow Desk.Comp.'), order=22, tooltip=_('If checked, desktop composition will be allowed'), tab=gui.PARAMETERS_TAB)
    smooth = gui.CheckBoxField(label=_('Font Smoothing'), order=23, tooltip=_('If checked, fonts smoothing will be allowed (windows clients only)'), tab=gui.PARAMETERS_TAB)
    credssp = gui.CheckBoxField(label=_('Credssp Support'), order=24, tooltip=_('If checked, will enable Credentials Provider Support)'), tab=gui.PARAMETERS_TAB)
    multimedia = gui.CheckBoxField(label=_('Multimedia sync'), order=25, tooltip=_('If checked. Linux client will use multimedia parameter for xfreerdp'), tab='Linux Client')
    alsa = gui.CheckBoxField(label=_('Use Alsa'), order=26, tooltip=_('If checked, Linux client will try to use ALSA, otherwise Pulse will be used'), tab='Linux Client')
    printerString = gui.TextField(label=_('Printer string'), order=27, tooltip=_('If printer is checked, the printer string used with xfreerdp client'), tab='Linux Client')
    smartcardString = gui.TextField(label=_('Smartcard string'), order=28, tooltip=_('If smartcard is checked, the smartcard string used with xfreerdp client'), tab='Linux Client')
    customParameters = gui.TextField(label=_('Custom parameters'), order=29, 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')

    def isAvailableFor(self, userService, ip):
        '''
        Checks if the transport is available for the requested destination ip
        Override this in yours transports
        '''
        logger.debug('Checking availability for {0}'.format(ip))
        ready = self.cache.get(ip)
        if ready is None:
            # Check again for ready
            if self.testServer(userService, ip, '3389') is True:
                self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT)
                return True
            else:
                self.cache.put(ip, 'N', READY_CACHE_TIMEOUT)
        return ready == 'Y'

    def processedUser(self, userService, userName):
        v = self.processUserPassword(userService, userName, '')
        return v['username']

    def processUserPassword(self, service, user, password):
        username = user.getUsernameForAuth()

        if self.fixedName.value != '':
            username = self.fixedName.value

        proc = username.split('@')
        if len(proc) > 1:
            domain = proc[1]
        else:
            domain = ''
        username = proc[0]

        if self.fixedPassword.value != '':
            password = self.fixedPassword.value
        if self.fixedDomain.value != '':
            domain = self.fixedDomain.value
        if self.useEmptyCreds.isTrue():
            username, password, domain = '', '', ''

        if self.withoutDomain.isTrue():
            domain = ''

        if domain != '':  # If has domain
            if '.' in domain:  # Dotter domain form
                username = username + '@' + domain
                domain = ''
            else:  # In case of a NETBIOS domain (not recomended), join it so processUserPassword can deal with it
                username = domain + '\\' + username
                domain = ''

        # Temporal "fix" to check if we do something on processUserPassword

        # Fix username/password acording to os manager
        username, password = service.processUserPassword(username, password)

        # Recover domain name if needed
        if '\\' in username:
            username, domain = username.split('\\')

        return {'protocol': self.protocol, 'username': username, 'password': password, 'domain': domain}

    def getConnectionInfo(self, service, user, password):
        return self.processUserPassword(service, user, password)

    def getScript(self, scriptName, osName, params):
        # Reads script
        scriptName = scriptName.format(osName)
        with open(os.path.join(os.path.dirname(__file__), scriptName)) as f:
            script = f.read()
        # Reads signature
        with open(os.path.join(os.path.dirname(__file__), scriptName + '.signature')) as f:
            signature = f.read()
        return script, signature, params