HEX
Server: Apache
System: Linux sg241.singhost.net 2.6.32-896.16.1.lve1.4.51.el6.x86_64 #1 SMP Wed Jan 17 13:19:23 EST 2018 x86_64
User: honghock (909)
PHP: 8.0.30
Disabled: passthru,system,shell_exec,show_source,exec,popen,proc_open
Upload Files
File: //usr/lib/python2.7/site-packages/salt/tokens/rediscluster.py
# -*- coding: utf-8 -*-

'''
Provide token storage in Redis cluster.

To get started simply start a redis cluster and assign all hashslots to the connected nodes.
Add the redis hostname and port to master configs as eauth_redis_host and eauth_redis_port.
Default values for these configs are as follow:

.. code-block:: yaml

    eauth_redis_host: localhost
    eauth_redis_port: 6379

:depends:   - redis-py-cluster Python package
'''

from __future__ import absolute_import, print_function, unicode_literals


try:
    import rediscluster
    HAS_REDIS = True
except ImportError:
    HAS_REDIS = False


import os
import logging
import hashlib

import salt.payload

from salt.ext import six

log = logging.getLogger(__name__)

__virtualname__ = 'rediscluster'


def __virtual__():
    if not HAS_REDIS:
        return False, 'Could not use redis for tokens; '\
                      'rediscluster python client is not installed.'
    return __virtualname__


def _redis_client(opts):
    '''
    Connect to the redis host and return a StrictRedisCluster client object.
    If connection fails then return None.
    '''
    redis_host = opts.get("eauth_redis_host", "localhost")
    redis_port = opts.get("eauth_redis_port", 6379)
    try:
        return rediscluster.StrictRedisCluster(host=redis_host, port=redis_port, decode_responses=True)
    except rediscluster.exceptions.RedisClusterException as err:
        log.warning(
            'Failed to connect to redis at %s:%s - %s',
            redis_host, redis_port, err
        )
        return None


def mk_token(opts, tdata):
    '''
    Mint a new token using the config option hash_type and store tdata with 'token' attribute set
    to the token.
    This module uses the hash of random 512 bytes as a token.

    :param opts: Salt master config options
    :param tdata: Token data to be stored with 'token' attirbute of this dict set to the token.
    :returns: tdata with token if successful. Empty dict if failed.
    '''
    redis_client = _redis_client(opts)
    if not redis_client:
        return {}
    hash_type = getattr(hashlib, opts.get('hash_type', 'md5'))
    tok = six.text_type(hash_type(os.urandom(512)).hexdigest())
    try:
        while redis_client.get(tok) is not None:
            tok = six.text_type(hash_type(os.urandom(512)).hexdigest())
    except Exception as err:  # pylint: disable=broad-except
        log.warning(
            'Authentication failure: cannot get token %s from redis: %s',
            tok, err
        )
        return {}
    tdata['token'] = tok
    serial = salt.payload.Serial(opts)
    try:
        redis_client.set(tok, serial.dumps(tdata))
    except Exception as err:  # pylint: disable=broad-except
        log.warning(
            'Authentication failure: cannot save token %s to redis: %s',
            tok, err
        )
        return {}
    return tdata


def get_token(opts, tok):
    '''
    Fetch the token data from the store.

    :param opts: Salt master config options
    :param tok: Token value to get
    :returns: Token data if successful. Empty dict if failed.
    '''
    redis_client = _redis_client(opts)
    if not redis_client:
        return {}
    serial = salt.payload.Serial(opts)
    try:
        tdata = serial.loads(redis_client.get(tok))
        return tdata
    except Exception as err:  # pylint: disable=broad-except
        log.warning(
            'Authentication failure: cannot get token %s from redis: %s',
            tok, err
        )
        return {}


def rm_token(opts, tok):
    '''
    Remove token from the store.

    :param opts: Salt master config options
    :param tok: Token to remove
    :returns: Empty dict if successful. None if failed.
    '''
    redis_client = _redis_client(opts)
    if not redis_client:
        return
    try:
        redis_client.delete(tok)
        return {}
    except Exception as err:  # pylint: disable=broad-except
        log.warning('Could not remove token %s: %s', tok, err)


def list_tokens(opts):
    '''
    List all tokens in the store.

    :param opts: Salt master config options
    :returns: List of dicts (token_data)
    '''
    ret = []
    redis_client = _redis_client(opts)
    if not redis_client:
        return []
    serial = salt.payload.Serial(opts)
    try:
        return [k.decode('utf8') for k in redis_client.keys()]
    except Exception as err:  # pylint: disable=broad-except
        log.warning('Failed to list keys: %s', err)
        return []