コード例 #1
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'))))
コード例 #2
0
ファイル: TSNXTransport.py プロジェクト: aiminickwong/openuds
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)
コード例 #3
0
class PoolPerformanceReport(StatsReport):
    filename = 'pools_performance.pdf'
    name = _('Pools performance by date')  # Report name
    description = _('Pools performance report by date')  # Report description
    uuid = '88932b48-1fd3-11e5-a776-10feed05884b'

    # Input fields
    pools = gui.MultiChoiceField(order=1,
                                 label=_('Pools'),
                                 tooltip=_('Pools for report'),
                                 required=True)

    startDate = gui.DateField(order=2,
                              label=_('Starting date'),
                              tooltip=_('starting date for report'),
                              defvalue=datetime.date.min,
                              required=True)

    endDate = gui.DateField(order=3,
                            label=_('Finish date'),
                            tooltip=_('finish date for report'),
                            defvalue=datetime.date.max,
                            required=True)

    samplingPoints = gui.NumericField(
        order=4,
        label=_('Number of intervals'),
        length=3,
        minValue=0,
        maxValue=32,
        tooltip=_('Number of sampling points used in charts'),
        defvalue='8')

    def initialize(self, values):
        pass

    def initGui(self):
        logger.debug('Initializing gui')
        vals = [
            gui.choiceItem(v.uuid, v.name)
            for v in ServicePool.objects.all().order_by('name')
        ]
        self.pools.setValues(vals)

    def getPools(self):
        return [(v.id, v.name)
                for v in ServicePool.objects.filter(uuid__in=self.pools.value)]

    def getRangeData(self):
        start = self.startDate.stamp()
        end = self.endDate.stamp()

        if self.samplingPoints.num() < 2:
            self.samplingPoints.value = (self.endDate.date() -
                                         self.startDate.date()).days
        if self.samplingPoints.num() < 2:
            self.samplingPoints.value = 2
        if self.samplingPoints.num() > 32:
            self.samplingPoints.value = 32

        samplingPoints = self.samplingPoints.num()

        pools = self.getPools()

        if len(pools) == 0:
            raise Exception(_('Select at least a service pool for the report'))

        logger.debug('Pools: {}'.format(pools))

        # x axis label format
        if end - start > 3600 * 24 * 2:
            xLabelFormat = 'SHORT_DATE_FORMAT'
        else:
            xLabelFormat = 'SHORT_DATETIME_FORMAT'

        # Generate samplings interval
        samplingIntervals = []
        prevVal = None
        for val in range(start, end, int(
            (end - start) / (samplingPoints + 1))):
            if prevVal is None:
                prevVal = val
                continue
            samplingIntervals.append((prevVal, val))
            prevVal = val

        # Store dataUsers for all pools
        poolsData = []

        fld = events.statsManager().getEventFldFor('username')

        reportData = []
        for p in pools:
            dataUsers = []
            dataAccesses = []
            for interval in samplingIntervals:
                key = (interval[0] + interval[1]) / 2
                q = events.statsManager().getEvents(
                    events.OT_DEPLOYED,
                    events.ET_ACCESS,
                    since=interval[0],
                    to=interval[1],
                    owner_id=p[0]).values(fld).annotate(cnt=Count(fld))
                accesses = 0
                for v in q:
                    accesses += v['cnt']

                dataUsers.append((key, len(q)))  # @UndefinedVariable
                dataAccesses.append((key, accesses))
                reportData.append({
                    'name':
                    p[1],
                    'date':
                    tools.timestampAsStr(interval[0], xLabelFormat) + ' - ' +
                    tools.timestampAsStr(interval[1], xLabelFormat),
                    'users':
                    len(q),
                    'accesses':
                    accesses
                })
            poolsData.append({
                'pool': p[0],
                'name': p[1],
                'dataUsers': dataUsers,
                'dataAccesses': dataAccesses,
            })

        return xLabelFormat, poolsData, reportData

    def generate(self):
        # Generate the sampling intervals and get dataUsers from db
        xLabelFormat, poolsData, reportData = self.getRangeData()

        graph1 = io.BytesIO()
        graph2 = io.BytesIO()

        # surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)  # @UndefinedVariable

        # logger.debug('PoolsData: %s', poolsData)

        X = [v[0] for v in poolsData[0]['dataUsers']]
        data = {
            'title':
            _('Distinct Users'),
            'x':
            X,
            'xtickFnc':
            lambda l: filters.date(datetime.datetime.fromtimestamp(X[int(l)]),
                                   xLabelFormat) if int(l) >= 0 else '',
            'xlabel':
            _('Date'),
            'y': [{
                'label': p['name'],
                'data': [v[1] for v in p['dataUsers']]
            } for p in poolsData],
            'ylabel':
            _('Users')
        }

        graphs.barChart(SIZE, data, graph1)

        X = [v[0] for v in poolsData[0]['dataAccesses']]
        data = {
            'title':
            _('Accesses'),
            'x':
            X,
            'xtickFnc':
            lambda l: filters.date(datetime.datetime.fromtimestamp(X[int(l)]),
                                   xLabelFormat) if int(l) >= 0 else '',
            'xlabel':
            _('Date'),
            'y': [{
                'label': p['name'],
                'data': [v[1] for v in p['dataAccesses']]
            } for p in poolsData],
            'ylabel':
            _('Accesses')
        }

        graphs.barChart(SIZE, data, graph2)

        # Generate Data for pools, basically joining all pool data

        return self.templateAsPDF(
            'uds/reports/stats/pools-performance.html',
            dct={
                'data': reportData,
                'pools': [i[1] for i in self.getPools()],
                'beginning': self.startDate.date(),
                'ending': self.endDate.date(),
                'intervals': self.samplingPoints.num(),
            },
            header=ugettext('UDS Pools Performance Report'),
            water=ugettext('Pools Performance'),
            images={
                'graph1': graph1.getvalue(),
                'graph2': graph2.getvalue()
            },
        )
