Comment out the 'loadtest' backend in the 'counter' backend sample so that it does...
[gae-samples.git] / openid-consumer / openid / extensions / pape.py
blobf5f931b9f5e40e6e233ff88684d5fe05262c04c9
1 """An implementation of the OpenID Provider Authentication Policy
2 Extension 1.0
4 @see: http://openid.net/developers/specs/
6 @since: 2.1.0
7 """
9 __all__ = [
10 'Request',
11 'Response',
12 'ns_uri',
13 'AUTH_PHISHING_RESISTANT',
14 'AUTH_MULTI_FACTOR',
15 'AUTH_MULTI_FACTOR_PHYSICAL',
18 from openid.extension import Extension
20 ns_uri = "http://specs.openid.net/extensions/pape/1.0"
22 AUTH_MULTI_FACTOR_PHYSICAL = \
23 'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical'
24 AUTH_MULTI_FACTOR = \
25 'http://schemas.openid.net/pape/policies/2007/06/multi-factor'
26 AUTH_PHISHING_RESISTANT = \
27 'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant'
29 class Request(Extension):
30 """A Provider Authentication Policy request, sent from a relying
31 party to a provider
33 @ivar preferred_auth_policies: The authentication policies that
34 the relying party prefers
35 @type preferred_auth_policies: [str]
37 @ivar max_auth_age: The maximum time, in seconds, that the relying
38 party wants to allow to have elapsed before the user must
39 re-authenticate
40 @type max_auth_age: int or NoneType
41 """
43 ns_alias = 'pape'
45 def __init__(self, preferred_auth_policies=None, max_auth_age=None):
46 super(Request, self).__init__(self)
47 if not preferred_auth_policies:
48 preferred_auth_policies = []
50 self.preferred_auth_policies = preferred_auth_policies
51 self.max_auth_age = max_auth_age
53 def __nonzero__(self):
54 return bool(self.preferred_auth_policies or
55 self.max_auth_age is not None)
57 def addPolicyURI(self, policy_uri):
58 """Add an acceptable authentication policy URI to this request
60 This method is intended to be used by the relying party to add
61 acceptable authentication types to the request.
63 @param policy_uri: The identifier for the preferred type of
64 authentication.
65 @see: http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#auth_policies
66 """
67 if policy_uri not in self.preferred_auth_policies:
68 self.preferred_auth_policies.append(policy_uri)
70 def getExtensionArgs(self):
71 """@see: C{L{Extension.getExtensionArgs}}
72 """
73 ns_args = {
74 'preferred_auth_policies':' '.join(self.preferred_auth_policies)
77 if self.max_auth_age is not None:
78 ns_args['max_auth_age'] = str(self.max_auth_age)
80 return ns_args
82 def fromOpenIDRequest(cls, request):
83 """Instantiate a Request object from the arguments in a
84 C{checkid_*} OpenID message
85 """
86 self = cls()
87 args = request.message.getArgs(self.ns_uri)
89 if args == {}:
90 return None
92 self.parseExtensionArgs(args)
93 return self
95 fromOpenIDRequest = classmethod(fromOpenIDRequest)
97 def parseExtensionArgs(self, args):
98 """Set the state of this request to be that expressed in these
99 PAPE arguments
101 @param args: The PAPE arguments without a namespace
103 @rtype: None
105 @raises ValueError: When the max_auth_age is not parseable as
106 an integer
109 # preferred_auth_policies is a space-separated list of policy URIs
110 self.preferred_auth_policies = []
112 policies_str = args.get('preferred_auth_policies')
113 if policies_str:
114 for uri in policies_str.split(' '):
115 if uri not in self.preferred_auth_policies:
116 self.preferred_auth_policies.append(uri)
118 # max_auth_age is base-10 integer number of seconds
119 max_auth_age_str = args.get('max_auth_age')
120 self.max_auth_age = None
122 if max_auth_age_str:
123 try:
124 self.max_auth_age = int(max_auth_age_str)
125 except ValueError:
126 pass
128 def preferredTypes(self, supported_types):
129 """Given a list of authentication policy URIs that a provider
130 supports, this method returns the subsequence of those types
131 that are preferred by the relying party.
133 @param supported_types: A sequence of authentication policy
134 type URIs that are supported by a provider
136 @returns: The sub-sequence of the supported types that are
137 preferred by the relying party. This list will be ordered
138 in the order that the types appear in the supported_types
139 sequence, and may be empty if the provider does not prefer
140 any of the supported authentication types.
142 @returntype: [str]
144 return filter(self.preferred_auth_policies.__contains__,
145 supported_types)
147 Request.ns_uri = ns_uri
150 class Response(Extension):
151 """A Provider Authentication Policy response, sent from a provider
152 to a relying party
155 ns_alias = 'pape'
157 def __init__(self, auth_policies=None, auth_age=None,
158 nist_auth_level=None):
159 super(Response, self).__init__(self)
160 if auth_policies:
161 self.auth_policies = auth_policies
162 else:
163 self.auth_policies = []
165 self.auth_age = auth_age
166 self.nist_auth_level = nist_auth_level
168 def addPolicyURI(self, policy_uri):
169 """Add a authentication policy to this response
171 This method is intended to be used by the provider to add a
172 policy that the provider conformed to when authenticating the user.
174 @param policy_uri: The identifier for the preferred type of
175 authentication.
176 @see: http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#auth_policies
178 if policy_uri not in self.auth_policies:
179 self.auth_policies.append(policy_uri)
181 def fromSuccessResponse(cls, success_response):
182 """Create a C{L{Response}} object from a successful OpenID
183 library response
184 (C{L{openid.consumer.consumer.SuccessResponse}}) response
185 message
187 @param success_response: A SuccessResponse from consumer.complete()
188 @type success_response: C{L{openid.consumer.consumer.SuccessResponse}}
190 @rtype: Response
191 @returns: A provider authentication policy response from the
192 data that was supplied with the C{id_res} response.
194 self = cls()
196 # PAPE requires that the args be signed.
197 args = success_response.getSignedNS(self.ns_uri)
199 self.parseExtensionArgs(args)
201 return self
203 def parseExtensionArgs(self, args, strict=False):
204 """Parse the provider authentication policy arguments into the
205 internal state of this object
207 @param args: unqualified provider authentication policy
208 arguments
210 @param strict: Whether to raise an exception when bad data is
211 encountered
213 @returns: None. The data is parsed into the internal fields of
214 this object.
216 policies_str = args.get('auth_policies')
217 if policies_str:
218 self.auth_policies = policies_str.split(' ')
220 nist_level_str = args.get('nist_auth_level')
221 if nist_level_str:
222 try:
223 nist_level = int(nist_level_str)
224 except ValueError:
225 if strict:
226 raise ValueError('nist_auth_level must be an integer between '
227 'zero and four, inclusive')
228 else:
229 self.nist_auth_level = None
230 else:
231 if 0 <= nist_level < 5:
232 self.nist_auth_level = nist_level
234 auth_age_str = args.get('auth_age')
235 if auth_age_str:
236 try:
237 auth_age = int(auth_age_str)
238 except ValueError:
239 if strict:
240 raise
241 else:
242 if auth_age >= 0:
243 self.auth_age = auth_age
244 elif strict:
245 raise ValueError('Auth age must be above zero')
247 fromSuccessResponse = classmethod(fromSuccessResponse)
249 def getExtensionArgs(self):
250 """@see: C{L{Extension.getExtensionArgs}}
252 ns_args = {
253 'auth_policies':' '.join(self.auth_policies),
256 if self.nist_auth_level is not None:
257 if self.nist_auth_level not in range(0, 5):
258 raise ValueError('nist_auth_level must be an integer between '
259 'zero and four, inclusive')
260 ns_args['nist_auth_level'] = str(self.nist_auth_level)
262 if self.auth_age is not None:
263 if self.auth_age < 0:
264 raise ValueError('Auth age must be above zero')
266 ns_args['auth_age'] = str(int(self.auth_age))
268 return ns_args
270 Response.ns_uri = ns_uri