Проблема скрученной аутентификации - PullRequest
2 голосов
/ 20 июля 2011

У меня ошибка в pb, cred ...

У нас простой клиент:

#!/usr/bin/env python

from twisted.spread import pb
from twisted.internet import reactor
from twisted.cred import credentials

def main():
    factory = pb.PBClientFactory()
    reactor.connectTCP("localhost", 8801, factory)
    def1 = factory.login(credentials.UsernamePassword("admin", "pass2"))
    def1.addCallback(connected)
    def1.addErrback(bad_connected)
    def1.addBoth(disconnect)
    reactor.run()

def bad_connected(perspective):
    print 'bad login or password', perspective
    perspective.addCallback(disconnect)

def connected(perspective):
    print "got perspective1 ref:", perspective
    print "asking it to foo(13)"

    return perspective.callRemote("foo", 13)

def disconnect(perspective):
    print 'disconnect'
    reactor.stop()

main()

Если мы подключаемся - >спектива.callRemote ("foo", 13) и отключить

Если мы не подключаемся -> вывести «неверный логин или пароль» и отключить

Северный код

#!/usr/bin/env python

from zope.interface import implements
from twisted.python import failure, log
from twisted.cred import portal, checkers, credentials, error as credError
from twisted.internet import defer, reactor
from twisted.spread import pb


class PasswordDictChecker:
    implements(checkers.ICredentialsChecker)
    credentialInterfaces = (credentials.IUsernamePassword,)

    def __init__(self, passwords):
        "passwords: a dict-like object mapping usernames to passwords"
        self.passwords = passwords

    def requestAvatarId(self, credentials):
        username = credentials.username
        if self.passwords.has_key(username):
            if credentials.password == self.passwords[username]:
                return defer.succeed(username)
            else:
                return defer.fail(
                    credError.UnauthorizedLogin("Bad password"))
        else:
            return defer.fail(
                credError.UnauthorizedLogin("No such user"))

class MyRealm(object):
    implements(portal.IRealm)

    def requestAvatar(self, user, mind, *interfaces):
        assert pb.IPerspective in interfaces
        avatar = MyAvatar(user)
        avatar.attached(mind)
        return pb.IPerspective, avatar, lambda a=avatar:a.detached(mind)

class MyAvatar(pb.Avatar):
    def __init__(self, user):
        self.user = user

    def attached(self, mind):
        self.remote = mind
        print 'User %s connected' % (self.user,)

    def detached(self, mind):
        self.remote = None
        print 'User %s disconnected' % (self.user,)

passwords = {
    'admin': 'aaa',
    'user1': 'bbb',
    'user2': 'ccc'
    }

if __name__ == "__main__":
    checker = PasswordDictChecker(passwords)
    realm = MyRealm()
    p = portal.Portal(realm, [checker])

    reactor.listenTCP(8801, pb.PBServerFactory(p))
    reactor.run()

Проблема в том, что при этой записи отображается ошибка:

Unhandled Error
Traceback (most recent call last):
Failure: twisted.cred.error.UnhandledCredentials: No checker for twisted.cred.credentials.IUsernameHashedPassword, twisted.spread.pb.IUsernameMD5Password, twisted.spread.interfaces.IJellyable

Зачем ему IUsernameHashedPassword? Если я изменю на

 credentialInterfaces = (credentials.IUsernamePassword, redentials.IUsernameHashedPassword)

Код выполняется, но умер в строке:

if credentials.password == self.passwords[username]:

Unhandled Error
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\twisted\spread\pb.py", line 841, in _recvMessage
    netResult = object.remoteMessageReceived(self, message, netArgs, netKw)
  File "C:\Python27\lib\site-packages\twisted\spread\flavors.py", line 114, in remoteMessageReceived
    state = method(*args, **kw)
  File "C:\Python27\lib\site-packages\twisted\spread\pb.py", line 1347, in remote_respond
    d = self.portal.login(self, mind, IPerspective)
  File "C:\Python27\lib\site-packages\twisted\cred\portal.py", line 115, in login
    return maybeDeferred(self.checkers[i].requestAvatarId, credentials
--- <exception caught here> ---
  File "C:\Python27\lib\site-packages\twisted\internet\defer.py", line 133, in maybeDeferred
    result = f(*args, **kw)
  File "C:/Dropbox/my_py/network/pb-cred/pb6serverV2.py", line 21, in requestAvatarId
    if credentials.password == self.passwords[username]:
exceptions.AttributeError: _PortalAuthChallenger instance has no attribute 'password'

Пожалуйста, помогите мне понять проблему.

Ответы [ 2 ]

2 голосов
/ 20 июля 2011

Для входа в систему необходимо использовать объект учетных данных twisted.spread.pb.IUsernameMD5Password, поскольку PB Twisted использует небольшую схему запроса / ответа во время аутентификации, которая требует хеширования пароля (MD5). Этот алгоритм в настоящее время жестко запрограммирован в модуле PB. Вы не можете легко внедрить / использовать другие контейнеры учетных данных с PB, если вы не планируете развернуть свой собственный суб-протокол аутентификации.

Этот протокол предназначен для защиты клиента и сервера от атак типа "человек посередине".

Я бы рекомендовал взглянуть на исходный код InMemoryUsernamePasswordDatabaseDontUse для описания того, как контролеры должны проверять переданные им учетные данные (имя этого класса - тонкий намек на то, что использовать класс в производственном сервере ...)

0 голосов
/ 27 июля 2011

Мой простой тестовый сервер использует PB и MySQL

# -*- coding: utf-8 -*-
import MySQLdb
from twisted.cred import portal, checkers, credentials, error as credError
from twisted.protocols import basic
from twisted.internet import protocol, reactor, defer
from zope.interface import Interface, implements
from twisted.spread import pb
from twisted.enterprise import adbapi, util as dbutil


class MyPerspective(pb.Avatar):
    def __init__(self, name):
        self.name = name
    def perspective_foo(self, arg):
        print "I am", self.name, "perspective_foo(",arg,") called on", self
        return arg

class MyRealm:
    implements(portal.IRealm)
    def requestAvatar(self, avatarId, mind, *interfaces):
        #print 'qqqq', avatarId
        if pb.IPerspective not in interfaces:
            raise NotImplementedError
        return pb.IPerspective, MyPerspective(avatarId), lambda:None


class DbPasswordChecker(object):
    implements(checkers.ICredentialsChecker)
    credentialInterfaces = (credentials.IUsernamePassword,
                            credentials.IUsernameHashedPassword)
    def __init__(self, dbconn):
        self.dbconn = dbconn
    def requestAvatarId(self, credentials):
        query = "select userid, password from user where username = %s" % (
            dbutil.quote(credentials.username, "char"))
        return self.dbconn.runQuery(query).addCallback(
            self._gotQueryResults, credentials)
    def _gotQueryResults(self, rows, userCredentials):
        if rows:
            userid, password = rows[0]
            return defer.maybeDeferred(
                userCredentials.checkPassword, password).addCallback(
                self._checkedPassword, userid)
        else:
            raise credError.UnauthorizedLogin, "No such user"
    def _checkedPassword(self, matched, userid):
        if matched:
            return userid
        else:
            raise credError.UnauthorizedLogin("Bad password")

DB_DRIVER = "MySQLdb"
DB_ARGS = {
    'db': 'dbname',
    'user': 'root',
    'passwd': '',
    }

connection = adbapi.ConnectionPool(DB_DRIVER, **DB_ARGS)
p = portal.Portal(MyRealm())
p.registerChecker(DbPasswordChecker(connection))
#p.registerChecker(PasswordDictChecker(passwords))
reactor.listenTCP(8800, pb.PBServerFactory(p))
reactor.run()
...