コード例 #4
0
class StatsReportLogin(StatsReport):
    filename = 'access.pdf'
    name = _('Users access report by date')  # Report name
    description = _(
        'Report of user access to platform by date')  # Report description
    uuid = '0f62f19a-f166-11e4-8f59-10feed05884b'

    # Input fields
    startDate = gui.DateField(order=1,
                              label=_('Starting date'),
                              tooltip=_('starting date for report'),
                              defvalue=datetime.date.min,
                              required=True)

    endDate = gui.DateField(order=2,
                            label=_('Finish date'),
                            tooltip=_('finish date for report'),
                            defvalue=datetime.date.max,
                            required=True)

    samplingPoints = gui.NumericField(
        order=3,
        label=_('Number of intervals'),
        length=3,
        minValue=0,
        maxValue=128,
        tooltip=_('Number of sampling points used in charts'),
        defvalue='64')

    def initialize(self, values):
        pass

    def initGui(self):
        pass

    def getRangeData(self):
        start = self.startDate.stamp()
        end = self.endDate.stamp()
        if self.samplingPoints.num() < 8:
            self.samplingPoints.value = (self.endDate.date() -
                                         self.startDate.date()).days
        if self.samplingPoints.num() < 2:
            self.samplingPoints.value = 2
        if self.samplingPoints.num() > 128:
            self.samplingPoints.value = 128

        samplingPoints = self.samplingPoints.num()

        # x axis label format
        if end - start > 3600 * 24 * 2:
            xLabelFormat = 'SHORT_DATE_FORMAT'
        else:
            xLabelFormat = 'SHORT_DATETIME_FORMAT'

        samplingIntervals = []
        prevVal = None
        for val in range(start, end, int(
            (end - start) / (samplingPoints + 1))):
            if prevVal is None:
                prevVal = val
                continue
            samplingIntervals.append((prevVal, val))
            prevVal = val

        data = []
        reportData = []
        for interval in samplingIntervals:
            key = (interval[0] + interval[1]) / 2
            val = events.statsManager().getEvents(events.OT_AUTHENTICATOR,
                                                  events.ET_LOGIN,
                                                  since=interval[0],
                                                  to=interval[1]).count()
            data.append((key, val))  # @UndefinedVariable
            reportData.append({
                'date':
                tools.timestampAsStr(interval[0], xLabelFormat) + ' - ' +
                tools.timestampAsStr(interval[1], xLabelFormat),
                'users':
                val
            })

        return xLabelFormat, data, reportData

    def getWeekHourlyData(self):
        start = self.startDate.stamp()
        end = self.endDate.stamp()

        dataWeek = [0] * 7
        dataHour = [0] * 24
        dataWeekHour = [[0] * 24 for _ in range(7)]
        for val in events.statsManager().getEvents(events.OT_AUTHENTICATOR,
                                                   events.ET_LOGIN,
                                                   since=start,
                                                   to=end):
            s = datetime.datetime.fromtimestamp(val.stamp)
            dataWeek[s.weekday()] += 1
            dataHour[s.hour] += 1
            dataWeekHour[s.weekday()][s.hour] += 1
            logger.debug('Data: {} {}'.format(s.weekday(), s.hour))

        return dataWeek, dataHour, dataWeekHour

    def generate(self):
        # Sample query:
        #   'SELECT *, count(*) as number, CEIL(stamp/(3600))*3600 as block'
        #   ' FROM {table}'
        #   ' WHERE event_type = 0 and stamp >= {start} and stamp <= {end}'
        #   ' GROUP BY CEIL(stamp/(3600))'
        #   ' ORDER BY block'

        xLabelFormat, data, reportData = self.getRangeData()

        #
        # User access by date graph
        #
        graph1 = io.BytesIO()

        X = [v[0] for v in data]
        d = {
            'title':
            _('Users Access (global)'),
            'x':
            X,
            'xtickFnc':
            lambda l: filters.date(datetime.datetime.fromtimestamp(l),
                                   xLabelFormat),
            'xlabel':
            _('Date'),
            'y': [{
                'label': 'Users',
                'data': [v[1] for v in data]
            }],
            'ylabel':
            'Users',
            'allTicks':
            False
        }

        graphs.lineChart(SIZE, d, graph1)

        graph2 = io.BytesIO()
        graph3 = io.BytesIO()
        graph4 = io.BytesIO()
        dataWeek, dataHour, dataWeekHour = self.getWeekHourlyData()

        X = list(range(7))
        d = {
            'title':
            _('Users Access (by week)'),
            'x':
            X,
            'xtickFnc':
            lambda l: [
                _('Monday'),
                _('Tuesday'),
                _('Wednesday'),
                _('Thursday'),
                _('Friday'),
                _('Saturday'),
                _('Sunday')
            ][l],
            'xlabel':
            _('Day of week'),
            'y': [{
                'label': 'Users',
                'data': [v for v in dataWeek]
            }],
            'ylabel':
            'Users'
        }

        graphs.barChart(SIZE, d, graph2)

        X = list(range(24))
        d = {
            'title': _('Users Access (by hour)'),
            'x': X,
            'xlabel': _('Hour'),
            'y': [{
                'label': 'Users',
                'data': [v for v in dataHour]
            }],
            'ylabel': 'Users'
        }

        graphs.barChart(SIZE, d, graph3)

        X = list(range(24))
        Y = list(range(7))
        d = {
            'title':
            _('Users Access (by hour)'),
            'x':
            X,
            'xlabel':
            _('Hour'),
            'xtickFnc':
            lambda l: l,
            'y':
            Y,
            'ylabel':
            _('Day of week'),
            'ytickFnc':
            lambda l: [
                _('Monday'),
                _('Tuesday'),
                _('Wednesday'),
                _('Thursday'),
                _('Friday'),
                _('Saturday'),
                _('Sunday')
            ][l],
            'z':
            dataWeekHour,
            'zlabel':
            _('Users')
        }

        graphs.surfaceChart(SIZE, d, graph4)

        return self.templateAsPDF(
            'uds/reports/stats/user-access.html',
            dct={
                'data': reportData,
                'beginning': self.startDate.date(),
                'ending': self.endDate.date(),
                'intervals': self.samplingPoints.num(),
            },
            header=ugettext('Users access to UDS'),
            water=ugettext('UDS Report for users access'),
            images={
                'graph1': graph1.getvalue(),
                'graph2': graph2.getvalue(),
                'graph3': graph3.getvalue(),
                'graph4': graph4.getvalue()
            },
        )
