Package flumotion :: Package component :: Package bouncers :: Module multibouncerplug
[hide private]

Source Code for Module flumotion.component.bouncers.multibouncerplug

  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  from twisted.internet import defer 
 19  from twisted.python import util 
 20   
 21  from flumotion.common import keycards, watched 
 22  from flumotion.common import messages, errors, documentation 
 23  from flumotion.common.i18n import N_, gettexter 
 24  from flumotion.component.bouncers import base, plug, combinator 
 25   
 26  T_ = gettexter() 
 27   
 28   
29 -class MultiBouncerPlug(plug.BouncerPlug):
30 31 logCategory = 'multibouncerplug' 32
33 - def start(self, component):
34 self.watchable_keycards = watched.WatchedDict() # keycard id -> Keycard 35 self.contexts = {} # keycard id -> {algorithm name -> result} 36 self.algorithms = util.OrderedDict() # name -> algorithm 37 38 def add_entry(entry, algorithm): 39 name = entry['type'] 40 if name in self.algorithms: 41 suffix = 1 42 while ('%s-%d' % (name, suffix)) in self.algorithms: 43 suffix += 1 44 name = '%s-%d' % (name, suffix) 45 46 assert name not in self.algorithms 47 self.algorithms[name] = algorithm 48 return name
49 50 # get all algorithm plugs this component has, put them into 51 # self.algorithms with unique names 52 entries = component.config['plugs'].get(base.BOUNCER_ALGORITHM_SOCKET, 53 []) 54 algorithms = component.plugs.get(base.BOUNCER_ALGORITHM_SOCKET, []) 55 56 if not algorithms: 57 m = messages.Error(T_(N_( 58 "The multibouncerplug requires at least one bouncer " 59 "algorithm plug to be present")), mid='no-algorithm') 60 component.addMessage(m) 61 raise errors.ComponentSetupHandledError() 62 63 for entry, algorithm in zip(entries, algorithms): 64 # add the algorithm to the algorithms dictionary 65 name = add_entry(entry, algorithm) 66 # provide the algorithm with the keycard store 67 algorithm.set_keycard_store(self.watchable_keycards) 68 # provide the algorithm with an expiry function crafted especially 69 # for it (containing its unique name) 70 expire = lambda ids: self.algorithm_expire_keycard_ids(ids, name) 71 algorithm.set_expire_function(expire) 72 73 self.debug("configured with algorithms %r", self.algorithms.keys()) 74 75 # create the algorithm combinator 76 props = self.args['properties'] 77 self.combinator = combinator.AlgorithmCombinator(self.algorithms) 78 79 if 'combination' in props and combinator.pyparsing is None: 80 m = messages.Error(T_(N_( 81 "To use the 'combination' property you need to " 82 "have the 'pyparsing' module installed.\n")), 83 mid='missing-pyparsing') 84 documentation.messageAddPythonInstall(m, 'pyparsing') 85 component.addMessage(m) 86 raise errors.ComponentSetupHandledError() 87 88 # get the combination specification, defaulting to implicit AND 89 spec = props.get('combination', ' and '.join(self.algorithms.keys())) 90 self.debug("using combination %s", spec) 91 try: 92 self.combinator.create_combination(spec) 93 except combinator.ParseException, e: 94 m = messages.Error(T_(N_( 95 "Invalid algorithms combination: %s"), str(e)), 96 mid='wrong-combination') 97 98 component.addMessage(m) 99 raise errors.ComponentSetupHandledError() 100 101 return plug.BouncerPlug.start(self, component)
102
103 - def authenticate(self, keycard):
104 # create a context for this request 105 context = {} 106 # ask the combinator for an answer 107 d = self.combinator.evaluate(keycard, context) 108 109 def authenticated(res, keycard): 110 # the answer is True/False 111 if not res: 112 # False, return None as per the bouncer protocol 113 return None 114 if hasattr(keycard, 'ttl') and keycard.ttl <= 0: 115 # keycard was invalid on input 116 self.log('immediately expiring keycard %r', keycard) 117 return None 118 if self.addKeycard(keycard): 119 # keycard added, set state to AUTHENTICATED, keep the context, 120 # return to caller 121 keycard.state = keycards.AUTHENTICATED 122 self.contexts[keycard.id] = context 123 self.watchable_keycards[keycard.id] = keycard 124 return keycard
125 126 d.addCallback(authenticated, keycard) 127 128 return d 129
130 - def on_keycardRemoved(self, keycard):
131 # clear our references to the keycard 132 del self.contexts[keycard.id] 133 del self.watchable_keycards[keycard.id]
134
135 - def algorithm_expire_keycard_ids(self, keycard_ids, name):
136 # this gets called by a particular algorithm when it wants to expire a 137 # keycard 138 to_expire = [] 139 140 self.debug("algorithm %r requested expiration of keycards %r", 141 name, keycard_ids) 142 143 for keycard_id in keycard_ids: 144 # change the result in the context 145 context = self.contexts[keycard_id] 146 context[name] = False 147 # Reevaluate in the combinator. Because we already got an answer 148 # for that context, it should contain all necesary info, so we 149 # never should call any algorithm method: just do synchronous 150 # evaluation. 151 if not self.combinator.synchronous_evaluate(context): 152 self.log("keycard with id %r will be expired", keycard_id) 153 to_expire.append(keycard_id) 154 155 return self.expireKeycardIds(to_expire)
156