Package openid :: Module association
[frames] | no frames]

Source Code for Module openid.association

  1  """ 
  2  This module contains code for dealing with associations between 
  3  consumers and servers. 
  4  """ 
  5   
  6  import time 
  7   
  8  from openid import cryptutil 
  9  from openid import kvform 
 10  from openid import oidutil 
 11   
12 -class Association(object):
13 """ 14 This class represents an association between a server and a 15 consumer. In general, users of this library will never see 16 instances of this object. The only exception is if you implement 17 a custom C{L{OpenIDStore<openid.store.interface.OpenIDStore>}}. 18 19 If you do implement such a store, it will need to store the values 20 of the C{L{handle}}, C{L{secret}}, C{L{issued}}, C{L{lifetime}}, and 21 C{L{assoc_type}} instance variables. 22 23 @ivar handle: This is the handle the server gave this association. 24 25 @type handle: C{str} 26 27 28 @ivar secret: This is the shared secret the server generated for 29 this association. 30 31 @type secret: C{str} 32 33 34 @ivar issued: This is the time this association was issued, in 35 seconds since 00:00 GMT, January 1, 1970. (ie, a unix 36 timestamp) 37 38 @type issued: C{int} 39 40 41 @ivar lifetime: This is the amount of time this association is 42 good for, measured in seconds since the association was 43 issued. 44 45 @type lifetime: C{int} 46 47 48 @ivar assoc_type: This is the type of association this instance 49 represents. The only valid value of this field at this time 50 is C{'HMAC-SHA1'}, but new types may be defined in the future. 51 52 @type assoc_type: C{str} 53 54 55 @sort: __init__, fromExpiresIn, getExpiresIn, __eq__, __ne__, 56 handle, secret, issued, lifetime, assoc_type 57 """ 58 59 # This is a HMAC-SHA1 specific value. 60 SIG_LENGTH = 20 61 62 # The ordering and name of keys as stored by serialize 63 assoc_keys = [ 64 'version', 65 'handle', 66 'secret', 67 'issued', 68 'lifetime', 69 'assoc_type', 70 ] 71
72 - def fromExpiresIn(cls, expires_in, handle, secret, assoc_type):
73 """ 74 This is an alternate constructor used by the OpenID consumer 75 library to create associations. C{L{OpenIDStore 76 <openid.store.interface.OpenIDStore>}} implementations 77 shouldn't use this constructor. 78 79 80 @param expires_in: This is the amount of time this association 81 is good for, measured in seconds since the association was 82 issued. 83 84 @type expires_in: C{int} 85 86 87 @param handle: This is the handle the server gave this 88 association. 89 90 @type handle: C{str} 91 92 93 @param secret: This is the shared secret the server generated 94 for this association. 95 96 @type secret: C{str} 97 98 99 @param assoc_type: This is the type of association this 100 instance represents. The only valid value of this field 101 at this time is C{'HMAC-SHA1'}, but new types may be 102 defined in the future. 103 104 @type assoc_type: C{str} 105 """ 106 issued = int(time.time()) 107 lifetime = expires_in 108 return cls(handle, secret, issued, lifetime, assoc_type)
109 110 fromExpiresIn = classmethod(fromExpiresIn) 111
112 - def __init__(self, handle, secret, issued, lifetime, assoc_type):
113 """ 114 This is the standard constructor for creating an association. 115 116 117 @param handle: This is the handle the server gave this 118 association. 119 120 @type handle: C{str} 121 122 123 @param secret: This is the shared secret the server generated 124 for this association. 125 126 @type secret: C{str} 127 128 129 @param issued: This is the time this association was issued, 130 in seconds since 00:00 GMT, January 1, 1970. (ie, a unix 131 timestamp) 132 133 @type issued: C{int} 134 135 136 @param lifetime: This is the amount of time this association 137 is good for, measured in seconds since the association was 138 issued. 139 140 @type lifetime: C{int} 141 142 143 @param assoc_type: This is the type of association this 144 instance represents. The only valid value of this field 145 at this time is C{'HMAC-SHA1'}, but new types may be 146 defined in the future. 147 148 @type assoc_type: C{str} 149 """ 150 if assoc_type != 'HMAC-SHA1': 151 fmt = 'HMAC-SHA1 is the only supported association type (got %r)' 152 raise ValueError(fmt % (assoc_type,)) 153 154 self.handle = handle 155 self.secret = secret 156 self.issued = issued 157 self.lifetime = lifetime 158 self.assoc_type = assoc_type
159
160 - def getExpiresIn(self, now=None):
161 """ 162 This returns the number of seconds this association is still 163 valid for, or C{0} if the association is no longer valid. 164 165 166 @return: The number of seconds this association is still valid 167 for, or C{0} if the association is no longer valid. 168 169 @rtype: C{int} 170 """ 171 if now is None: 172 now = int(time.time()) 173 174 return max(0, self.issued + self.lifetime - now)
175 176 expiresIn = property(getExpiresIn) 177
178 - def __eq__(self, other):
179 """ 180 This checks to see if two C{L{Association}} instances 181 represent the same association. 182 183 184 @return: C{True} if the two instances represent the same 185 association, C{False} otherwise. 186 187 @rtype: C{bool} 188 """ 189 return type(self) is type(other) and self.__dict__ == other.__dict__
190
191 - def __ne__(self, other):
192 """ 193 This checks to see if two C{L{Association}} instances 194 represent different associations. 195 196 197 @return: C{True} if the two instances represent different 198 associations, C{False} otherwise. 199 200 @rtype: C{bool} 201 """ 202 return not (self == other)
203
204 - def serialize(self):
205 """ 206 Convert an association to KV form. 207 208 @return: String in KV form suitable for deserialization by 209 deserialize. 210 211 @rtype: str 212 """ 213 data = { 214 'version':'2', 215 'handle':self.handle, 216 'secret':oidutil.toBase64(self.secret), 217 'issued':str(int(self.issued)), 218 'lifetime':str(int(self.lifetime)), 219 'assoc_type':self.assoc_type 220 } 221 222 assert len(data) == len(self.assoc_keys) 223 pairs = [] 224 for field_name in self.assoc_keys: 225 pairs.append((field_name, data[field_name])) 226 227 return kvform.seqToKV(pairs, strict=True)
228
229 - def deserialize(cls, assoc_s):
230 """ 231 Parse an association as stored by serialize(). 232 233 inverse of serialize 234 235 236 @param assoc_s: Association as serialized by serialize() 237 238 @type assoc_s: str 239 240 241 @return: instance of this class 242 """ 243 pairs = kvform.kvToSeq(assoc_s, strict=True) 244 keys = [] 245 values = [] 246 for k, v in pairs: 247 keys.append(k) 248 values.append(v) 249 250 if keys != cls.assoc_keys: 251 raise ValueError('Unexpected key values: %r', keys) 252 253 version, handle, secret, issued, lifetime, assoc_type = values 254 if version != '2': 255 raise ValueError('Unknown version: %r' % version) 256 issued = int(issued) 257 lifetime = int(lifetime) 258 secret = oidutil.fromBase64(secret) 259 return cls(handle, secret, issued, lifetime, assoc_type)
260 261 deserialize = classmethod(deserialize) 262
263 - def sign(self, pairs):
264 """ 265 Generate a signature for a sequence of (key, value) pairs 266 267 268 @param pairs: The pairs to sign, in order 269 270 @type pairs: sequence of (str, str) 271 272 273 @return: The binary signature of this sequence of pairs 274 275 @rtype: str 276 """ 277 assert self.assoc_type == 'HMAC-SHA1' 278 kv = kvform.seqToKV(pairs) 279 return cryptutil.hmacSha1(self.secret, kv)
280
281 - def signDict(self, fields, data, prefix='openid.'):
282 """ 283 Generate a signature for some fields in a dictionary 284 285 286 @param fields: The fields to sign, in order 287 288 @type fields: sequence of str 289 290 291 @param data: Dictionary of values to sign 292 293 @type data: {str:str} 294 295 296 @return: the signature, base64 encoded 297 298 @rtype: str 299 """ 300 pairs = [] 301 for field in fields: 302 pairs.append((field, data.get(prefix + field, ''))) 303 304 return oidutil.toBase64(self.sign(pairs))
305
306 - def addSignature(self, fields, data, prefix='openid.'):
307 sig = self.signDict(fields, data, prefix) 308 signed = ','.join(fields) 309 data[prefix + 'sig'] = sig 310 data[prefix + 'signed'] = signed
311
312 - def checkSignature(self, data, prefix='openid.'):
313 try: 314 signed = data[prefix + 'signed'] 315 fields = signed.split(',') 316 expected_sig = self.signDict(fields, data, prefix) 317 request_sig = data[prefix + 'sig'] 318 except KeyError: 319 return False 320 321 return request_sig == expected_sig
322