コード例 #5
0
ファイル: WindowsOsManager.py プロジェクト: shaba/openuds
class WindowsOsManager(osmanagers.OSManager):
    typeName = _('Windows Basic OS Manager')
    typeType = 'WindowsManager'
    typeDescription = _(
        'Os Manager to control windows machines without domain.')
    iconFile = 'wosmanager.png'
    servicesType = (serviceTypes.VDI, )

    onLogout = gui.ChoiceField(
        label=_('Logout Action'),
        order=10,
        rdonly=True,
        tooltip=_('What to do when user logs out from service'),
        values=[
            {
                'id': 'keep',
                'text': ugettext_lazy('Keep service assigned')
            },
            {
                'id': 'remove',
                'text': ugettext_lazy('Remove service')
            },
            {
                'id':
                'keep-always',
                'text':
                ugettext_lazy('Keep service assigned even on new publication')
            },
        ],
        defvalue='keep')

    idle = gui.NumericField(
        label=_("Max.Idle time"),
        length=4,
        defvalue=-1,
        rdonly=False,
        order=11,
        tooltip=
        _('Maximum idle time (in seconds) before session is automatically closed to the user (<= 0 means no max. idle time)'
          ),
        required=True)

    @staticmethod
    def validateLen(length):
        try:
            length = int(length)
        except Exception:
            raise osmanagers.OSManager.ValidationException(
                _('Length must be numeric!!'))
        if length > 6 or length < 1:
            raise osmanagers.OSManager.ValidationException(
                _('Length must be betwen 1 and 6'))
        return length

    def __setProcessUnusedMachines(self):
        self.processUnusedMachines = self._onLogout == 'remove'

    def __init__(self, environment, values):
        super(WindowsOsManager, self).__init__(environment, values)
        if values is not None:
            self._onLogout = values['onLogout']
            self._idle = int(values['idle'])
        else:
            self._onLogout = ''
            self._idle = -1

        self.__setProcessUnusedMachines()

    def isRemovableOnLogout(self, userService):
        '''
        Says if a machine is removable on logout
        '''
        if userService.in_use == False:
            if (self._onLogout
                    == 'remove') or (not userService.isValidPublication()
                                     and self._onLogout == 'keep'):
                return True

        return False

    def release(self, service):
        pass

    def getName(self, service):
        """
        gets name from deployed
        """
        return service.getName()

    def infoVal(self, service):
        return 'rename:' + self.getName(service)

    def infoValue(self, service):
        return 'rename\r' + self.getName(service)

    def notifyIp(self, uid, service, data):
        si = service.getInstance()

        ip = ''
        # Notifies IP to deployed
        for p in data['ips']:
            if p[0].lower() == uid.lower():
                si.setIp(p[1])
                ip = p[1]
                break

        self.logKnownIp(service, ip)
        service.updateData(si)

    def doLog(self, service, data, origin=log.OSMANAGER):
        # Stores a log associated with this service
        try:
            msg, level = data.split('\t')
            try:
                level = int(level)
            except Exception:
                logger.debug('Do not understand level {}'.format(level))
                level = log.INFO

            log.doLog(service, level, msg, origin)
        except Exception:
            logger.exception('WindowsOs Manager message log: ')
            log.doLog(service, log.ERROR, "do not understand {0}".format(data),
                      origin)

    # default "ready received" does nothing
    def readyReceived(self, userService, data):
        pass

    def process(self, userService, msg, data, options=None):
        """
        We understand this messages:
        * msg = info, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class) (old method)
        * msg = information, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class) (new method)
        * msg = logon, data = Username, Informs that the username has logged in inside the machine
        * msg = logoff, data = Username, Informs that the username has logged out of the machine
        * msg = ready, data = None, Informs machine ready to be used
        """
        logger.info(
            "Invoked WindowsOsManager for {0} with params: {1},{2}".format(
                userService, msg, data))

        if msg in ('ready', 'ip'):
            if not isinstance(
                    data, dict
            ):  # Old actors, previous to 2.5, convert it information..
                data = {
                    'ips': [v.split('=') for v in data.split(',')],
                    'hostname': userService.friendly_name
                }

        # We get from storage the name for this userService. If no name, we try to assign a new one
        ret = "ok"
        notifyReady = False
        doRemove = False
        state = userService.os_state
        if msg == "info":
            ret = self.infoVal(userService)
            state = State.PREPARING
        elif msg == "information":
            ret = self.infoValue(userService)
            state = State.PREPARING
        elif msg == "log":
            self.doLog(userService, data, log.ACTOR)
        elif msg == "logon" or msg == 'login':
            if '\\' not in data:
                self.loggedIn(userService, data)
            userService.setInUse(True)
            # We get the userService logged hostname & ip and returns this
            ip, hostname = userService.getConnectionSource()
            deadLine = userService.deployed_service.getDeadline()
            if userService.getProperty('actor_version', '0.0.0') >= '2.0.0':
                ret = "{0}\t{1}\t{2}".format(
                    ip, hostname, 0 if deadLine is None else deadLine)
            else:
                ret = "{0}\t{1}".format(ip, hostname)
        elif msg == "logoff" or msg == 'logout':
            self.loggedOut(userService, data)
            doRemove = self.isRemovableOnLogout(userService)
        elif msg == "ip":
            # This ocurss on main loop inside machine, so userService is usable
            state = State.USABLE
            self.notifyIp(userService.unique_id, userService, data)
        elif msg == "ready":
            self.toReady(userService)
            state = State.USABLE
            notifyReady = True
            self.notifyIp(userService.unique_id, userService, data)
            self.readyReceived(userService, data)

        userService.setOsState(state)

        # If notifyReady is not true, save state, let UserServiceManager do it for us else
        if doRemove is True:
            userService.release()
        else:
            if notifyReady is False:
                userService.save(update_fields=[
                    'in_use', 'in_use_date', 'os_state', 'state', 'data'
                ])
            else:
                logger.debug('Notifying ready')
                UserServiceManager.manager().notifyReadyFromOsManager(
                    userService, '')
        logger.debug('Returning {} to {} message'.format(ret, msg))
        if options is not None and options.get('scramble', True) is False:
            return ret
        return scrambleMsg(ret)

    def processUserPassword(self, service, username, password):
        if service.getProperty('sso_available') == '1':
            # Generate a ticket, store it and return username with no password
            domain = ''
            if '@' in username:
                username, domain = username.split('@')
            elif '\\' in username:
                username, domain = username.split('\\')

            creds = {
                'username': username,
                'password': password,
                'domain': domain
            }
            ticket = TicketStore.create(
                creds, validator=None,
                validity=300)  # , owner=SECURE_OWNER, secure=True)
            return ticket, ''
        else:
            return osmanagers.OSManager.processUserPassword(
                self, service, username, password)

    def processUnused(self, userService):
        """
        This will be invoked for every assigned and unused user service that has been in this state at least 1/2 of Globalconfig.CHECK_UNUSED_TIME
        This function can update userService values. Normal operation will be remove machines if this state is not valid
        """
        if self.isRemovableOnLogout(userService):
            userService.remove()

    def isPersistent(self):
        return self._onLogout == 'keep-always'

    def checkState(self, service):
        logger.debug('Checking state for service {0}'.format(service))
        return State.RUNNING

    def maxIdle(self):
        """
        On production environments, will return no idle for non removable machines
        """
        if self._idle <= 0:  # or (settings.DEBUG is False and self._onLogout != 'remove'):
            return None

        return self._idle

    def marshal(self):
        """
        Serializes the os manager data so we can store it in database
        """
        return '\t'.join(['v2', self._onLogout,
                          str(self._idle)]).encode('utf8')

    def unmarshal(self, s):
        data = s.decode('utf8').split('\t')
        try:
            if data[0] == 'v1':
                self._onLogout = data[1]
                self._idle = -1
            elif data[0] == 'v2':
                self._onLogout, self._idle = data[1], int(data[2])
        except Exception:
            logger.exception(
                'Exception unmarshalling. Some values left as default ones')

        self.__setProcessUnusedMachines()

    def valuesDict(self):
        return {'onLogout': self._onLogout, 'idle': self._idle}
