Trees | Indices | Help |
---|
|
1 # -*- Mode: Python -*- 2 # vi:si:et:sw=4:sts=4:ts=4 3 4 # Flumotion - a streaming media server 5 # Copyright (C) 2004,2005,2006,2007,2008,2009 Fluendo, S.L. 6 # Copyright (C) 2010,2011 Flumotion Services, S.A. 7 # All rights reserved. 8 # 9 # This file may be distributed and/or modified under the terms of 10 # the GNU Lesser General Public License version 2.1 as published by 11 # the Free Software Foundation. 12 # This file is distributed without any warranty; without even the implied 13 # warranty of merchantability or fitness for a particular purpose. 14 # See "LICENSE.LGPL" in the source distribution for more information. 15 # 16 # Headers in this file shall remain intact. 17 18 """ 19 A bouncer that combines other bouncers. 20 """ 21 22 from twisted.internet import defer 23 from twisted.python import util 24 25 from flumotion.common import keycards, watched 26 from flumotion.common import messages, errors, documentation 27 from flumotion.common.i18n import N_, gettexter 28 from flumotion.component.bouncers import base, component, combinator 29 30 T_ = gettexter() 31 3234 35 logCategory = 'multibouncer' 36 # FIXME random classes for now, they're going away anyway 37 keycardClasses = (keycards.KeycardHTTPGetArguments, 38 keycards.KeycardGeneric, keycards.KeycardUACPCC, 39 keycards.KeycardUACPP, keycards.KeycardToken) 4011142 self.watchable_keycards = watched.WatchedDict() # keycard id -> Keycard 43 self.contexts = {} # keycard id -> {algorithm name -> bool result} 44 self.algorithms = util.OrderedDict() # name -> algorithm instance 45 self.combinator = None4648 49 def add_entry(entry, algorithm): 50 name = entry['type'] 51 if name in self.algorithms: 52 suffix = 1 53 while ('%s-%d' % (name, suffix)) in self.algorithms: 54 suffix += 1 55 name = '%s-%d' % (name, suffix) 56 57 assert name not in self.algorithms 58 self.algorithms[name] = algorithm 59 return name60 61 # get all algorithm plugs this component has, put them into 62 # self.algorithms with unique names 63 entries = self.config['plugs'].get(base.BOUNCER_ALGORITHM_SOCKET, []) 64 algorithms = self.plugs.get(base.BOUNCER_ALGORITHM_SOCKET, []) 65 66 # check if there's at least one algorithm plug in a separate method, so 67 # subclasses can override it 68 self.check_algorithms(algorithms) 69 70 for entry, algorithm in zip(entries, algorithms): 71 # add the algorithm to the algorithms dictionary 72 name = add_entry(entry, algorithm) 73 # provide the algorithm with the keycard store 74 algorithm.set_keycard_store(self.watchable_keycards) 75 # provide the algorithm with an expiry function crafted especially 76 # for it (containing its unique name) 77 expire = lambda ids: self.algorithm_expire_keycard_ids(ids, name) 78 algorithm.set_expire_function(expire) 79 80 # we don't have any algorithms, stop here (see StaticMultiBouncer to 81 # see why we need this) 82 if not self.algorithms: 83 return 84 85 self.debug("configured with algorithms %r", self.algorithms.keys()) 86 87 # create the algorithm combinator 88 props = self.config['properties'] 89 self.combinator = combinator.AlgorithmCombinator(self.algorithms) 90 91 if 'combination' in props and combinator.pyparsing is None: 92 m = messages.Error(T_(N_( 93 "To use the 'combination' property you need to " 94 "have the 'pyparsing' module installed.\n")), 95 mid='missing-pyparsing') 96 documentation.messageAddPythonInstall(m, 'pyparsing') 97 self.addMessage(m) 98 raise errors.ComponentSetupHandledError() 99 100 # get the combination specification, defaulting to implicit AND 101 spec = props.get('combination', ' and '.join(self.algorithms.keys())) 102 self.debug("using combination %s", spec) 103 try: 104 self.combinator.create_combination(spec) 105 except combinator.ParseException, e: 106 m = messages.Error(T_(N_( 107 "Invalid algorithms combination: %s"), str(e)), 108 mid='wrong-combination') 109 self.addMessage(m) 110 raise errors.ComponentSetupHandledError()113 if not algorithms: 114 m = messages.Error(T_(N_( 115 "The multibouncer requires at least one bouncer " 116 "algorithm plug to be present")), mid='no-algorithm') 117 self.addMessage(m) 118 raise errors.ComponentSetupHandledError()119121 # create a context for this request 122 context = {} 123 # ask the combinator for an answer 124 d = self.combinator.evaluate(keycard, context) 125 126 def authenticated(res, keycard): 127 # the answer is True/False 128 if not res: 129 # False, return None as per the bouncer protocol 130 return None 131 if hasattr(keycard, 'ttl') and keycard.ttl <= 0: 132 # keycard was invalid on input 133 self.log('immediately expiring keycard %r', keycard) 134 return None 135 if self.addKeycard(keycard): 136 # keycard added, set state to AUTHENTICATED, keep the context, 137 # return to caller 138 keycard.state = keycards.AUTHENTICATED 139 self.contexts[keycard.id] = context 140 self.watchable_keycards[keycard.id] = keycard 141 return keycard142 143 return d.addCallback(authenticated, keycard) 144146 # clear our references to the keycard 147 del self.contexts[keycard.id] 148 del self.watchable_keycards[keycard.id]149151 # this gets called by a particular algorithm when it wants to expire a 152 # keycard 153 to_expire = [] 154 155 self.debug("algorithm %r requested expiration of keycards %r", 156 name, keycard_ids) 157 158 for keycard_id in keycard_ids: 159 # change the result in the context 160 context = self.contexts[keycard_id] 161 context[name] = False 162 # Reevaluate in the combinator. Because we already got an answer 163 # for that context, it should contain all necesary info, so we 164 # never should call any algorithm method: just do synchronous 165 # evaluation. 166 if not self.combinator.synchronous_evaluate(context): 167 self.log("keycard with id %r will be expired", keycard_id) 168 to_expire.append(keycard_id) 169 170 return self.expireKeycardIds(to_expire)171 172174 """A multibouncer that has a static list of bouncer algorithm plugs""" 175 176 algorithmClasses = None 177 181201 202 try: 203 klasses = iter(self.algorithmClasses) 204 except TypeError: 205 klasses = iter((self.algorithmClasses, )) 206 207 d = defer.Deferred() 208 for klass in klasses: 209 name = klass.__name__ 210 algorithm = klass({'properties': self.config['properties']}) 211 start_algorithm(d, algorithm, name) 212 213 def create_combinator(_): 214 self.combinator = combinator.AlgorithmCombinator(self.algorithms) 215 spec = ' and '.join(self.algorithms.keys()) 216 self.combinator.create_combination(spec) 217 218 d.addCallback(create_combinator) 219 220 d.callback(None) 221 return d 222 225 233183 MultiBouncer.setMedium(self, medium) 184 for algorithm in self.algorithms.itervalues(): 185 self._export_plug_interface(algorithm, medium)186188 if self.algorithmClasses is None: 189 raise NotImplementedError("Subclass did not choose algorithm") 190 191 def start_algorithm(d, algorithm, name): 192 self.algorithms[name] = algorithm 193 d.addCallback(lambda _: defer.maybeDeferred(algorithm.start, self)) 194 d.addCallback(algorithm_started, algorithm, name)195 196 def algorithm_started(_, algorithm, name): 197 algorithm.set_keycard_store(self.watchable_keycards) 198 199 expire = lambda ids: self.algorithm_expire_keycard_ids(ids, name) 200 algorithm.set_expire_function(expire)
Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 on Tue Aug 13 06:17:16 2013 | http://epydoc.sourceforge.net |