コード例 #1
0
ファイル: protocol.py プロジェクト: dredozubov/rpac
 def __init__(self, *args, **kwargs):
     self.delimiter = str('\r\n') # explicitly
     self.args = [] # arguments for current command
     self.ready = True # ready to parse next command
     self.authorized = False # is authorized
     self.config = ConfigParser()
     self.command = '' # internal command buffer
     self.re_arg = re.compile('^\*(\d)')
     #self.SINGLE_LINE_RESPONSE = ['PING', ' SET', ' SELECT', ' SAVE', ' BGSAVE', ' SHUTDOWN', ' RENAME', ' LPUSH', ' RPUSH', ' LSET', ' LTRIM']
     #self.NOT_RECV_COMMANDS = ['SUBSCRIBE', 'UNSUBSCRIBE', 'PSUBSCRIBE', 'PUNSUBSCRIBE']
     self.BLOCKING_COMMANDS = ['BLPOP', 'BRPOP', 'SMOVE']
     self.ALL_KEYS_COMMANDS = ['DEL', 'MGET', 'SINTER', 'SINTERSTORE', 'SUNION', 'SUNIONSTORE', 'SDIFF', 'SDIFFSTORE', 'RENAME', 'RENAMENX']
     self.KEYLESS_COMMANDS = ['PING', 'DBSIZE', 'SELECT', 'FLUSHDB', 'FLUSHALL', 'AUTH']