コード例 #6
0
ファイル: BaseX2GOTransport.py プロジェクト: techkie/openuds
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
コード例 #7
0
ファイル: NXTransport.py プロジェクト: j4ackwolf/openuds
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)
コード例 #8
0
ファイル: TRDPTransport.py プロジェクト: borzel/openuds
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)
コード例 #9
0
ファイル: Authenticator.py プロジェクト: aiminickwong/openuds
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
コード例 #10
0
class PoolPerformanceReport(StatsReport):
    filename = 'pools_performance.pdf'
    name = _('Pools performance by date')  # Report name
    description = _('Pools performance report by date')  # Report description
    uuid = '88932b48-1fd3-11e5-a776-10feed05884b'

    # Input fields
    pools = gui.MultiChoiceField(order=1,
                                 label=_('Pools'),
                                 tooltip=_('Pools for report'),
                                 required=True)

    startDate = gui.DateField(order=2,
                              label=_('Starting date'),
                              tooltip=_('starting date for report'),
                              defvalue=datetime.date.min,
                              required=True)

    endDate = gui.DateField(order=3,
                            label=_('Finish date'),
                            tooltip=_('finish date for report'),
                            defvalue=datetime.date.max,
                            required=True)

    samplingPoints = gui.NumericField(
        order=4,
        label=_('Number of intervals'),
        length=3,
        minValue=0,
        maxValue=32,
        tooltip=_('Number of sampling points used in charts'),
        defvalue='8')

    def initialize(self, values):
        pass

    def initGui(self):
        logger.debug('Initializing gui')
        vals = [
            gui.choiceItem(v.uuid, v.name) for v in ServicePool.objects.all()
        ]
        self.pools.setValues(vals)

    def getRangeData(self):
        start = self.startDate.stamp()
        end = self.endDate.stamp()

        if self.samplingPoints.num() < 2:
            self.samplingPoints.value = (self.endDate.date() -
                                         self.startDate.date()).days
        if self.samplingPoints.num() < 2:
            self.samplingPoints.value = 2
        if self.samplingPoints.num() > 32:
            self.samplingPoints.value = 32

        samplingPoints = self.samplingPoints.num()

        pools = [(v.id, v.name)
                 for v in ServicePool.objects.filter(uuid__in=self.pools.value)
                 ]
        if len(pools) == 0:
            raise Exception(_('Select at least a service pool for the report'))

        logger.debug('Pools: {}'.format(pools))

        # x axis label format
        if end - start > 3600 * 24 * 2:
            xLabelFormat = 'SHORT_DATE_FORMAT'
        else:
            xLabelFormat = 'SHORT_DATETIME_FORMAT'

        # Generate samplings interval
        samplingIntervals = []
        prevVal = None
        for val in range(start, end, (end - start) / (samplingPoints + 1)):
            if prevVal is None:
                prevVal = val
                continue
            samplingIntervals.append((prevVal, val))
            prevVal = val

        # Store dataUsers for all pools
        poolsData = []

        fld = events.statsManager().getEventFldFor('username')

        reportData = []
        for p in pools:
            dataUsers = []
            dataAccesses = []
            for interval in samplingIntervals:
                key = (interval[0] + interval[1]) / 2
                q = events.statsManager().getEvents(
                    events.OT_DEPLOYED,
                    events.ET_ACCESS,
                    since=interval[0],
                    to=interval[1],
                    owner_id=p[0]).values(fld).annotate(cnt=Count(fld))
                accesses = 0
                for v in q:
                    accesses += v['cnt']

                dataUsers.append((key, len(q)))  # @UndefinedVariable
                dataAccesses.append((key, accesses))
                reportData.append({
                    'name':
                    p[1],
                    'date':
                    tools.timestampAsStr(interval[0], xLabelFormat) + ' - ' +
                    tools.timestampAsStr(interval[1], xLabelFormat),
                    'users':
                    len(q),
                    'accesses':
                    accesses
                })
            poolsData.append({
                'pool': p[0],
                'name': p[1],
                'dataUsers': dataUsers,
                'dataAccesses': dataAccesses,
            })

        return (xLabelFormat, poolsData, reportData)

    def generate(self):
        # Generate the sampling intervals and get dataUsers from db
        start = self.startDate.stamp()
        end = self.endDate.stamp()

        xLabelFormat, poolsData, reportData = self.getRangeData()

        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH,
                                     HEIGHT)  # @UndefinedVariable

        options = {
            'encoding': 'utf-8',
            'axis': {
                'x': {
                    'ticks': [
                        dict(v=i,
                             label=filters.date(
                                 datetime.datetime.fromtimestamp(l),
                                 xLabelFormat)) for i, l in enumerate(
                                     range(start, end, (end - start) /
                                           self.samplingPoints.num()))
                    ],
                    'range': (0, self.samplingPoints.num()),
                    'showLines':
                    True,
                },
                'y': {
                    'tickCount': 10,
                    'showLines': True,
                },
                'tickFontSize': 16,
            },
            'background': {
                'chartColor': '#f0f0f0',
                'baseColor': '#f0f0f0',
                'lineColor': '#187FF2'
            },
            'colorScheme': {
                'name': 'rainbow',
                'args': {
                    'initialColor': 'blue',
                },
            },
            'legend': {
                'hide': False,
                'legendFontSize': 16,
                'position': {
                    'left': 96,
                    'top': 40,
                }
            },
            'padding': {
                'left': 48,
                'top': 16,
                'right': 48,
                'bottom': 48,
            },
            'title': _('Users by pool'),
        }

        # chart = pycha.line.LineChart(surface, options)
        # chart = pycha.bar.VerticalBarChart(surface, options)
        chart = pycha.stackedbar.StackedVerticalBarChart(surface, options)

        dataset = []
        for pool in poolsData:
            logger.debug(pool['dataUsers'])
            ds = list((i, l[1]) for i, l in enumerate(pool['dataUsers']))
            logger.debug(ds)
            dataset.append((ugettext('Users for {}').format(pool['name']), ds))

        logger.debug('Dataset: {}'.format(dataset))
        chart.addDataset(dataset)

        chart.render()

        img = PILImage.frombuffer("RGBA",
                                  (surface.get_width(), surface.get_height()),
                                  surface.get_data(), "raw", "BGRA", 0, 1)

        # Accesses
        chart = pycha.stackedbar.StackedVerticalBarChart(surface, options)

        dataset = []
        for pool in poolsData:
            logger.debug(pool['dataAccesses'])
            ds = list((i, l[1]) for i, l in enumerate(pool['dataAccesses']))
            logger.debug(ds)
            dataset.append(
                (ugettext('Accesses for {}').format(pool['name']), ds))

        logger.debug('Dataset: {}'.format(dataset))
        chart.addDataset(dataset)

        chart.render()

        img2 = PILImage.frombuffer("RGBA",
                                   (surface.get_width(), surface.get_height()),
                                   surface.get_data(), "raw", "BGRA", 0, 1)

        # Generate Data for pools, basically joining all pool data

        queryset = [{'image': img, 'image2': img2, 'data': reportData}]

        logger.debug(queryset)

        output = six.StringIO()

        try:
            report = AccessReport(queryset=queryset)
            report.title = ugettext('UDS Pools Performance Report')
            report.generate_by(PDFGenerator, filename=output)
            return output.getvalue()
        except Exception:
            logger.exception('Errool')
            return None
