Package buildbot :: Module pbmanager
[frames] | no frames]

Source Code for Module buildbot.pbmanager

  1  # This file is part of Buildbot.  Buildbot is free software: you can 
  2  # redistribute it and/or modify it under the terms of the GNU General Public 
  3  # License as published by the Free Software Foundation, version 2. 
  4  # 
  5  # This program is distributed in the hope that it will be useful, but WITHOUT 
  6  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
  8  # details. 
  9  # 
 10  # You should have received a copy of the GNU General Public License along with 
 11  # this program; if not, write to the Free Software Foundation, Inc., 51 
 12  # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16  from zope.interface import implements 
 17  from twisted.spread import pb 
 18  from twisted.python import failure, log 
 19  from twisted.internet import defer 
 20  from twisted.cred import portal, checkers, credentials, error 
 21  from twisted.application import service, strports 
 22   
 23  debug = False 
 24   
25 -class PBManager(service.MultiService):
26 """ 27 A centralized manager for PB ports and authentication on them. 28 29 Allows various pieces of code to request a (port, username) combo, along 30 with a password and a perspective factory. 31 """
32 - def __init__(self):
33 service.MultiService.__init__(self) 34 self.setName('pbmanager') 35 self.dispatchers = {}
36
37 - def register(self, portstr, username, password, pfactory):
38 """ 39 Register a perspective factory PFACTORY to be executed when a PB 40 connection arrives on PORTSTR with USERNAME/PASSWORD. Returns a 41 Registration object which can be used to unregister later. 42 """ 43 # do some basic normalization of portstrs 44 if type(portstr) == type(0) or ':' not in portstr: 45 portstr = "tcp:%s" % portstr 46 47 reg = Registration(self, portstr, username) 48 49 if portstr not in self.dispatchers: 50 disp = self.dispatchers[portstr] = Dispatcher(portstr) 51 disp.setServiceParent(self) 52 else: 53 disp = self.dispatchers[portstr] 54 55 disp.register(username, password, pfactory) 56 57 return reg
58
59 - def _unregister(self, registration):
60 disp = self.dispatchers[registration.portstr] 61 disp.unregister(registration.username) 62 registration.username = None 63 if not disp.users: 64 disp = self.dispatchers[registration.portstr] 65 del self.dispatchers[registration.portstr] 66 return disp.disownServiceParent() 67 return defer.succeed(None)
68 69
70 -class Registration(object):
71 - def __init__(self, pbmanager, portstr, username):
72 self.portstr = portstr 73 "portstr this registration is active on" 74 self.username = username 75 "username of this registration" 76 77 self.pbmanager = pbmanager
78
79 - def unregister(self):
80 """ 81 Unregister this registration, removing the username from the port, and 82 closing the port if there are no more users left. Returns a Deferred. 83 """ 84 return self.pbmanager._unregister(self)
85
86 - def getPort(self):
87 """ 88 Helper method for testing; returns the TCP port used for this 89 registration, even if it was specified as 0 and thus allocated by the 90 OS. 91 """ 92 disp = self.pbmanager.dispatchers[self.portstr] 93 return disp.port.getHost().port
94 95
96 -class Dispatcher(service.Service):
97 implements(portal.IRealm, checkers.ICredentialsChecker) 98 99 credentialInterfaces = [ credentials.IUsernamePassword, 100 credentials.IUsernameHashedPassword ] 101
102 - def __init__(self, portstr):
103 self.portstr = portstr 104 self.users = {} 105 106 # there's lots of stuff to set up for a PB connection! 107 self.portal = portal.Portal(self) 108 self.portal.registerChecker(self) 109 self.serverFactory = pb.PBServerFactory(self.portal) 110 self.serverFactory.unsafeTracebacks = True 111 self.port = strports.listen(portstr, self.serverFactory)
112
113 - def stopService(self):
114 # stop listening on the port when shut down 115 d = defer.maybeDeferred(self.port.stopListening) 116 d.addCallback(lambda _ : service.Service.stopService(self)) 117 return d
118
119 - def register(self, username, password, pfactory):
120 if debug: 121 log.msg("registering username '%s' on pb port %s: %s" 122 % (username, self.portstr, pfactory)) 123 if username in self.users: 124 raise KeyError, ("username '%s' is already registered on PB port %s" 125 % (username, self.portstr)) 126 self.users[username] = (password, pfactory)
127
128 - def unregister(self, username):
129 if debug: 130 log.msg("unregistering username '%s' on pb port %s" 131 % (username, self.portstr)) 132 del self.users[username]
133 134 # IRealm 135
136 - def requestAvatar(self, username, mind, interface):
137 assert interface == pb.IPerspective 138 if username not in self.users: 139 d = defer.succeed(None) # no perspective 140 else: 141 _, afactory = self.users.get(username) 142 d = defer.maybeDeferred(afactory, mind, username) 143 144 # check that we got a perspective 145 def check(persp): 146 if not persp: 147 raise ValueError("no perspective for '%s'" % username) 148 return persp
149 d.addCallback(check) 150 151 # call the perspective's attached(mind) 152 def call_attached(persp): 153 d = defer.maybeDeferred(persp.attached, mind) 154 d.addCallback(lambda _ : persp) # keep returning the perspective 155 return d
156 d.addCallback(call_attached) 157 158 # return the tuple requestAvatar is expected to return 159 def done(persp): 160 return (pb.IPerspective, persp, lambda: persp.detached(mind)) 161 d.addCallback(done) 162 163 return d 164 165 # ICredentialsChecker 166
167 - def requestAvatarId(self, creds):
168 if creds.username in self.users: 169 password, _ = self.users[creds.username] 170 d = defer.maybeDeferred(creds.checkPassword, password) 171 def check(matched): 172 if not matched: 173 log.msg("invalid login from user '%s'" % creds.username) 174 return failure.Failure(error.UnauthorizedLogin()) 175 return creds.username
176 d.addCallback(check) 177 return d 178 else: 179 log.msg("invalid login from unknown user '%s'" % creds.username) 180 return defer.fail(error.UnauthorizedLogin()) 181