~divmod-dev/divmod.org/dangling-1091

« back to all changes in this revision

Viewing changes to Axiom/axiom/userbase.py

  • Committer: glyph
  • Date: 2005-07-28 22:09:16 UTC
  • Revision ID: svn-v4:866e43f7-fbfc-0310-8f2a-ec88d1da2979:trunk:2
move this repository to a more official-looking URL

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: axiom.test.test_userbase -*-
 
2
 
 
3
from twisted.cred.portal import IRealm
 
4
from twisted.cred.credentials import IUsernamePassword, IUsernameHashedPassword
 
5
from twisted.cred.checkers import ICredentialsChecker, ANONYMOUS
 
6
from twisted.cred.error import UnauthorizedLogin
 
7
from twisted.python import log
 
8
 
 
9
from axiom.substore import SubStore
 
10
from axiom.item import Item
 
11
from axiom.attributes import text, bytes, integer, reference, AND
 
12
 
 
13
from zope.interface import implements, Interface
 
14
 
 
15
def dflip(x):
 
16
    l = x.split('.')
 
17
    l.reverse()
 
18
    return '.'.join(l)
 
19
 
 
20
class BadCredentials(UnauthorizedLogin):
 
21
    pass
 
22
 
 
23
class NoSuchUser(UnauthorizedLogin):
 
24
    pass
 
25
 
 
26
class DuplicateUser(Exception):
 
27
    pass
 
28
 
 
29
 
 
30
 
 
31
class LoginAccount(Item):
 
32
    schemaVersion = 1
 
33
    typeName = 'login'
 
34
 
 
35
    username = text(indexed=True)
 
36
    domain = text(indexed=True) # flipped using dflip, e.g. "com.divmod"
 
37
    password = bytes()
 
38
    avatars = reference()       # reference to a thing which can be adapted to
 
39
                                # implementations for application-level
 
40
                                # protocols.  In general this is a reference to
 
41
                                # a SubStore because this is optimized for
 
42
                                # applications where per-user data is a
 
43
                                # substantial portion of the cost.
 
44
    disabled = integer()
 
45
 
 
46
    def __conform__(self, interface):
 
47
        return interface(self.avatars, None)
 
48
 
 
49
class LoginSystem(Item):
 
50
    implements(IRealm, ICredentialsChecker)
 
51
 
 
52
    credentialInterfaces = (IUsernamePassword, IUsernameHashedPassword)
 
53
 
 
54
    schemaVersion = 1
 
55
    typeName = 'login_system'
 
56
 
 
57
    loginCount = integer()
 
58
    failedLogins = integer()
 
59
 
 
60
    def __init__(self, **kw):
 
61
        super(LoginSystem, self).__init__(**kw)
 
62
        self.failedLogins = 0
 
63
        self.loginCount = 0
 
64
 
 
65
    def install(self):
 
66
        self.store.powerUp(self, IRealm)
 
67
        self.store.powerUp(self, ICredentialsChecker)
 
68
 
 
69
    def accountByAddress(self, username, domain):
 
70
        """
 
71
        @type username: C{unicode} without NUL
 
72
        @type domain: C{unicode} without NUL
 
73
        """
 
74
        for account in self.store.query(LoginAccount,
 
75
                                     AND(LoginAccount.domain == dflip(domain),
 
76
                                         LoginAccount.username == username)):
 
77
            return account
 
78
 
 
79
    def addAccount(self, username, domain, password):
 
80
        username = unicode(username)
 
81
        domain = unicode(domain)
 
82
        if self.accountByAddress(username, domain) is not None:
 
83
            raise DuplicateUser(username, domain)
 
84
        return LoginAccount(store=self.store,
 
85
                            username=username,
 
86
                            domain=dflip(domain),
 
87
                            password=password,
 
88
                            avatars=SubStore(self.store,
 
89
                                             ('account', domain, username)),
 
90
                            disabled=0)
 
91
 
 
92
    def logoutFactory(self, obj):
 
93
        return getattr(obj, 'logout', lambda: None)
 
94
 
 
95
    def requestAvatar(self, avatarId, mind, *interfaces):
 
96
        if avatarId is ANONYMOUS:
 
97
            av = self.store
 
98
        else:
 
99
            av = self.store.getItemByID(avatarId)
 
100
        for interface in interfaces:
 
101
            impl = interface(av, None)
 
102
            if impl is not None:
 
103
                self.loginCount += 1
 
104
                return interface, impl, self.logoutFactory(impl)
 
105
        raise NotImplementedError()
 
106
 
 
107
    def requestAvatarId(self, credentials):
 
108
        passwordSecure = IUsernameHashedPassword(credentials, None) is not None
 
109
        # ^ need to do something with this.  security warning perhaps?
 
110
        username, domain = credentials.username.split('@', 1)
 
111
 
 
112
        username = unicode(username)
 
113
        domain = unicode(domain)
 
114
 
 
115
        acct = self.accountByAddress(username, domain)
 
116
        if acct is not None:
 
117
            password = acct.password
 
118
            if credentials.checkPassword(password):
 
119
                return acct.storeID
 
120
            else:
 
121
                self.failedLogins += 1
 
122
                raise BadCredentials()
 
123
        raise NoSuchUser()
 
124