コード例 #11
0
class WindowsOsManager(osmanagers.OSManager):
    typeName = _('Windows Basic OS Manager')
    typeType = 'WindowsManager'
    typeDescription = _(
        'Os Manager to control windows machines without domain. (Basically renames machine)'
    )
    iconFile = 'wosmanager.png'

    onLogout = gui.ChoiceField(
        label=_('On Logout'),
        order=10,
        rdonly=True,
        tooltip=_('What to do when user logs out from service'),
        values=[{
            'id': 'keep',
            'text': _('Keep service assigned')
        }, {
            'id': 'remove',
            'text': _('Remove service')
        }],
        defvalue='keep')

    idle = gui.NumericField(
        label=_("Max.Idle time"),
        length=4,
        defvalue=-1,
        rdonly=False,
        order=11,
        tooltip=
        _('Maximum idle time (in seconds) before session is automaticatlly closed to the user (<= 0 means no max. idle time)'
          ),
        required=True)

    @staticmethod
    def validateLen(length):
        try:
            length = int(length)
        except Exception:
            raise osmanagers.OSManager.ValidationException(
                _('Length must be numeric!!'))
        if length > 6 or length < 1:
            raise osmanagers.OSManager.ValidationException(
                _('Length must be betwen 1 and 6'))
        return length

    def __setProcessUnusedMachines(self):
        self.processUnusedMachines = self._onLogout == 'remove'

    def __init__(self, environment, values):
        super(WindowsOsManager, self).__init__(environment, values)
        if values is not None:
            self._onLogout = values['onLogout']
            self._idle = int(values['idle'])
        else:
            self._onLogout = ''
            self._idle = -1

        self.__setProcessUnusedMachines()

    def release(self, service):
        pass

    def getName(self, service):
        '''
        gets name from deployed
        '''
        return service.getName()

    def infoVal(self, service):
        return 'rename:' + self.getName(service)

    def infoValue(self, service):
        return 'rename\r' + self.getName(service)

    def notifyIp(self, uid, service, data):
        si = service.getInstance()

        ip = ''
        # Notifies IP to deployed
        pairs = data.split(',')
        for p in pairs:
            key, val = p.split('=')
            if key.lower() == uid.lower():
                si.setIp(val)
                ip = val
                break

        self.logKnownIp(service, ip)
        service.updateData(si)

    def doLog(self, service, data, origin=log.OSMANAGER):
        # Stores a log associated with this service
        try:
            msg, level = data.split('\t')
            try:
                level = int(level)
            except Exception:
                logger.debug('Do not understand level {}'.format(level))
                level = log.INFO

            log.doLog(service, level, msg, origin)
        except Exception:
            logger.exception('LinuxOs Manager message log: ')
            log.doLog(service, log.ERROR, "do not understand {0}".format(data),
                      origin)

    def process(self, service, msg, data, options):
        '''
        We understand this messages:
        * msg = info, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class) (old method)
        * msg = information, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class) (new method)
        * msg = logon, data = Username, Informs that the username has logged in inside the machine
        * msg = logoff, data = Username, Informs that the username has logged out of the machine
        * msg = ready, data = None, Informs machine ready to be used
        '''
        logger.info(
            "Invoked WindowsOsManager for {0} with params: {1},{2}".format(
                service, msg, data))
        # We get from storage the name for this service. If no name, we try to assign a new one
        ret = "ok"
        notifyReady = False
        doRemove = False
        state = service.os_state
        if msg == "info":
            ret = self.infoVal(service)
            state = State.PREPARING
        elif msg == "information":
            ret = self.infoValue(service)
            state = State.PREPARING
        elif msg == "log":
            self.doLog(service, data, log.ACTOR)
        elif msg == "logon" or msg == 'login':
            if '\\' not in data:
                self.loggedIn(service, data, False)
            service.setInUse(True)
            # We get the service logged hostname & ip and returns this
            ip, hostname = service.getConnectionSource()
            ret = "{0}\t{1}".format(ip, hostname)
        elif msg == "logoff" or msg == 'logout':
            self.loggedOut(service, data, False)
            if self._onLogout == 'remove':
                doRemove = True
        elif msg == "ip":
            # This ocurss on main loop inside machine, so service is usable
            state = State.USABLE
            self.notifyIp(service.unique_id, service, data)
        elif msg == "ready":
            state = State.USABLE
            notifyReady = True
            self.notifyIp(service.unique_id, service, data)

        service.setOsState(state)

        # If notifyReady is not true, save state, let UserServiceManager do it for us else
        if doRemove is True:
            service.remove()
        else:
            if notifyReady is False:
                service.save()
            else:
                logger.debug('Notifying ready')
                UserServiceManager.manager().notifyReadyFromOsManager(
                    service, '')
        logger.debug('Returning {} to {} message'.format(ret, msg))
        if options is not None and options.get('scramble', True) is False:
            return ret
        return scrambleMsg(ret)

    def processUnused(self, userService):
        '''
        This will be invoked for every assigned and unused user service that has been in this state at least 1/2 of Globalconfig.CHECK_UNUSED_TIME
        This function can update userService values. Normal operation will be remove machines if this state is not valid
        '''
        if self._onLogout == 'remove':
            userService.remove()

    def checkState(self, service):
        logger.debug('Checking state for service {0}'.format(service))
        return State.RUNNING

    def maxIdle(self):
        '''
        On production environments, will return no idle for non removable machines
        '''
        if self._idle <= 0 or (settings.DEBUG is False
                               and self._onLogout != 'remove'):
            return None

        return self._idle

    def marshal(self):
        '''
        Serializes the os manager data so we can store it in database
        '''
        return '\t'.join(['v2', self._onLogout, six.text_type(self._idle)])

    def unmarshal(self, s):
        data = s.split('\t')
        try:
            if data[0] == 'v1':
                self._onLogout = data[1]
                self._idle = -1
            elif data[0] == 'v2':
                self._onLogout, self._idle = data[1], int(data[2])
        except Exception:
            logger.exception(
                'Exception unmarshalling. Some values left as default ones')

        self.__setProcessUnusedMachines()

    def valuesDict(self):
        return {'onLogout': self._onLogout, 'idle': self._idle}