コード例 #2
0
ファイル: protocol.py プロジェクト: dredozubov/rpac
class RedisAuthProxy(LineOnlyReceiver):
    """
        Twisted Protocol class
    """

    def __init__(self, *args, **kwargs):
        self.delimiter = str('\r\n') # explicitly
        self.args = [] # arguments for current command
        self.ready = True # ready to parse next command
        self.authorized = False # is authorized
        self.config = ConfigParser()
        self.command = '' # internal command buffer
        self.re_arg = re.compile('^\*(\d)')
        #self.SINGLE_LINE_RESPONSE = ['PING', ' SET', ' SELECT', ' SAVE', ' BGSAVE', ' SHUTDOWN', ' RENAME', ' LPUSH', ' RPUSH', ' LSET', ' LTRIM']
        #self.NOT_RECV_COMMANDS = ['SUBSCRIBE', 'UNSUBSCRIBE', 'PSUBSCRIBE', 'PUNSUBSCRIBE']
        self.BLOCKING_COMMANDS = ['BLPOP', 'BRPOP', 'SMOVE']
        self.ALL_KEYS_COMMANDS = ['DEL', 'MGET', 'SINTER', 'SINTERSTORE', 'SUNION', 'SUNIONSTORE', 'SDIFF', 'SDIFFSTORE', 'RENAME', 'RENAMENX']
        self.KEYLESS_COMMANDS = ['PING', 'DBSIZE', 'SELECT', 'FLUSHDB', 'FLUSHALL', 'AUTH']

    def resetCommandBuffer(self):
        self.command = u''

    def connectToRedis(self):
        # client is connected to proxy, time to connect to redis itself
        self.redis = RedisProxy(host=self.config.REDIS_HOST, port=self.config.REDIS_PORT, db=self.config.REDIS_DB)
        from time import gmtime
        self.redis.lpush('time', str(gmtime()))

    def checkCommand(self, command):
        if command.upper() in self.config.USER_COMMANDS[self.userconfig['user']]:
            return True
        else:
            return

    def __checkKeys(self, arguments):
        exact_restricted_keys = []
        startswith_restricted_keys = []
        for argument in arguments:

            namespaced = False
            for skey in self.userconfig['startswith_keys']:
                if argument.startswith(skey):
                    namespaced = True
            if not namespaced:
                startswith_restricted_keys.append(argument)

            if argument not in self.userconfig['exact_keys']:
                exact_restricted_keys.append(argument)

        restricted = set(startswith_restricted_keys).intersection(set(exact_restricted_keys))
        return restricted

    def checkKeys(self, command, arguments):
        checked = None
        if command in ['MSET', 'MSETNX']:
            # our main interest is arguments with odd indexes
            restricted_keys = self.__checkKeys(arguments[::2])
        elif command in self.ALL_KEYS_COMMANDS:
            # we must check all arguments
            restricted_keys = self.__checkKeys(arguments)
        else:
            restricted_keys = self.__checkKeys([arguments[0]])
        return restricted_keys

    def checkPermissions(self, command_list):
        command = command_list[0]
        arguments = command_list[1:]
        if self.checkCommand(command):
            if command not in self.KEYLESS_COMMANDS:
                restricted_keys = self.checkKeys(command, arguments)
            else:
                restricted_keys = []
            if not restricted_keys:
                print 'proxyExecute(command): ', command_list
                self.proxyExecute(command_list)
            else:
                self.sendLine('-ERR restricted keys: %s' % ' '.join(restricted_keys))
        elif command in self.BLOCKING_COMMANDS:
            # in case it'll be removed in config.py
            self.sendLine('-ERR blocking commands restricted: %s' % command.upper())
        else:
            self.sendLine('-ERR restricted command: %s' % command.upper())

    def sendLine(self, line):
        if not line:
            line = '$-1'
        if type(line) == 'unicode':
            line = self.safe_unicode(line).encode('utf-8')
        abstract.FileDescriptor.write(self.transport, str(line.rstrip('\r\n')+self.delimiter))

    def sendTerminate(self):
        abstract.FileDescriptor.write(self.transport, self.delimiter)

    def lineReceived(self, line):
        """
            Socket input parsing via redis network protocol
        """
        self.command += line+'\r\n'
        args = self.re_arg.match(self.command)
        if args:
            repeats = int(args.groups()[0])-1
            # DL for delimiter
            acceptable_range = u'\wАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъьэюя:punct:'
            arg_repeats = unicode('\$\d+DL(['+acceptable_range+']+)DL')*repeats
            re_val = unicode('^\*\dDL\$\d+DL([a-zA-Z:punct:]+)DL'+arg_repeats).replace('DL', '\r\n').encode('utf-8')
            regex = re.compile(re_val) # can't replicate effect of acceptable_range with re.U, dunno why
            m = regex.match(self.command)
            if m:
                self.command = self.command[m.end():]
                matches = [c for c in m.groups() if c is not None]
                # twisted breaks while sending unicode, so..
                command = matches[0].encode('utf-8')
                if len(matches) > 1:
                    arguments = list(matches[1:])
                else:
                    arguments = None

                # printing
                if arguments:
                    print '%s %s' % (command, ' '.join(arguments))
                elif command:
                    print command

                if command in ['AUTH', 'auth'] and not self.authorized:
                    # authorization
                    password = arguments[0]
                    self.authClient(password)
                elif self.authorized:
                    self.checkPermissions(m.groups())
                else:
                    self.sendLine('-ERR authorization denied')

    def authClient(self, password):
        """
            Authorization procedure
        """
        self.userconfig = self.config.getConfigByPassword(password)
        print self.userconfig
        # if self.userconfig exists - user exists
        if self.userconfig:
            self.sendLine('+OK')
            self.authorized = True
            self.connectToRedis()
            print 'authorized!'
        else:
            self.sendLine('-ERR invalid password')
            print 'not authorized!'

    def proxyExecute(self, command):
        """
            Command execution via RedisProxy and sending results back to client.
        """
        self.redis.execute_command(*command)
        # this condition must include non-recv commands support etc
        if hasattr(self.redis, 'res_data'):
            self.sendLine(self.redis.res_data)
        else:
            self.sendLine('-ERR wrong command')

    class RedisError(Exception):
        pass

    class AuthError(RedisError):
        pass