AMP Authentication

Overview

epsilon.ampauth integrates Twisted Cred with twisted.protocols.amp , providing support for selecting a IBoxReceiver based on the result of a Cred login.

Readers should familiarize themselves with the following concepts in order to understand all sections of this document:

Servers

CredAMPServerFactory is a factory for the CredReceiver protocol, an AMP subclass which implements responders for commands which allow a client to prove their identity. It uses a Portal to handle these commands and retrieve an IBoxReceiver which will be used to handle all further AMP boxes it receives.

# Copyright (c) 2008 Divmod.  See LICENSE for details.

"""
An AMP server which requires authentication of its clients before exposing an
addition command.
"""

from sys import stdout

from twisted.python.log import startLogging, msg
from twisted.internet import reactor
from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
from twisted.cred.portal import Portal
from twisted.protocols.amp import IBoxReceiver, Command, Integer, AMP

from epsilon.ampauth import CredAMPServerFactory


class Add(Command):
    """
    An example of an application-defined command which should be made available
    to clients after they successfully authenticate.
    """
    arguments = [("left", Integer()),
                 ("right", Integer())]

    response = [("sum", Integer())]



class Adder(AMP):
    """
    An example of an application-defined AMP protocol, the responders defined
    by which should only be available to clients after they have successfully
    authenticated.
    """
    def __init__(self, avatarId):
        AMP.__init__(self)
        self.avatarId = avatarId


    @Add.responder
    def add(self, left, right):
        msg("Adding %d to %d for %s" % (left, right, self.avatarId))
        return {'sum': left + right}



class AdditionRealm(object):
    """
    An example of an application-defined realm.
    """
    def requestAvatar(self, avatarId, mind, *interfaces):
        """
        Create Adder avatars for any IBoxReceiver request.
        """
        if IBoxReceiver in interfaces:
            return (IBoxReceiver, Adder(avatarId), lambda: None)
        raise NotImplementedError()



def main():
    """
    Start the AMP server and the reactor.
    """
    startLogging(stdout)
    checker = InMemoryUsernamePasswordDatabaseDontUse()
    checker.addUser("testuser", "examplepass")
    realm = AdditionRealm()
    factory = CredAMPServerFactory(Portal(realm, [checker]))
    reactor.listenTCP(7805, factory)
    reactor.run()


if __name__ == '__main__':
    main()

Add and Adder together define a simple AMP protocol for adding two integers together. AdditionRealm provides the necessary integration between this AMP protocol and Cred, creating new Adder instances whenever an IBoxReceiver is requested - which will be whenever a client attempts to authenticate itself to the server.

Clients

AMP clients can authenticate with an AMP server using login . login takes a connected AMP instance and a credentials object as arguments and returns a Deferred which fires when authentication finishes.

# Copyright (c) 2008 Divmod.  See LICENSE for details.

"""
An AMP client which connects to and authenticates with an AMP server, then
issues a command.
"""

from twisted.internet.protocol import ClientCreator
from twisted.cred.credentials import UsernamePassword
from twisted.protocols.amp import AMP

from epsilon.react import react
from epsilon.ampauth import login

from auth_server import Add


def add(proto):
    return proto.callRemote(Add, left=17, right=33)


def display(result):
    print result


def main(reactor):
    cc = ClientCreator(reactor, AMP)
    d = cc.connectTCP('localhost', 7805)
    d.addCallback(login, UsernamePassword("testuser", "examplepass"))
    d.addCallback(add)
    d.addCallback(display)
    return d


if __name__ == '__main__':
    from twisted.internet import reactor
    react(reactor, main, [])

The TCP connection is set up as usual, and the Add command is also issued in the usual way. The only change from a normal AMP client is the use of login after a connection has been set up but before any commands are issued.

One-Time Pad Authentication

epsilon.ampauth includes an CredentialsChecker for validating one-time pads: OneTimePadChecker . If this checker is registered with the portal, clients may use the OTPLogin command to authenticate.

# Copyright (c) 2008 Divmod.  See LICENSE for details.

"""
An AMP server which requires authentication of its clients before exposing an
addition command.
"""

from sys import stdout

from twisted.python.log import startLogging, msg
from twisted.internet import reactor
from twisted.cred.portal import Portal
from twisted.protocols.amp import IBoxReceiver, Command, Integer, AMP

from epsilon.ampauth import CredAMPServerFactory, OneTimePadChecker


class Add(Command):
    """
    An example of an application-defined command which should be made available
    to clients after they successfully authenticate.
    """
    arguments = [("left", Integer()),
                 ("right", Integer())]

    response = [("sum", Integer())]



class Adder(AMP):
    """
    An example of an application-defined AMP protocol, the responders defined
    by which should only be available to clients after they have successfully
    authenticated.
    """
    def __init__(self, avatarId):
        AMP.__init__(self)
        self.avatarId = avatarId


    @Add.responder
    def add(self, left, right):
        msg("Adding %d to %d for %s" % (left, right, self.avatarId))
        return {'sum': left + right}



class AdditionRealm(object):
    """
    An example of an application-defined realm.
    """
    def requestAvatar(self, avatarId, mind, *interfaces):
        """
        Create Adder avatars for any IBoxReceiver request.
        """
        if IBoxReceiver in interfaces:
            return (IBoxReceiver, Adder(avatarId), lambda: None)
        raise NotImplementedError()



def main():
    """
    Start the AMP server and the reactor.
    """
    startLogging(stdout)
    checker = OneTimePadChecker({'pad': 0})
    realm = AdditionRealm()
    factory = CredAMPServerFactory(Portal(realm, [checker]))
    reactor.listenTCP(7805, factory)
    reactor.run()


if __name__ == '__main__':
    main()
# Copyright (c) 2008 Divmod.  See LICENSE for details.

"""
An AMP client which connects to and authenticates with an AMP server using OTP,
then issues a command.
"""

from twisted.internet.protocol import ClientCreator
from twisted.cred.credentials import UsernamePassword
from twisted.protocols.amp import AMP

from epsilon.react import react
from epsilon.ampauth import OTPLogin

from auth_server import Add


def add(proto):
    return proto.callRemote(Add, left=17, right=33)


def display(result):
    print result


def otpLogin(client):
    client.callRemote(OTPLogin, pad='pad')
    return client


def main(reactor):
    cc = ClientCreator(reactor, AMP)
    d = cc.connectTCP('localhost', 7805)
    d.addCallback(otpLogin)
    d.addCallback(add)
    d.addCallback(display)
    return d


if __name__ == '__main__':
    from twisted.internet import reactor
    react(reactor, main, [])

Table Of Contents

Previous topic

Index

Next topic

AMP Routes

This Page