1 """An implementation of the OpenID Provider Authentication Policy
4 @see: http://openid.net/developers/specs/
13 'AUTH_PHISHING_RESISTANT',
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'
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
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
40 @type max_auth_age: int or NoneType
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
65 @see: http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#auth_policies
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}}
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
)
82 def fromOpenIDRequest(cls
, request
):
83 """Instantiate a Request object from the arguments in a
84 C{checkid_*} OpenID message
87 args
= request
.message
.getArgs(self
.ns_uri
)
92 self
.parseExtensionArgs(args
)
95 fromOpenIDRequest
= classmethod(fromOpenIDRequest
)
97 def parseExtensionArgs(self
, args
):
98 """Set the state of this request to be that expressed in these
101 @param args: The PAPE arguments without a namespace
105 @raises ValueError: When the max_auth_age is not parseable as
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')
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
124 self
.max_auth_age
= int(max_auth_age_str
)
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.
144 return filter(self
.preferred_auth_policies
.__contains
__,
147 Request
.ns_uri
= ns_uri
150 class Response(Extension
):
151 """A Provider Authentication Policy response, sent from a provider
157 def __init__(self
, auth_policies
=None, auth_age
=None,
158 nist_auth_level
=None):
159 super(Response
, self
).__init
__(self
)
161 self
.auth_policies
= auth_policies
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
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
184 (C{L{openid.consumer.consumer.SuccessResponse}}) response
187 @param success_response: A SuccessResponse from consumer.complete()
188 @type success_response: C{L{openid.consumer.consumer.SuccessResponse}}
191 @returns: A provider authentication policy response from the
192 data that was supplied with the C{id_res} response.
196 # PAPE requires that the args be signed.
197 args
= success_response
.getSignedNS(self
.ns_uri
)
199 self
.parseExtensionArgs(args
)
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
210 @param strict: Whether to raise an exception when bad data is
213 @returns: None. The data is parsed into the internal fields of
216 policies_str
= args
.get('auth_policies')
218 self
.auth_policies
= policies_str
.split(' ')
220 nist_level_str
= args
.get('nist_auth_level')
223 nist_level
= int(nist_level_str
)
226 raise ValueError('nist_auth_level must be an integer between '
227 'zero and four, inclusive')
229 self
.nist_auth_level
= None
231 if 0 <= nist_level
< 5:
232 self
.nist_auth_level
= nist_level
234 auth_age_str
= args
.get('auth_age')
237 auth_age
= int(auth_age_str
)
243 self
.auth_age
= auth_age
245 raise ValueError('Auth age must be above zero')
247 fromSuccessResponse
= classmethod(fromSuccessResponse
)
249 def getExtensionArgs(self
):
250 """@see: C{L{Extension.getExtensionArgs}}
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
))
270 Response
.ns_uri
= ns_uri