コード例 #12
0
ファイル: X2GOTransport.py プロジェクト: aiminickwong/openuds
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)
コード例 #13
0
ファイル: Authenticator.py プロジェクト: spofa/openuds
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")
        ]
コード例 #14
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
コード例 #15
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")
        ]
コード例 #16
0
ファイル: LinuxOsManager.py プロジェクト: techkie/openuds
class LinuxOsManager(osmanagers.OSManager):
    typeName = _('Linux OS Manager')
    typeType = 'LinuxManager'
    typeDescription = _('Os Manager to control Linux virtual machines')
    iconFile = 'losmanager.png'

    servicesType = (serviceTypes.VDI, )

    onLogout = gui.ChoiceField(
        label=_('Logout Action'),
        order=10,
        rdonly=True,
        tooltip=_('What to do when user logs out from service'),
        values=[
            {
                'id': 'keep',
                'text': ugettext_lazy('Keep service assigned')
            },
            {
                'id': 'remove',
                'text': ugettext_lazy('Remove service')
            },
            {
                'id':
                'keep-always',
                'text':
                ugettext_lazy('Keep service assigned even on new publication')
            },
        ],
        defvalue='keep')

    idle = gui.NumericField(
        label=_("Max.Idle time"),
        length=4,
        defvalue=-1,
        rdonly=False,
        order=11,
        tooltip=
        _('Maximum idle time (in seconds) before session is automatically closed to the user (<= 0 means no max idle time).'
          ),
        required=True)

    def __setProcessUnusedMachines(self):
        self.processUnusedMachines = self._onLogout == 'remove'

    def __init__(self, environment, values):
        super(LinuxOsManager, self).__init__(environment, values)
        if values is not None:
            self._onLogout = values['onLogout']
            self._idle = int(values['idle'])
        else:
            self._onLogout = ''
            self._idle = -1

        self.__setProcessUnusedMachines()

    def release(self, service):
        pass

    def isRemovableOnLogout(self, userService):
        '''
        Says if a machine is removable on logout
        '''
        if userService.in_use == False:
            if (self._onLogout
                    == 'remove') or (not userService.isValidPublication()
                                     and self._onLogout == 'keep'):
                return True

        return False

    def getName(self, service):
        """
        gets name from deployed
        """
        return service.getName()

    def infoVal(self, service):
        return 'rename:' + self.getName(service)

    def infoValue(self, service):
        return 'rename\r' + self.getName(service)

    def notifyIp(self, uid, service, data):
        si = service.getInstance()

        ip = ''
        # Notifies IP to deployed
        for p in data['ips']:
            if p[0].lower() == uid.lower():
                si.setIp(p[1])
                ip = p[1]
                break

        self.logKnownIp(service, ip)
        service.updateData(si)

    def doLog(self, service, data, origin=log.OSMANAGER):
        # Stores a log associated with this service
        try:
            msg, level = data.split('\t')
            try:
                level = int(level)
            except Exception:
                logger.debug('Do not understand level {}'.format(level))
                level = log.INFO
            log.doLog(service, level, msg, origin)
        except Exception:
            log.doLog(service, log.ERROR, "do not understand {0}".format(data),
                      origin)

    def process(self, userService, msg, data, options=None):
        """
        We understand this messages:
        * msg = info, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class), old method
        * msg = information, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class), new method
        * msg = logon, data = Username, Informs that the username has logged in inside the machine
        * msg = logoff, data = Username, Informs that the username has logged out of the machine
        * msg = ready, data = None, Informs machine ready to be used
        """
        logger.info(
            "Invoked LinuxOsManager for {0} with params: {1},{2}".format(
                userService, msg, data))
        # We get from storage the name for this userService. If no name, we try to assign a new one
        ret = "ok"
        notifyReady = False
        doRemove = False
        state = userService.os_state
        if msg in ('ready', 'ip'):
            if not isinstance(
                    data, dict
            ):  # Old actors, previous to 2.5, convert it information..
                data = {
                    'ips': [v.split('=') for v in data.split(',')],
                    'hostname': userService.friendly_name
                }

        # Old "info" state, will be removed in a near future
        if msg == "info":
            ret = self.infoVal(userService)
            state = State.PREPARING
        elif msg == "information":
            ret = self.infoValue(userService)
            state = State.PREPARING
        elif msg == "log":
            self.doLog(userService, data, log.ACTOR)
        elif msg == "login":
            self.loggedIn(userService, data, False)
            ip, hostname = userService.getConnectionSource()
            deadLine = userService.deployed_service.getDeadline()
            ret = "{0}\t{1}\t{2}".format(ip, hostname,
                                         0 if deadLine is None else deadLine)
        elif msg == "logout":
            self.loggedOut(userService, data, False)
            doRemove = self.isRemovableOnLogout(userService)
        elif msg == "ip":
            # This ocurss on main loop inside machine, so userService is usable
            state = State.USABLE
            self.notifyIp(userService.unique_id, userService, data)
        elif msg == "ready":
            self.toReady(userService)
            state = State.USABLE
            notifyReady = True
            self.notifyIp(userService.unique_id, userService, data)

        userService.setOsState(state)

        # If notifyReady is not true, save state, let UserServiceManager do it for us else
        if doRemove is True:
            userService.release()
        else:
            if notifyReady is False:
                userService.save()
            else:
                UserServiceManager.manager().notifyReadyFromOsManager(
                    userService, '')
        logger.debug('Returning {0}'.format(ret))
        return ret

    def processUnused(self, userService):
        """
        This will be invoked for every assigned and unused user service that has been in this state at least 1/2 of Globalconfig.CHECK_UNUSED_TIME
        This function can update userService values. Normal operation will be remove machines if this state is not valid
        """
        if self.isRemovableOnLogout(userService):
            userService.remove()

    def isPersistent(self):
        return self._onLogout == 'keep-always'

    def checkState(self, service):
        logger.debug('Checking state for service {0}'.format(service))
        return State.RUNNING

    def maxIdle(self):
        """
        On production environments, will return no idle for non removable machines
        """
        if self._idle <= 0:  # or (settings.DEBUG is False and self._onLogout != 'remove'):
            return None

        return self._idle

    def marshal(self):
        """
        Serializes the os manager data so we can store it in database
        """
        return '\t'.join(['v2', self._onLogout,
                          six.text_type(self._idle)]).encode('utf8')

    def unmarshal(self, s):
        data = s.decode('utf8').split('\t')
        if data[0] == 'v1':
            self._onLogout = data[1]
            self._idle = -1
        elif data[0] == 'v2':
            self._onLogout, self._idle = data[1], int(data[2])

        self.__setProcessUnusedMachines()

    def valuesDict(self):
        return {'onLogout': self._onLogout, 'idle': self._idle}
