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