1
# -*- test-case-name: twisted.words.test.test_service -*-
2
# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
A module that needs a better name.
8
Implements new cred things for words.
10
How does this thing work?
12
- Network connection on some port expecting to speak some protocol
14
- Protocol-specific authentication, resulting in some kind of credentials object
16
- twisted.cred.portal login using those credentials for the interface
17
IUser and with something implementing IChatClient as the mind
19
- successful login results in an IUser avatar the protocol can call
20
methods on, and state added to the realm such that the mind will have
21
methods called on it as is necessary
23
- protocol specific actions lead to calls onto the avatar; remote events
24
lead to calls onto the mind
26
- protocol specific hangup, realm is notified, user is removed from active
30
from time import time, ctime
32
from zope.interface import implements
34
from twisted.words import iwords, ewords
36
from twisted.python.components import registerAdapter
37
from twisted.cred import portal, credentials, error as ecred
38
from twisted.spread import pb
39
from twisted.words.protocols import irc
40
from twisted.internet import defer, protocol
41
from twisted.python import log, failure, reflect
42
from twisted import copyright
46
implements(iwords.IGroup)
48
def __init__(self, name):
57
def _ebUserCall(self, err, p):
58
return failure.Failure(Exception(p, err))
61
def _cbUserCall(self, results):
62
for (success, result) in results:
64
user, err = result.value # XXX
65
self.remove(user, err.getErrorMessage())
69
assert iwords.IChatClient.providedBy(user), "%r is not a chat client" % (user,)
70
if user.name not in self.users:
72
self.users[user.name] = user
73
for p in self.users.itervalues():
75
d = defer.maybeDeferred(p.userJoined, self, user)
76
d.addErrback(self._ebUserCall, p=p)
78
defer.DeferredList(additions).addCallback(self._cbUserCall)
79
return defer.succeed(None)
82
def remove(self, user, reason=None):
83
assert reason is None or isinstance(reason, unicode)
85
del self.users[user.name]
90
for p in self.users.itervalues():
92
d = defer.maybeDeferred(p.userLeft, self, user, reason)
93
d.addErrback(self._ebUserCall, p=p)
95
defer.DeferredList(removals).addCallback(self._cbUserCall)
96
return defer.succeed(None)
100
return defer.succeed(len(self.users))
103
def receive(self, sender, recipient, message):
104
assert recipient is self
106
for p in self.users.itervalues():
108
d = defer.maybeDeferred(p.receive, sender, self, message)
109
d.addErrback(self._ebUserCall, p=p)
111
defer.DeferredList(receives).addCallback(self._cbUserCall)
112
return defer.succeed(None)
115
def setMetadata(self, meta):
118
for p in self.users.itervalues():
119
d = defer.maybeDeferred(p.groupMetaUpdate, self, meta)
120
d.addErrback(self._ebUserCall, p=p)
122
defer.DeferredList(sets).addCallback(self._cbUserCall)
123
return defer.succeed(None)
128
return iter(self.users.values())
132
implements(iwords.IUser)
137
def __init__(self, name):
140
self.lastMessage = time()
143
def loggedIn(self, realm, mind):
149
def join(self, group):
151
self.groups.append(group)
153
return group.add(self.mind).addCallback(cbJoin)
156
def leave(self, group, reason=None):
158
self.groups.remove(group)
160
return group.remove(self.mind, reason).addCallback(cbLeave)
163
def send(self, recipient, message):
164
self.lastMessage = time()
165
return recipient.receive(self.mind, recipient, message)
168
def itergroups(self):
169
return iter(self.groups)
173
for g in self.groups[:]:
177
NICKSERV = 'NickServ!NickServ@services'
180
class IRCUser(irc.IRC):
182
Protocol instance representing an IRC user connected to the server.
184
implements(iwords.IChatClient)
186
# A list of IGroups in which I am participating
189
# A no-argument callable I should invoke when I go away
192
# An IUser we use to interact with the chat service
198
# How to handle unicode (TODO: Make this customizable on a per-user basis)
202
def connectionMade(self):
203
self.irc_PRIVMSG = self.irc_NICKSERV_PRIVMSG
204
self.realm = self.factory.realm
205
self.hostname = self.realm.name
208
def connectionLost(self, reason):
209
if self.logout is not None:
214
# Make sendMessage a bit more useful to us
215
def sendMessage(self, command, *parameter_list, **kw):
216
if not kw.has_key('prefix'):
217
kw['prefix'] = self.hostname
218
if not kw.has_key('to'):
219
kw['to'] = self.name.encode(self.encoding)
221
arglist = [self, command, kw['to']] + list(parameter_list)
222
irc.IRC.sendMessage(*arglist, **kw)
225
# IChatClient implementation
226
def userJoined(self, group, user):
228
"%s!%s@%s" % (user.name, user.name, self.hostname),
232
def userLeft(self, group, user, reason=None):
233
assert reason is None or isinstance(reason, unicode)
235
"%s!%s@%s" % (user.name, user.name, self.hostname),
237
(reason or u"leaving").encode(self.encoding, 'replace'))
240
def receive(self, sender, recipient, message):
241
#>> :glyph!glyph@adsl-64-123-27-108.dsl.austtx.swbell.net PRIVMSG glyph_ :hello
244
if iwords.IGroup.providedBy(recipient):
245
recipientName = '#' + recipient.name
247
recipientName = recipient.name
249
text = message.get('text', '<an unrepresentable message>')
250
for L in text.splitlines():
252
'%s!%s@%s' % (sender.name, sender.name, self.hostname),
257
def groupMetaUpdate(self, group, meta):
259
topic = meta['topic']
260
author = meta.get('topic_author', '')
265
'%s!%s@%s' % (author, author, self.hostname)
268
# irc.IRC callbacks - starting with login related stuff.
272
def irc_PASS(self, prefix, params):
273
"""Password message -- Register a password.
275
Parameters: <password>
279
Note that IRC requires the client send this *before* NICK
282
self.password = params[-1]
285
def irc_NICK(self, prefix, params):
286
"""Nick message -- Set your nickname.
288
Parameters: <nickname>
293
nickname = params[0].decode(self.encoding)
294
except UnicodeDecodeError:
298
'Your nickname is cannot be decoded. Please use ASCII or UTF-8.')
299
self.transport.loseConnection()
302
self.nickname = nickname
305
for code, text in self._motdMessages:
306
self.sendMessage(code, text % self.factory._serverInfo)
308
if self.password is None:
314
password = self.password
316
self.logInAs(nickname, password)
319
def irc_USER(self, prefix, params):
320
"""User message -- Set your realname.
322
Parameters: <user> <mode> <unused> <realname>
324
# Note: who gives a crap about this? The IUser has the real
325
# information we care about. Save it anyway, I guess, just
327
self.realname = params[-1]
330
def irc_NICKSERV_PRIVMSG(self, prefix, params):
331
"""Send a (private) message.
333
Parameters: <msgtarget> <text to be sent>
336
password = params[-1]
338
if self.nickname is None:
339
# XXX Send an error response here
340
self.transport.loseConnection()
341
elif target.lower() != "nickserv":
345
"Denied. Please send me (NickServ) your password.")
347
nickname = self.nickname
349
self.logInAs(nickname, password)
352
def logInAs(self, nickname, password):
353
d = self.factory.portal.login(
354
credentials.UsernamePassword(nickname, password),
357
d.addCallbacks(self._cbLogin, self._ebLogin, errbackArgs=(nickname,))
362
":connected to Twisted IRC"),
364
":Your host is %(serviceName)s, running version %(serviceVersion)s"),
366
":This server was created on %(creationDate)s"),
368
# "Bummer. This server returned a worthless 004 numeric.
369
# I'll have to guess at all the values"
372
# w and n are the currently supported channel and user modes
373
# -- specify this better
374
"%(serviceName)s %(serviceVersion)s w n")
379
":- %(serviceName)s Message of the Day - "),
381
":End of /MOTD command.")
384
def _cbLogin(self, (iface, avatar, logout)):
385
assert iface is iwords.IUser, "Realm is buggy, got %r" % (iface,)
387
# Let them send messages to the world
392
for code, text in self._welcomeMessages:
393
self.sendMessage(code, text % self.factory._serverInfo)
396
def _ebLogin(self, err, nickname):
397
if err.check(ewords.AlreadyLoggedIn):
401
"Already logged in. No pod people allowed!")
402
elif err.check(ecred.UnauthorizedLogin):
406
"Login failed. Goodbye.")
408
log.msg("Unhandled error during login:")
413
"Server error during login. Sorry.")
414
self.transport.loseConnection()
417
# Great, now that's out of the way, here's some of the interesting
419
def irc_PING(self, prefix, params):
422
Parameters: <server1> [ <server2> ]
424
if self.realm is not None:
425
self.sendMessage('PONG', self.hostname)
428
def irc_QUIT(self, prefix, params):
431
Parameters: [ <Quit Message> ]
433
self.transport.loseConnection()
436
def _channelMode(self, group, modes=None, *args):
440
":Unknown MODE flag.")
442
self.channelMode(self.name, '#' + group.name, '+')
445
def _userMode(self, user, modes=None):
449
":Unknown MODE flag.")
450
elif user is self.avatar:
456
irc.ERR_USERSDONTMATCH,
457
":You can't look at someone else's modes.")
460
def irc_MODE(self, prefix, params):
463
Parameters: <nickname>
464
*( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
468
channelOrUser = params[0].decode(self.encoding)
469
except UnicodeDecodeError:
471
irc.ERR_NOSUCHNICK, params[0],
472
":No such nickname (could not decode your unicode!)")
475
if channelOrUser.startswith('#'):
477
err.trap(ewords.NoSuchGroup)
479
irc.ERR_NOSUCHCHANNEL, params[0],
480
":That channel doesn't exist.")
481
d = self.realm.lookupGroup(channelOrUser[1:])
485
callbackArgs=tuple(params[1:]))
490
":No such nickname.")
492
d = self.realm.lookupUser(channelOrUser)
496
callbackArgs=tuple(params[1:]))
499
def irc_USERHOST(self, prefix, params):
502
Parameters: <nickname> *( SPACE <nickname> )
509
def irc_PRIVMSG(self, prefix, params):
510
"""Send a (private) message.
512
Parameters: <msgtarget> <text to be sent>
515
targetName = params[0].decode(self.encoding)
516
except UnicodeDecodeError:
518
irc.ERR_NOSUCHNICK, targetName,
519
":No such nick/channel (could not decode your unicode!)")
522
messageText = params[-1]
523
if targetName.startswith('#'):
524
target = self.realm.lookupGroup(targetName[1:])
526
target = self.realm.lookupUser(targetName).addCallback(lambda user: user.mind)
530
return self.avatar.send(targ, {"text": messageText})
534
irc.ERR_NOSUCHNICK, targetName,
535
":No such nick/channel.")
537
target.addCallbacks(cbTarget, ebTarget)
540
def irc_JOIN(self, prefix, params):
543
Parameters: ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] )
546
groupName = params[0].decode(self.encoding)
547
except UnicodeDecodeError:
549
irc.IRC_NOSUCHCHANNEL, params[0],
550
":No such channel (could not decode your unicode!)")
553
if groupName.startswith('#'):
554
groupName = groupName[1:]
558
self.userJoined(group, self)
562
[user.name for user in group.iterusers()])
563
self._sendTopic(group)
564
return self.avatar.join(group).addCallback(cbJoin)
568
irc.ERR_NOSUCHCHANNEL, '#' + groupName,
571
self.realm.getGroup(groupName).addCallbacks(cbGroup, ebGroup)
574
def irc_PART(self, prefix, params):
577
Parameters: <channel> *( "," <channel> ) [ <Part Message> ]
580
groupName = params[0].decode(self.encoding)
581
except UnicodeDecodeError:
583
irc.ERR_NOTONCHANNEL, params[0],
584
":Could not decode your unicode!")
587
if groupName.startswith('#'):
588
groupName = groupName[1:]
591
reason = params[1].decode('utf-8')
597
self.userLeft(group, self, reason)
598
return self.avatar.leave(group, reason).addCallback(cbLeave)
601
err.trap(ewords.NoSuchGroup)
603
irc.ERR_NOTONCHANNEL,
605
":" + err.getErrorMessage())
607
self.realm.lookupGroup(groupName).addCallbacks(cbGroup, ebGroup)
610
def irc_NAMES(self, prefix, params):
613
Parameters: [ <channel> *( "," <channel> ) [ <target> ] ]
616
#>> :benford.openprojects.net 353 glyph = #python :Orban ... @glyph ... Zymurgy skreech
617
#>> :benford.openprojects.net 366 glyph #python :End of /NAMES list.
619
channel = params[-1].decode(self.encoding)
620
except UnicodeDecodeError:
622
irc.ERR_NOSUCHCHANNEL, params[-1],
623
":No such channel (could not decode your unicode!)")
626
if channel.startswith('#'):
627
channel = channel[1:]
633
[user.name for user in group.iterusers()])
636
err.trap(ewords.NoSuchGroup)
637
# No group? Fine, no names!
643
self.realm.lookupGroup(channel).addCallbacks(cbGroup, ebGroup)
646
def irc_TOPIC(self, prefix, params):
649
Parameters: <channel> [ <topic> ]
652
channel = params[0].decode(self.encoding)
653
except UnicodeDecodeError:
655
irc.ERR_NOSUCHCHANNEL,
656
":That channel doesn't exist (could not decode your unicode!)")
659
if channel.startswith('#'):
660
channel = channel[1:]
663
self._setTopic(channel, params[1])
665
self._getTopic(channel)
668
def _sendTopic(self, group):
670
Send the topic of the given group to this user, if it has one.
672
topic = group.meta.get("topic")
674
author = group.meta.get("topic_author") or "<noone>"
675
date = group.meta.get("topic_date", 0)
676
self.topic(self.name, '#' + group.name, topic)
677
self.topicAuthor(self.name, '#' + group.name, author, date)
680
def _getTopic(self, channel):
682
#>> :benford.openprojects.net 332 glyph #python :<churchr> I really did. I sprained all my toes.
683
#>> :benford.openprojects.net 333 glyph #python itamar|nyc 994713482
685
err.trap(ewords.NoSuchGroup)
687
irc.ERR_NOSUCHCHANNEL, '=', channel,
688
":That channel doesn't exist.")
690
self.realm.lookupGroup(channel).addCallbacks(self._sendTopic, ebGroup)
693
def _setTopic(self, channel, topic):
694
#<< TOPIC #divunal :foo
695
#>> :glyph!glyph@adsl-64-123-27-108.dsl.austtx.swbell.net TOPIC #divunal :foo
698
newMeta = group.meta.copy()
699
newMeta['topic'] = topic
700
newMeta['topic_author'] = self.name
701
newMeta['topic_date'] = int(time())
705
irc.ERR_CHANOPRIVSNEEDED,
707
":You need to be a channel operator to do that.")
709
return group.setMetadata(newMeta).addErrback(ebSet)
712
err.trap(ewords.NoSuchGroup)
714
irc.ERR_NOSUCHCHANNEL, '=', channel,
715
":That channel doesn't exist.")
717
self.realm.lookupGroup(channel).addCallbacks(cbGroup, ebGroup)
720
def list(self, channels):
721
"""Send a group of LIST response lines
723
@type channel: C{list} of C{(str, int, str)}
724
@param channel: Information about the channels being sent:
725
their name, the number of participants, and their topic.
727
for (name, size, topic) in channels:
728
self.sendMessage(irc.RPL_LIST, name, str(size), ":" + topic)
729
self.sendMessage(irc.RPL_LISTEND, ":End of /LIST")
732
def irc_LIST(self, prefix, params):
735
Return information about the indicated channels, or about all
736
channels if none are specified.
738
Parameters: [ <channel> *( "," <channel> ) [ <target> ] ]
741
#>> :orwell.freenode.net 321 exarkun Channel :Users Name
742
#>> :orwell.freenode.net 322 exarkun #python 358 :The Python programming language
743
#>> :orwell.freenode.net 323 exarkun :End of /LIST
745
# Return information about indicated channels
747
channels = params[0].decode(self.encoding).split(',')
748
except UnicodeDecodeError:
750
irc.ERR_NOSUCHCHANNEL, params[0],
751
":No such channel (could not decode your unicode!)")
756
if ch.startswith('#'):
758
groups.append(self.realm.lookupGroup(ch))
760
groups = defer.DeferredList(groups, consumeErrors=True)
761
groups.addCallback(lambda gs: [r for (s, r) in gs if s])
763
# Return information about all channels
764
groups = self.realm.itergroups()
766
def cbGroups(groups):
767
def gotSize(size, group):
768
return group.name, size, group.meta.get('topic')
769
d = defer.DeferredList([
770
group.size().addCallback(gotSize, group) for group in groups])
771
d.addCallback(lambda results: self.list([r for (s, r) in results if s]))
773
groups.addCallback(cbGroups)
776
def _channelWho(self, group):
777
self.who(self.name, '#' + group.name,
778
[(m.name, self.hostname, self.realm.name, m.name, "H", 0, m.name) for m in group.iterusers()])
781
def _userWho(self, user):
782
self.sendMessage(irc.RPL_ENDOFWHO,
783
":User /WHO not implemented")
786
def irc_WHO(self, prefix, params):
789
Parameters: [ <mask> [ "o" ] ]
792
#>> :x.opn 352 glyph #python aquarius pc-62-31-193-114-du.blueyonder.co.uk y.opn Aquarius H :3 Aquarius
794
#>> :x.opn 352 glyph #python foobar europa.tranquility.net z.opn skreech H :0 skreech
795
#>> :x.opn 315 glyph #python :End of /WHO list.
798
#>> :x.opn 352 glyph #python glyph adsl-64-123-27-108.dsl.austtx.swbell.net x.opn glyph H :0 glyph
799
#>> :x.opn 315 glyph glyph :End of /WHO list.
801
self.sendMessage(irc.RPL_ENDOFWHO, ":/WHO not supported.")
805
channelOrUser = params[0].decode(self.encoding)
806
except UnicodeDecodeError:
808
irc.RPL_ENDOFWHO, params[0],
809
":End of /WHO list (could not decode your unicode!)")
812
if channelOrUser.startswith('#'):
814
err.trap(ewords.NoSuchGroup)
816
irc.RPL_ENDOFWHO, channelOrUser,
817
":End of /WHO list.")
818
d = self.realm.lookupGroup(channelOrUser[1:])
819
d.addCallbacks(self._channelWho, ebGroup)
822
err.trap(ewords.NoSuchUser)
824
irc.RPL_ENDOFWHO, channelOrUser,
825
":End of /WHO list.")
826
d = self.realm.lookupUser(channelOrUser)
827
d.addCallbacks(self._userWho, ebUser)
831
def irc_WHOIS(self, prefix, params):
834
Parameters: [ <target> ] <mask> *( "," <mask> )
839
user.name, user.name, self.realm.name,
840
user.name, self.realm.name, 'Hi mom!', False,
841
int(time() - user.lastMessage), user.signOn,
842
['#' + group.name for group in user.itergroups()])
845
err.trap(ewords.NoSuchUser)
849
":No such nick/channel")
852
user = params[0].decode(self.encoding)
853
except UnicodeDecodeError:
857
":No such nick/channel")
860
self.realm.lookupUser(user).addCallbacks(cbUser, ebUser)
863
# Unsupported commands, here for legacy compatibility
864
def irc_OPER(self, prefix, params):
867
Parameters: <name> <password>
869
self.sendMessage(irc.ERR_NOOPERHOST, ":O-lines not applicable")
872
class IRCFactory(protocol.ServerFactory):
874
IRC server that creates instances of the L{IRCUser} protocol.
876
@ivar _serverInfo: A dictionary mapping:
877
"serviceName" to the name of the server,
878
"serviceVersion" to the copyright version,
879
"creationDate" to the time that the server was started.
883
def __init__(self, realm, portal):
887
"serviceName": self.realm.name,
888
"serviceVersion": copyright.version,
889
"creationDate": ctime()
894
class PBMind(pb.Referenceable):
898
def jellyFor(self, jellier):
899
return reflect.qual(PBMind), jellier.invoker.registerReference(self)
901
def remote_userJoined(self, user, group):
904
def remote_userLeft(self, user, group, reason):
907
def remote_receive(self, sender, recipient, message):
910
def remote_groupMetaUpdate(self, group, meta):
914
class PBMindReference(pb.RemoteReference):
915
implements(iwords.IChatClient)
917
def receive(self, sender, recipient, message):
918
if iwords.IGroup.providedBy(recipient):
919
rec = PBGroup(self.realm, self.avatar, recipient)
921
rec = PBUser(self.realm, self.avatar, recipient)
922
return self.callRemote(
924
PBUser(self.realm, self.avatar, sender),
928
def groupMetaUpdate(self, group, meta):
929
return self.callRemote(
931
PBGroup(self.realm, self.avatar, group),
934
def userJoined(self, group, user):
935
return self.callRemote(
937
PBGroup(self.realm, self.avatar, group),
938
PBUser(self.realm, self.avatar, user))
940
def userLeft(self, group, user, reason=None):
941
assert reason is None or isinstance(reason, unicode)
942
return self.callRemote(
944
PBGroup(self.realm, self.avatar, group),
945
PBUser(self.realm, self.avatar, user),
947
pb.setUnjellyableForClass(PBMind, PBMindReference)
950
class PBGroup(pb.Referenceable):
951
def __init__(self, realm, avatar, group):
957
def processUniqueID(self):
958
return hash((self.realm.name, self.avatar.name, self.group.name))
961
def jellyFor(self, jellier):
962
return reflect.qual(self.__class__), self.group.name.encode('utf-8'), jellier.invoker.registerReference(self)
965
def remote_leave(self, reason=None):
966
return self.avatar.leave(self.group, reason)
969
def remote_send(self, message):
970
return self.avatar.send(self.group, message)
973
class PBGroupReference(pb.RemoteReference):
974
implements(iwords.IGroup)
976
def unjellyFor(self, unjellier, unjellyList):
977
clsName, name, ref = unjellyList
978
self.name = name.decode('utf-8')
979
return pb.RemoteReference.unjellyFor(self, unjellier, [clsName, ref])
981
def leave(self, reason=None):
982
return self.callRemote("leave", reason)
984
def send(self, message):
985
return self.callRemote("send", message)
986
pb.setUnjellyableForClass(PBGroup, PBGroupReference)
988
class PBUser(pb.Referenceable):
989
def __init__(self, realm, avatar, user):
994
def processUniqueID(self):
995
return hash((self.realm.name, self.avatar.name, self.user.name))
998
class ChatAvatar(pb.Referenceable):
999
implements(iwords.IChatClient)
1001
def __init__(self, avatar):
1002
self.avatar = avatar
1005
def jellyFor(self, jellier):
1006
return reflect.qual(self.__class__), jellier.invoker.registerReference(self)
1009
def remote_join(self, groupName):
1010
assert isinstance(groupName, unicode)
1012
def cbJoin(ignored):
1013
return PBGroup(self.avatar.realm, self.avatar, group)
1014
d = self.avatar.join(group)
1015
d.addCallback(cbJoin)
1017
d = self.avatar.realm.getGroup(groupName)
1018
d.addCallback(cbGroup)
1020
registerAdapter(ChatAvatar, iwords.IUser, pb.IPerspective)
1022
class AvatarReference(pb.RemoteReference):
1023
def join(self, groupName):
1024
return self.callRemote('join', groupName)
1027
d = defer.Deferred()
1028
self.broker.notifyOnDisconnect(lambda: d.callback(None))
1029
self.broker.transport.loseConnection()
1032
pb.setUnjellyableForClass(ChatAvatar, AvatarReference)
1035
class WordsRealm(object):
1036
implements(portal.IRealm, iwords.IChatService)
1040
def __init__(self, name):
1044
def userFactory(self, name):
1048
def groupFactory(self, name):
1052
def logoutFactory(self, avatar, facet):
1054
# XXX Deferred support here
1055
getattr(facet, 'logout', lambda: None)()
1056
avatar.realm = avatar.mind = None
1060
def requestAvatar(self, avatarId, mind, *interfaces):
1061
if isinstance(avatarId, str):
1062
avatarId = avatarId.decode(self._encoding)
1064
def gotAvatar(avatar):
1065
if avatar.realm is not None:
1066
raise ewords.AlreadyLoggedIn()
1067
for iface in interfaces:
1068
facet = iface(avatar, None)
1069
if facet is not None:
1070
avatar.loggedIn(self, mind)
1071
mind.name = avatarId
1073
mind.avatar = avatar
1074
return iface, facet, self.logoutFactory(avatar, facet)
1075
raise NotImplementedError(self, interfaces)
1077
return self.getUser(avatarId).addCallback(gotAvatar)
1080
# IChatService, mostly.
1081
createGroupOnRequest = False
1082
createUserOnRequest = True
1084
def lookupUser(self, name):
1085
raise NotImplementedError
1088
def lookupGroup(self, group):
1089
raise NotImplementedError
1092
def addUser(self, user):
1093
"""Add the given user to this service.
1095
This is an internal method intented to be overridden by
1096
L{WordsRealm} subclasses, not called by external code.
1098
@type user: L{IUser}
1100
@rtype: L{twisted.internet.defer.Deferred}
1101
@return: A Deferred which fires with C{None} when the user is
1102
added, or which fails with
1103
L{twisted.words.ewords.DuplicateUser} if a user with the
1104
same name exists already.
1106
raise NotImplementedError
1109
def addGroup(self, group):
1110
"""Add the given group to this service.
1112
@type group: L{IGroup}
1114
@rtype: L{twisted.internet.defer.Deferred}
1115
@return: A Deferred which fires with C{None} when the group is
1116
added, or which fails with
1117
L{twisted.words.ewords.DuplicateGroup} if a group with the
1118
same name exists already.
1120
raise NotImplementedError
1123
def getGroup(self, name):
1124
assert isinstance(name, unicode)
1125
if self.createGroupOnRequest:
1127
err.trap(ewords.DuplicateGroup)
1128
return self.lookupGroup(name)
1129
return self.createGroup(name).addErrback(ebGroup)
1130
return self.lookupGroup(name)
1133
def getUser(self, name):
1134
assert isinstance(name, unicode)
1135
if self.createUserOnRequest:
1137
err.trap(ewords.DuplicateUser)
1138
return self.lookupUser(name)
1139
return self.createUser(name).addErrback(ebUser)
1140
return self.lookupUser(name)
1143
def createUser(self, name):
1144
assert isinstance(name, unicode)
1146
return failure.Failure(ewords.DuplicateUser(name))
1148
err.trap(ewords.NoSuchUser)
1149
return self.userFactory(name)
1152
d = self.lookupUser(name)
1153
d.addCallbacks(cbLookup, ebLookup)
1154
d.addCallback(self.addUser)
1158
def createGroup(self, name):
1159
assert isinstance(name, unicode)
1160
def cbLookup(group):
1161
return failure.Failure(ewords.DuplicateGroup(name))
1163
err.trap(ewords.NoSuchGroup)
1164
return self.groupFactory(name)
1167
d = self.lookupGroup(name)
1168
d.addCallbacks(cbLookup, ebLookup)
1169
d.addCallback(self.addGroup)
1173
class InMemoryWordsRealm(WordsRealm):
1174
def __init__(self, *a, **kw):
1175
super(InMemoryWordsRealm, self).__init__(*a, **kw)
1180
def itergroups(self):
1181
return defer.succeed(self.groups.itervalues())
1184
def addUser(self, user):
1185
if user.name in self.users:
1186
return defer.fail(failure.Failure(ewords.DuplicateUser()))
1187
self.users[user.name] = user
1188
return defer.succeed(user)
1191
def addGroup(self, group):
1192
if group.name in self.groups:
1193
return defer.fail(failure.Failure(ewords.DuplicateGroup()))
1194
self.groups[group.name] = group
1195
return defer.succeed(group)
1198
def lookupUser(self, name):
1199
assert isinstance(name, unicode)
1202
user = self.users[name]
1204
return defer.fail(failure.Failure(ewords.NoSuchUser(name)))
1206
return defer.succeed(user)
1209
def lookupGroup(self, name):
1210
assert isinstance(name, unicode)
1213
group = self.groups[name]
1215
return defer.fail(failure.Failure(ewords.NoSuchGroup(name)))
1217
return defer.succeed(group)
1222
'WordsRealm', 'InMemoryWordsRealm',