コード例 #17
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")]
コード例 #18
0
ファイル: TSNXTransport.py プロジェクト: borzel/openuds
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),
        )
コード例 #19
0
ファイル: login.py プロジェクト: spofa/openuds
class StatsReportLogin(StatsReport):
    filename = 'access.pdf'
    name = _('Users access report by date')  # Report name
    description = _(
        'Report of user access to platform by date')  # Report description
    uuid = '0f62f19a-f166-11e4-8f59-10feed05884b'

    # Input fields
    startDate = gui.DateField(order=1,
                              label=_('Starting date'),
                              tooltip=_('starting date for report'),
                              defvalue=datetime.date.min,
                              required=True)

    endDate = gui.DateField(order=2,
                            label=_('Finish date'),
                            tooltip=_('finish date for report'),
                            defvalue=datetime.date.max,
                            required=True)

    samplingPoints = gui.NumericField(
        order=3,
        label=_('Number of intervals'),
        length=3,
        minValue=0,
        maxValue=128,
        tooltip=_('Number of sampling points used in charts'),
        defvalue='64')

    def initialize(self, values):
        pass

    def initGui(self):
        pass

    def getRangeData(self):
        start = self.startDate.stamp()
        end = self.endDate.stamp()
        if self.samplingPoints.num() < 8:
            self.samplingPoints.value = (self.endDate.date() -
                                         self.startDate.date()).days
        if self.samplingPoints.num() < 2:
            self.samplingPoints.value = 2
        if self.samplingPoints.num() > 128:
            self.samplingPoints.value = 128

        samplingPoints = self.samplingPoints.num()

        # x axis label format
        if end - start > 3600 * 24 * 2:
            xLabelFormat = 'SHORT_DATE_FORMAT'
        else:
            xLabelFormat = 'SHORT_DATETIME_FORMAT'

        samplingIntervals = []
        prevVal = None
        for val in range(start, end, (end - start) / (samplingPoints + 1)):
            if prevVal is None:
                prevVal = val
                continue
            samplingIntervals.append((prevVal, val))
            prevVal = val

        data = []
        reportData = []
        for interval in samplingIntervals:
            key = (interval[0] + interval[1]) / 2
            val = events.statsManager().getEvents(events.OT_AUTHENTICATOR,
                                                  events.ET_LOGIN,
                                                  since=interval[0],
                                                  to=interval[1]).count()
            data.append((key, val))  # @UndefinedVariable
            reportData.append({
                'date':
                tools.timestampAsStr(interval[0], xLabelFormat) + ' - ' +
                tools.timestampAsStr(interval[1], xLabelFormat),
                'users':
                val
            })

        return (xLabelFormat, data, reportData)

    def getWeekHourlyData(self):
        start = self.startDate.stamp()
        end = self.endDate.stamp()

        dataWeek = [0] * 7
        dataHour = [0] * 24
        for val in events.statsManager().getEvents(events.OT_AUTHENTICATOR,
                                                   events.ET_LOGIN,
                                                   since=start,
                                                   to=end):
            s = datetime.datetime.fromtimestamp(val.stamp)
            dataWeek[s.weekday()] += 1
            dataHour[s.hour] += 1

        return (dataWeek, dataHour)

    def generate(self):
        # Sample query:
        #   'SELECT *, count(*) as number, CEIL(stamp/(3600))*3600 as block'
        #   ' FROM {table}'
        #   ' WHERE event_type = 0 and stamp >= {start} and stamp <= {end}'
        #   ' GROUP BY CEIL(stamp/(3600))'
        #   ' ORDER BY block'

        # Generate the sampling intervals and get data from db
        start = self.startDate.stamp()
        end = self.endDate.stamp()

        xLabelFormat, data, reportData = self.getRangeData()

        #
        # User access by date graph
        #

        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH,
                                     HEIGHT)  # @UndefinedVariable

        dataset = ((ugettext('Users access to UDS'), data), )

        options = {
            'encoding': 'utf-8',
            'axis': {
                'x': {
                    'ticks': [
                        dict(v=i,
                             label=filters.date(
                                 datetime.datetime.fromtimestamp(i),
                                 xLabelFormat))
                        for i in range(start, end, (end - start) / 11)
                    ],
                    'range': (start, end),
                    'showLines':
                    True,
                },
                'y': {
                    'tickCount': 10,
                    'showLines': True,
                },
                'tickFontSize': 16,
            },
            'background': {
                'chartColor': '#f0f0f0',
                'baseColor': '#f0f0f0',
                'lineColor': '#187FF2'
            },
            'colorScheme': {
                'name': 'gradient',
                'args': {
                    'initialColor': '#B8CA16',
                },
            },
            'legend': {
                'hide': False,
                'legendFontSize': 16,
                'position': {
                    'left': 48,
                    'bottom': 8,
                }
            },
            'padding': {
                'left': 48,
                'top': 16,
                'right': 48,
                'bottom': 48,
            },
            'title': _('Users access to UDS')
        }

        chart = pycha.line.LineChart(surface, options)
        chart.addDataset(dataset)
        chart.render()

        img = PILImage.frombuffer("RGBA",
                                  (surface.get_width(), surface.get_height()),
                                  surface.get_data(), "raw", "BGRA", 0, 1)

        #
        # User access by day of week
        #
        dataWeek, dataHour = self.getWeekHourlyData()

        dataset = ((ugettext('Users access to UDS'), [(i, dataWeek[i])
                                                      for i in range(0, 7)]), )

        options['axis'] = {
            'x': {
                'ticks':
                [dict(v=i, label='Day {}'.format(i)) for i in range(0, 7)],
                'range': (0, 6),
                'showLines': True,
            },
            'y': {
                'tickCount': 10,
                'showLines': True,
            },
            'tickFontSize': 16,
        }

        chart = pycha.bar.VerticalBarChart(surface, options)
        chart.addDataset(dataset)
        chart.render()

        img2 = PILImage.frombuffer("RGBA",
                                   (surface.get_width(), surface.get_height()),
                                   surface.get_data(), "raw", "BGRA", 0, 1)

        # Hourly chart
        dataset = ((ugettext('Users access to UDS'),
                    [(i, dataHour[i]) for i in range(0, 24)]), )

        options['axis'] = {
            'x': {
                'ticks':
                [dict(v=i, label='{}:00'.format(i)) for i in range(0, 24)],
                'range': (0, 24),
                'showLines': True,
            },
            'y': {
                'tickCount': 10,
                'showLines': True,
            },
            'tickFontSize': 16,
        }

        chart = pycha.bar.VerticalBarChart(surface, options)
        chart.addDataset(dataset)
        chart.render()

        img3 = PILImage.frombuffer("RGBA",
                                   (surface.get_width(), surface.get_height()),
                                   surface.get_data(), "raw", "BGRA", 0, 1)

        output = six.StringIO()

        queryset = [{
            'image': img,
            'image2': img2,
            'image3': img3,
            'data': reportData
        }]

        logger.debug(queryset)

        try:
            report = AccessReport(queryset=queryset)
            report.title = ugettext('Users access to UDS')
            # report = UsersReport(queryset=users)
            report.generate_by(PDFGenerator, filename=output)
            return output.getvalue()
        except Exception:
            logger.exception('Errool')
            return None
コード例 #20
0
ファイル: Authenticator.py プロジェクト: keelzhang/openuds
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")]
コード例 #21
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)