Merge lp:ubuntu-uy-website into lp:ubuntu-uy-website/trunk

Proposed by Pablo Rubianes
Status: Merged
Merged at revision: 33
Proposed branch: lp:ubuntu-uy-website
Merge into: lp:ubuntu-uy-website/trunk
Diff against target: 101482 lines (+82070/-17446)
270 files modified
Auth/OpenID.php (+563/-0)
Auth/OpenID/AX.php (+1022/-0)
Auth/OpenID/Association.php (+610/-0)
Auth/OpenID/BigMath.php (+452/-0)
Auth/OpenID/Consumer.php (+2230/-0)
Auth/OpenID/CryptUtil.php (+108/-0)
Auth/OpenID/DatabaseConnection.php (+130/-0)
Auth/OpenID/DiffieHellman.php (+113/-0)
Auth/OpenID/Discover.php (+606/-0)
Auth/OpenID/DumbStore.php (+99/-0)
Auth/OpenID/Extension.php (+61/-0)
Auth/OpenID/FileStore.php (+618/-0)
Auth/OpenID/HMAC.php (+98/-0)
Auth/OpenID/Interface.php (+196/-0)
Auth/OpenID/KVForm.php (+111/-0)
Auth/OpenID/MDB2Store.php (+413/-0)
Auth/OpenID/MemcachedStore.php (+207/-0)
Auth/OpenID/Message.php (+920/-0)
Auth/OpenID/MySQLStore.php (+77/-0)
Auth/OpenID/Nonce.php (+108/-0)
Auth/OpenID/PAPE.php (+300/-0)
Auth/OpenID/Parse.php (+377/-0)
Auth/OpenID/PostgreSQLStore.php (+112/-0)
Auth/OpenID/SQLStore.php (+557/-0)
Auth/OpenID/SQLiteStore.php (+70/-0)
Auth/OpenID/SReg.php (+521/-0)
Auth/OpenID/Server.php (+1765/-0)
Auth/OpenID/ServerRequest.php (+36/-0)
Auth/OpenID/TrustRoot.php (+461/-0)
Auth/OpenID/URINorm.php (+249/-0)
Auth/Yadis/HTTPFetcher.php (+174/-0)
Auth/Yadis/Manager.php (+521/-0)
Auth/Yadis/Misc.php (+58/-0)
Auth/Yadis/ParanoidHTTPFetcher.php (+245/-0)
Auth/Yadis/ParseHTML.php (+258/-0)
Auth/Yadis/PlainHTTPFetcher.php (+248/-0)
Auth/Yadis/XML.php (+352/-0)
Auth/Yadis/XRDS.php (+478/-0)
Auth/Yadis/XRI.php (+234/-0)
Auth/Yadis/XRIRes.php (+72/-0)
Auth/Yadis/Yadis.php (+382/-0)
Doc/BluePrints.txt (+14/-0)
Doc/COPYRIGHT.txt (+22/-0)
Doc/LICENCE.txt (+133/-0)
Doc/LICENCIA.txt (+117/-0)
Doc/Launchpad.txt (+7/-0)
Doc/Readme.txt (+86/-0)
Doc/openid/Auth/OpenID.php (+552/-0)
Doc/openid/Auth/OpenID/AX.php (+1023/-0)
Doc/openid/Auth/OpenID/Association.php (+613/-0)
Doc/openid/Auth/OpenID/BigMath.php (+471/-0)
Doc/openid/Auth/OpenID/Consumer.php (+2227/-0)
Doc/openid/Auth/OpenID/CryptUtil.php (+109/-0)
Doc/openid/Auth/OpenID/DatabaseConnection.php (+131/-0)
Doc/openid/Auth/OpenID/DiffieHellman.php (+113/-0)
Doc/openid/Auth/OpenID/Discover.php (+548/-0)
Doc/openid/Auth/OpenID/DumbStore.php (+100/-0)
Doc/openid/Auth/OpenID/Extension.php (+62/-0)
Doc/openid/Auth/OpenID/FileStore.php (+618/-0)
Doc/openid/Auth/OpenID/HMAC.php (+99/-0)
Doc/openid/Auth/OpenID/Interface.php (+197/-0)
Doc/openid/Auth/OpenID/KVForm.php (+112/-0)
Doc/openid/Auth/OpenID/MemcachedStore.php (+208/-0)
Doc/openid/Auth/OpenID/Message.php (+915/-0)
Doc/openid/Auth/OpenID/MySQLStore.php (+78/-0)
Doc/openid/Auth/OpenID/Nonce.php (+109/-0)
Doc/openid/Auth/OpenID/PAPE.php (+301/-0)
Doc/openid/Auth/OpenID/Parse.php (+352/-0)
Doc/openid/Auth/OpenID/PostgreSQLStore.php (+113/-0)
Doc/openid/Auth/OpenID/SQLStore.php (+569/-0)
Doc/openid/Auth/OpenID/SQLiteStore.php (+71/-0)
Doc/openid/Auth/OpenID/SReg.php (+521/-0)
Doc/openid/Auth/OpenID/Server.php (+1754/-0)
Doc/openid/Auth/OpenID/ServerRequest.php (+37/-0)
Doc/openid/Auth/OpenID/TrustRoot.php (+462/-0)
Doc/openid/Auth/OpenID/URINorm.php (+249/-0)
Doc/openid/Auth/Yadis/HTTPFetcher.php (+147/-0)
Doc/openid/Auth/Yadis/Manager.php (+529/-0)
Doc/openid/Auth/Yadis/Misc.php (+59/-0)
Doc/openid/Auth/Yadis/ParanoidHTTPFetcher.php (+228/-0)
Doc/openid/Auth/Yadis/ParseHTML.php (+259/-0)
Doc/openid/Auth/Yadis/PlainHTTPFetcher.php (+251/-0)
Doc/openid/Auth/Yadis/XML.php (+374/-0)
Doc/openid/Auth/Yadis/XRDS.php (+478/-0)
Doc/openid/Auth/Yadis/XRI.php (+234/-0)
Doc/openid/Auth/Yadis/XRIRes.php (+72/-0)
Doc/openid/Auth/Yadis/Yadis.php (+382/-0)
Doc/openid/conectar.html (+28/-0)
Doc/openid/conectar.php (+28/-0)
Doc/openid/config.php (+12/-0)
Doc/openid/formulario-registro.php (+114/-0)
Doc/openid/index.html (+44/-0)
Doc/openid/login.php (+42/-0)
Doc/openid/logout.php (+7/-0)
Doc/openid/openid.php (+40/-0)
Doc/openid/openid_return.php (+64/-0)
Doc/openid/usuarios.sql (+8/-0)
Doc/openid/zona-restringida.php (+10/-0)
Readme.txt (+0/-18)
SQL/locoportal.sql (+103/-0)
admin/css/UnitySlide.css (+313/-0)
admin/css/Unityslide.css (+311/-0)
admin/css/default.css (+334/-0)
admin/css/ie.css (+299/-0)
admin/css/ie6.css (+301/-0)
admin/css/lightbox.css (+27/-0)
admin/css/wireframe-default.css (+120/-0)
admin/css/wireframe-ie.css (+123/-0)
admin/css/wireframe-ie6.css (+123/-0)
admin/highslide/highslide-full.js (+3302/-0)
admin/highslide/highslide-full.min.js (+9/-0)
admin/highslide/highslide-full.packed.js (+9/-0)
admin/highslide/highslide-ie6.css (+76/-0)
admin/highslide/highslide-with-gallery.js (+2639/-0)
admin/highslide/highslide-with-gallery.min.js (+9/-0)
admin/highslide/highslide-with-gallery.packed.js (+9/-0)
admin/highslide/highslide-with-html.js (+2477/-0)
admin/highslide/highslide-with-html.min.js (+9/-0)
admin/highslide/highslide-with-html.packed.js (+9/-0)
admin/highslide/highslide.css (+886/-0)
admin/highslide/highslide.js (+1886/-0)
admin/highslide/highslide.min.js (+9/-0)
admin/highslide/highslide.packed.js (+9/-0)
admin/index.php (+84/-0)
admin/js/builder.js (+136/-0)
admin/js/effects.js (+1123/-0)
admin/js/lightbox-web.js (+497/-0)
admin/js/lightbox.js (+496/-0)
admin/js/main.js (+43/-0)
admin/js/pngFix.js (+13/-0)
admin/js/prototype.js (+6081/-0)
admin/js/scriptaculous.js (+68/-0)
admin/js/ventanas.js (+20/-0)
admin/template.php (+61/-0)
auth.php (+84/-0)
calendario.php (+82/-115)
cerrar.php (+11/-0)
common.php (+97/-0)
conex.php (+14/-14)
css/default.css (+23/-5)
css/lightbox.css (+27/-0)
enviarPedido.php (+107/-0)
estadisticas.php (+42/-45)
examples/ajax.html (+51/-0)
examples/flash.html (+61/-0)
examples/gallery-controls-in-heading.html (+89/-0)
examples/gallery-dark.html (+93/-0)
examples/gallery-floating-caption.html (+91/-0)
examples/gallery-floating-thumbs.html (+144/-0)
examples/gallery-horizontal-strip.html (+147/-0)
examples/gallery-in-box.html (+95/-0)
examples/gallery-in-page.html (+221/-0)
examples/gallery-thumbstrip-above.html (+142/-0)
examples/gallery-vertical-strip.html (+178/-0)
examples/gallery-white.html (+90/-0)
examples/headline.html (+49/-0)
examples/html.html (+67/-0)
examples/iframe.html (+44/-0)
examples/image-map.html (+49/-0)
examples/includes/ajax.htm (+45/-0)
examples/includes/include-short.htm (+40/-0)
examples/inline.html (+50/-0)
examples/mini-galleries.html (+135/-0)
examples/mini-gallery.html (+89/-0)
examples/no-border.html (+62/-0)
examples/no-outline.html (+52/-0)
examples/outer-glow.html (+53/-0)
examples/white-10px.html (+51/-0)
examples/white-rounded-outline.html (+51/-0)
examples/youtube.html (+62/-0)
finish_auth.php (+97/-0)
footer.php (+90/-93)
highslide/highslide-full.js (+3302/-0)
highslide/highslide-full.min.js (+9/-0)
highslide/highslide-full.packed.js (+9/-0)
highslide/highslide-ie6.css (+76/-0)
highslide/highslide-with-gallery.js (+2639/-0)
highslide/highslide-with-gallery.min.js (+9/-0)
highslide/highslide-with-gallery.packed.js (+9/-0)
highslide/highslide-with-html.js (+2477/-0)
highslide/highslide-with-html.min.js (+9/-0)
highslide/highslide-with-html.packed.js (+9/-0)
highslide/highslide.css (+886/-0)
highslide/highslide.js (+1886/-0)
highslide/highslide.min.js (+9/-0)
highslide/highslide.packed.js (+9/-0)
index.php (+193/-157)
irc.php (+57/-51)
js/builder.js (+136/-0)
js/effects.js (+1123/-0)
js/lightbox-web.js (+497/-0)
js/lightbox.js (+496/-0)
js/prototype.js (+6081/-0)
js/scriptaculous.js (+68/-0)
live.php (+3/-2)
login/Auth/OpenID.php (+0/-563)
login/Auth/OpenID/AX.php (+0/-1022)
login/Auth/OpenID/Association.php (+0/-610)
login/Auth/OpenID/BigMath.php (+0/-452)
login/Auth/OpenID/Consumer.php (+0/-2230)
login/Auth/OpenID/CryptUtil.php (+0/-108)
login/Auth/OpenID/DatabaseConnection.php (+0/-130)
login/Auth/OpenID/DiffieHellman.php (+0/-113)
login/Auth/OpenID/Discover.php (+0/-606)
login/Auth/OpenID/DumbStore.php (+0/-99)
login/Auth/OpenID/Extension.php (+0/-61)
login/Auth/OpenID/FileStore.php (+0/-618)
login/Auth/OpenID/HMAC.php (+0/-98)
login/Auth/OpenID/Interface.php (+0/-196)
login/Auth/OpenID/KVForm.php (+0/-111)
login/Auth/OpenID/MDB2Store.php (+0/-413)
login/Auth/OpenID/MemcachedStore.php (+0/-207)
login/Auth/OpenID/Message.php (+0/-920)
login/Auth/OpenID/MySQLStore.php (+0/-77)
login/Auth/OpenID/Nonce.php (+0/-108)
login/Auth/OpenID/PAPE.php (+0/-300)
login/Auth/OpenID/Parse.php (+0/-377)
login/Auth/OpenID/PostgreSQLStore.php (+0/-112)
login/Auth/OpenID/SQLStore.php (+0/-557)
login/Auth/OpenID/SQLiteStore.php (+0/-70)
login/Auth/OpenID/SReg.php (+0/-521)
login/Auth/OpenID/Server.php (+0/-1765)
login/Auth/OpenID/ServerRequest.php (+0/-36)
login/Auth/OpenID/TrustRoot.php (+0/-461)
login/Auth/OpenID/URINorm.php (+0/-249)
login/Auth/Yadis/HTTPFetcher.php (+0/-174)
login/Auth/Yadis/Manager.php (+0/-521)
login/Auth/Yadis/Misc.php (+0/-58)
login/Auth/Yadis/ParanoidHTTPFetcher.php (+0/-245)
login/Auth/Yadis/ParseHTML.php (+0/-258)
login/Auth/Yadis/PlainHTTPFetcher.php (+0/-248)
login/Auth/Yadis/XML.php (+0/-352)
login/Auth/Yadis/XRDS.php (+0/-478)
login/Auth/Yadis/XRI.php (+0/-234)
login/Auth/Yadis/XRIRes.php (+0/-72)
login/Auth/Yadis/Yadis.php (+0/-382)
login/conectar.php (+0/-28)
login/config.php (+0/-12)
login/formulario-registro.php (+0/-114)
login/index.html (+0/-44)
login/login.php (+0/-42)
login/logout.php (+0/-7)
login/openid.php (+0/-42)
login/openid_return.php (+0/-72)
login/usuarios.sql (+0/-8)
login/zona-restringida.php (+0/-10)
menu.php (+37/-16)
planet/uyplanet/config.php (+66/-0)
planet/uyplanet/css/2c-r-fixed.css (+68/-0)
planet/uyplanet/css/images.css (+64/-0)
planet/uyplanet/css/reset.css (+64/-0)
planet/uyplanet/css/typography.css (+141/-0)
planet/uyplanet/index.php (+169/-0)
planet/uyplanet/magpierss/extlib/Snoopy.class.inc (+900/-0)
planet/uyplanet/magpierss/rss_cache.inc (+200/-0)
planet/uyplanet/magpierss/rss_fetch.inc (+458/-0)
planet/uyplanet/magpierss/rss_parse.inc (+605/-0)
planet/uyplanet/magpierss/rss_utils.inc (+67/-0)
planet/uyplanet/planet.php (+57/-0)
planet/uyplanet/style.css (+274/-0)
shipituy.php (+275/-0)
shipituypopup.php (+264/-0)
slide/fotos-uy.html (+14/-11)
slide/novedades.html (+1/-0)
slide/webdevel.html (+1/-0)
template.php (+82/-78)
ubuntu.php (+120/-123)
unity.php (+95/-127)
webdevel.php (+222/-0)
webdevel/website.xhtml (+130/-0)
To merge this branch: bzr merge lp:ubuntu-uy-website
Reviewer Review Type Date Requested Status
Pablo Rubianes Approve
Review via email: mp+65112@code.launchpad.net

This proposal has been superseded by a proposal from 2011-06-20.

Commit message

Se aprueba el merge de la version 1.0

Description of the change

Pasar a trunk todo el desarollo de la version 1.0

To post a comment you must log in.
Revision history for this message
Pablo Rubianes (pablorubianes-uy) wrote :

Se aprueba

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'Auth'
=== added directory 'Auth/OpenID'
=== added file 'Auth/OpenID.php'
--- Auth/OpenID.php 1970-01-01 00:00:00 +0000
+++ Auth/OpenID.php 2011-06-19 04:48:34 +0000
@@ -0,0 +1,563 @@
1<?php
2
3/**
4 * This is the PHP OpenID library by JanRain, Inc.
5 *
6 * This module contains core utility functionality used by the
7 * library. See Consumer.php and Server.php for the consumer and
8 * server implementations.
9 *
10 * PHP versions 4 and 5
11 *
12 * LICENSE: See the COPYING file included in this distribution.
13 *
14 * @package OpenID
15 * @author JanRain, Inc. <openid@janrain.com>
16 * @copyright 2005-2008 Janrain, Inc.
17 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
18 */
19
20/**
21 * The library version string
22 */
23define('Auth_OpenID_VERSION', '2.2.2');
24
25/**
26 * Require the fetcher code.
27 */
28require_once "Auth/Yadis/PlainHTTPFetcher.php";
29require_once "Auth/Yadis/ParanoidHTTPFetcher.php";
30require_once "Auth/OpenID/BigMath.php";
31require_once "Auth/OpenID/URINorm.php";
32
33/**
34 * Status code returned by the server when the only option is to show
35 * an error page, since we do not have enough information to redirect
36 * back to the consumer. The associated value is an error message that
37 * should be displayed on an HTML error page.
38 *
39 * @see Auth_OpenID_Server
40 */
41define('Auth_OpenID_LOCAL_ERROR', 'local_error');
42
43/**
44 * Status code returned when there is an error to return in key-value
45 * form to the consumer. The caller should return a 400 Bad Request
46 * response with content-type text/plain and the value as the body.
47 *
48 * @see Auth_OpenID_Server
49 */
50define('Auth_OpenID_REMOTE_ERROR', 'remote_error');
51
52/**
53 * Status code returned when there is a key-value form OK response to
54 * the consumer. The value associated with this code is the
55 * response. The caller should return a 200 OK response with
56 * content-type text/plain and the value as the body.
57 *
58 * @see Auth_OpenID_Server
59 */
60define('Auth_OpenID_REMOTE_OK', 'remote_ok');
61
62/**
63 * Status code returned when there is a redirect back to the
64 * consumer. The value is the URL to redirect back to. The caller
65 * should return a 302 Found redirect with a Location: header
66 * containing the URL.
67 *
68 * @see Auth_OpenID_Server
69 */
70define('Auth_OpenID_REDIRECT', 'redirect');
71
72/**
73 * Status code returned when the caller needs to authenticate the
74 * user. The associated value is a {@link Auth_OpenID_ServerRequest}
75 * object that can be used to complete the authentication. If the user
76 * has taken some authentication action, use the retry() method of the
77 * {@link Auth_OpenID_ServerRequest} object to complete the request.
78 *
79 * @see Auth_OpenID_Server
80 */
81define('Auth_OpenID_DO_AUTH', 'do_auth');
82
83/**
84 * Status code returned when there were no OpenID arguments
85 * passed. This code indicates that the caller should return a 200 OK
86 * response and display an HTML page that says that this is an OpenID
87 * server endpoint.
88 *
89 * @see Auth_OpenID_Server
90 */
91define('Auth_OpenID_DO_ABOUT', 'do_about');
92
93/**
94 * Defines for regexes and format checking.
95 */
96define('Auth_OpenID_letters',
97 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
98
99define('Auth_OpenID_digits',
100 "0123456789");
101
102define('Auth_OpenID_punct',
103 "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
104
105Auth_OpenID_include_init();
106
107/**
108 * The OpenID utility function class.
109 *
110 * @package OpenID
111 * @access private
112 */
113class Auth_OpenID {
114
115 /**
116 * Return true if $thing is an Auth_OpenID_FailureResponse object;
117 * false if not.
118 *
119 * @access private
120 */
121 static function isFailure($thing)
122 {
123 return is_a($thing, 'Auth_OpenID_FailureResponse');
124 }
125
126 /**
127 * Gets the query data from the server environment based on the
128 * request method used. If GET was used, this looks at
129 * $_SERVER['QUERY_STRING'] directly. If POST was used, this
130 * fetches data from the special php://input file stream.
131 *
132 * Returns an associative array of the query arguments.
133 *
134 * Skips invalid key/value pairs (i.e. keys with no '=value'
135 * portion).
136 *
137 * Returns an empty array if neither GET nor POST was used, or if
138 * POST was used but php://input cannot be opened.
139 *
140 * See background:
141 * http://lists.openidenabled.com/pipermail/dev/2007-March/000395.html
142 *
143 * @access private
144 */
145 static function getQuery($query_str=null)
146 {
147 $data = array();
148
149 if ($query_str !== null) {
150 $data = Auth_OpenID::params_from_string($query_str);
151 } else if (!array_key_exists('REQUEST_METHOD', $_SERVER)) {
152 // Do nothing.
153 } else {
154 // XXX HACK FIXME HORRIBLE.
155 //
156 // POSTing to a URL with query parameters is acceptable, but
157 // we don't have a clean way to distinguish those parameters
158 // when we need to do things like return_to verification
159 // which only want to look at one kind of parameter. We're
160 // going to emulate the behavior of some other environments
161 // by defaulting to GET and overwriting with POST if POST
162 // data is available.
163 $data = Auth_OpenID::params_from_string($_SERVER['QUERY_STRING']);
164
165 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
166 $str = file_get_contents('php://input');
167
168 if ($str === false) {
169 $post = array();
170 } else {
171 $post = Auth_OpenID::params_from_string($str);
172 }
173
174 $data = array_merge($data, $post);
175 }
176 }
177
178 return $data;
179 }
180
181 static function params_from_string($str)
182 {
183 $chunks = explode("&", $str);
184
185 $data = array();
186 foreach ($chunks as $chunk) {
187 $parts = explode("=", $chunk, 2);
188
189 if (count($parts) != 2) {
190 continue;
191 }
192
193 list($k, $v) = $parts;
194 $data[urldecode($k)] = urldecode($v);
195 }
196
197 return $data;
198 }
199
200 /**
201 * Create dir_name as a directory if it does not exist. If it
202 * exists, make sure that it is, in fact, a directory. Returns
203 * true if the operation succeeded; false if not.
204 *
205 * @access private
206 */
207 static function ensureDir($dir_name)
208 {
209 if (is_dir($dir_name) || @mkdir($dir_name)) {
210 return true;
211 } else {
212 $parent_dir = dirname($dir_name);
213
214 // Terminal case; there is no parent directory to create.
215 if ($parent_dir == $dir_name) {
216 return true;
217 }
218
219 return (Auth_OpenID::ensureDir($parent_dir) && @mkdir($dir_name));
220 }
221 }
222
223 /**
224 * Adds a string prefix to all values of an array. Returns a new
225 * array containing the prefixed values.
226 *
227 * @access private
228 */
229 static function addPrefix($values, $prefix)
230 {
231 $new_values = array();
232 foreach ($values as $s) {
233 $new_values[] = $prefix . $s;
234 }
235 return $new_values;
236 }
237
238 /**
239 * Convenience function for getting array values. Given an array
240 * $arr and a key $key, get the corresponding value from the array
241 * or return $default if the key is absent.
242 *
243 * @access private
244 */
245 static function arrayGet($arr, $key, $fallback = null)
246 {
247 if (is_array($arr)) {
248 if (array_key_exists($key, $arr)) {
249 return $arr[$key];
250 } else {
251 return $fallback;
252 }
253 } else {
254 trigger_error("Auth_OpenID::arrayGet (key = ".$key.") expected " .
255 "array as first parameter, got " .
256 gettype($arr), E_USER_WARNING);
257
258 return false;
259 }
260 }
261
262 /**
263 * Replacement for PHP's broken parse_str.
264 */
265 static function parse_str($query)
266 {
267 if ($query === null) {
268 return null;
269 }
270
271 $parts = explode('&', $query);
272
273 $new_parts = array();
274 for ($i = 0; $i < count($parts); $i++) {
275 $pair = explode('=', $parts[$i]);
276
277 if (count($pair) != 2) {
278 continue;
279 }
280
281 list($key, $value) = $pair;
282 $new_parts[urldecode($key)] = urldecode($value);
283 }
284
285 return $new_parts;
286 }
287
288 /**
289 * Implements the PHP 5 'http_build_query' functionality.
290 *
291 * @access private
292 * @param array $data Either an array key/value pairs or an array
293 * of arrays, each of which holding two values: a key and a value,
294 * sequentially.
295 * @return string $result The result of url-encoding the key/value
296 * pairs from $data into a URL query string
297 * (e.g. "username=bob&id=56").
298 */
299 static function httpBuildQuery($data)
300 {
301 $pairs = array();
302 foreach ($data as $key => $value) {
303 if (is_array($value)) {
304 $pairs[] = urlencode($value[0])."=".urlencode($value[1]);
305 } else {
306 $pairs[] = urlencode($key)."=".urlencode($value);
307 }
308 }
309 return implode("&", $pairs);
310 }
311
312 /**
313 * "Appends" query arguments onto a URL. The URL may or may not
314 * already have arguments (following a question mark).
315 *
316 * @access private
317 * @param string $url A URL, which may or may not already have
318 * arguments.
319 * @param array $args Either an array key/value pairs or an array of
320 * arrays, each of which holding two values: a key and a value,
321 * sequentially. If $args is an ordinary key/value array, the
322 * parameters will be added to the URL in sorted alphabetical order;
323 * if $args is an array of arrays, their order will be preserved.
324 * @return string $url The original URL with the new parameters added.
325 *
326 */
327 static function appendArgs($url, $args)
328 {
329 if (count($args) == 0) {
330 return $url;
331 }
332
333 // Non-empty array; if it is an array of arrays, use
334 // multisort; otherwise use sort.
335 if (array_key_exists(0, $args) &&
336 is_array($args[0])) {
337 // Do nothing here.
338 } else {
339 $keys = array_keys($args);
340 sort($keys);
341 $new_args = array();
342 foreach ($keys as $key) {
343 $new_args[] = array($key, $args[$key]);
344 }
345 $args = $new_args;
346 }
347
348 $sep = '?';
349 if (strpos($url, '?') !== false) {
350 $sep = '&';
351 }
352
353 return $url . $sep . Auth_OpenID::httpBuildQuery($args);
354 }
355
356 /**
357 * Implements python's urlunparse, which is not available in PHP.
358 * Given the specified components of a URL, this function rebuilds
359 * and returns the URL.
360 *
361 * @access private
362 * @param string $scheme The scheme (e.g. 'http'). Defaults to 'http'.
363 * @param string $host The host. Required.
364 * @param string $port The port.
365 * @param string $path The path.
366 * @param string $query The query.
367 * @param string $fragment The fragment.
368 * @return string $url The URL resulting from assembling the
369 * specified components.
370 */
371 static function urlunparse($scheme, $host, $port = null, $path = '/',
372 $query = '', $fragment = '')
373 {
374
375 if (!$scheme) {
376 $scheme = 'http';
377 }
378
379 if (!$host) {
380 return false;
381 }
382
383 if (!$path) {
384 $path = '';
385 }
386
387 $result = $scheme . "://" . $host;
388
389 if ($port) {
390 $result .= ":" . $port;
391 }
392
393 $result .= $path;
394
395 if ($query) {
396 $result .= "?" . $query;
397 }
398
399 if ($fragment) {
400 $result .= "#" . $fragment;
401 }
402
403 return $result;
404 }
405
406 /**
407 * Given a URL, this "normalizes" it by adding a trailing slash
408 * and / or a leading http:// scheme where necessary. Returns
409 * null if the original URL is malformed and cannot be normalized.
410 *
411 * @access private
412 * @param string $url The URL to be normalized.
413 * @return mixed $new_url The URL after normalization, or null if
414 * $url was malformed.
415 */
416 static function normalizeUrl($url)
417 {
418 @$parsed = parse_url($url);
419
420 if (!$parsed) {
421 return null;
422 }
423
424 if (isset($parsed['scheme']) &&
425 isset($parsed['host'])) {
426 $scheme = strtolower($parsed['scheme']);
427 if (!in_array($scheme, array('http', 'https'))) {
428 return null;
429 }
430 } else {
431 $url = 'http://' . $url;
432 }
433
434 $normalized = Auth_OpenID_urinorm($url);
435 if ($normalized === null) {
436 return null;
437 }
438 list($defragged, $frag) = Auth_OpenID::urldefrag($normalized);
439 return $defragged;
440 }
441
442 /**
443 * Replacement (wrapper) for PHP's intval() because it's broken.
444 *
445 * @access private
446 */
447 static function intval($value)
448 {
449 $re = "/^\\d+$/";
450
451 if (!preg_match($re, $value)) {
452 return false;
453 }
454
455 return intval($value);
456 }
457
458 /**
459 * Count the number of bytes in a string independently of
460 * multibyte support conditions.
461 *
462 * @param string $str The string of bytes to count.
463 * @return int The number of bytes in $str.
464 */
465 static function bytes($str)
466 {
467 return strlen(bin2hex($str)) / 2;
468 }
469
470 /**
471 * Get the bytes in a string independently of multibyte support
472 * conditions.
473 */
474 static function toBytes($str)
475 {
476 $hex = bin2hex($str);
477
478 if (!$hex) {
479 return array();
480 }
481
482 $b = array();
483 for ($i = 0; $i < strlen($hex); $i += 2) {
484 $b[] = chr(base_convert(substr($hex, $i, 2), 16, 10));
485 }
486
487 return $b;
488 }
489
490 static function urldefrag($url)
491 {
492 $parts = explode("#", $url, 2);
493
494 if (count($parts) == 1) {
495 return array($parts[0], "");
496 } else {
497 return $parts;
498 }
499 }
500
501 static function filter($callback, &$sequence)
502 {
503 $result = array();
504
505 foreach ($sequence as $item) {
506 if (call_user_func_array($callback, array($item))) {
507 $result[] = $item;
508 }
509 }
510
511 return $result;
512 }
513
514 static function update(&$dest, &$src)
515 {
516 foreach ($src as $k => $v) {
517 $dest[$k] = $v;
518 }
519 }
520
521 /**
522 * Wrap PHP's standard error_log functionality. Use this to
523 * perform all logging. It will interpolate any additional
524 * arguments into the format string before logging.
525 *
526 * @param string $format_string The sprintf format for the message
527 */
528 static function log($format_string)
529 {
530 $args = func_get_args();
531 $message = call_user_func_array('sprintf', $args);
532 error_log($message);
533 }
534
535 static function autoSubmitHTML($form, $title="OpenId transaction in progress")
536 {
537 return("<html>".
538 "<head><title>".
539 $title .
540 "</title></head>".
541 "<body onload='document.forms[0].submit();'>".
542 $form .
543 "<script>".
544 "var elements = document.forms[0].elements;".
545 "for (var i = 0; i < elements.length; i++) {".
546 " elements[i].style.display = \"none\";".
547 "}".
548 "</script>".
549 "</body>".
550 "</html>");
551 }
552}
553
554/*
555 * Function to run when this file is included.
556 * Abstracted to a function to make life easier
557 * for some PHP optimizers.
558 */
559function Auth_OpenID_include_init() {
560 if (Auth_OpenID_getMathLib() === null) {
561 Auth_OpenID_setNoMathSupport();
562 }
563}
0564
=== added file 'Auth/OpenID/AX.php'
--- Auth/OpenID/AX.php 1970-01-01 00:00:00 +0000
+++ Auth/OpenID/AX.php 2011-06-19 04:48:34 +0000
@@ -0,0 +1,1022 @@
1<?php
2
3/**
4 * Implements the OpenID attribute exchange specification, version 1.0
5 * as of svn revision 370 from openid.net svn.
6 *
7 * @package OpenID
8 */
9
10/**
11 * Require utility classes and functions for the consumer.
12 */
13require_once "Auth/OpenID/Extension.php";
14require_once "Auth/OpenID/Message.php";
15require_once "Auth/OpenID/TrustRoot.php";
16
17define('Auth_OpenID_AX_NS_URI',
18 'http://openid.net/srv/ax/1.0');
19
20// Use this as the 'count' value for an attribute in a FetchRequest to
21// ask for as many values as the OP can provide.
22define('Auth_OpenID_AX_UNLIMITED_VALUES', 'unlimited');
23
24// Minimum supported alias length in characters. Here for
25// completeness.
26define('Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH', 32);
27
28/**
29 * AX utility class.
30 *
31 * @package OpenID
32 */
33class Auth_OpenID_AX {
34 /**
35 * @param mixed $thing Any object which may be an
36 * Auth_OpenID_AX_Error object.
37 *
38 * @return bool true if $thing is an Auth_OpenID_AX_Error; false
39 * if not.
40 */
41 static function isError($thing)
42 {
43 return is_a($thing, 'Auth_OpenID_AX_Error');
44 }
45}
46
47/**
48 * Check an alias for invalid characters; raise AXError if any are
49 * found. Return None if the alias is valid.
50 */
51function Auth_OpenID_AX_checkAlias($alias)
52{
53 if (strpos($alias, ',') !== false) {
54 return new Auth_OpenID_AX_Error(sprintf(
55 "Alias %s must not contain comma", $alias));
56 }
57 if (strpos($alias, '.') !== false) {
58 return new Auth_OpenID_AX_Error(sprintf(
59 "Alias %s must not contain period", $alias));
60 }
61
62 return true;
63}
64
65/**
66 * Results from data that does not meet the attribute exchange 1.0
67 * specification
68 *
69 * @package OpenID
70 */
71class Auth_OpenID_AX_Error {
72 function Auth_OpenID_AX_Error($message=null)
73 {
74 $this->message = $message;
75 }
76}
77
78/**
79 * Abstract class containing common code for attribute exchange
80 * messages.
81 *
82 * @package OpenID
83 */
84class Auth_OpenID_AX_Message extends Auth_OpenID_Extension {
85 /**
86 * ns_alias: The preferred namespace alias for attribute exchange
87 * messages
88 */
89 var $ns_alias = 'ax';
90
91 /**
92 * mode: The type of this attribute exchange message. This must be
93 * overridden in subclasses.
94 */
95 var $mode = null;
96
97 var $ns_uri = Auth_OpenID_AX_NS_URI;
98
99 /**
100 * Return Auth_OpenID_AX_Error if the mode in the attribute
101 * exchange arguments does not match what is expected for this
102 * class; true otherwise.
103 *
104 * @access private
105 */
106 function _checkMode($ax_args)
107 {
108 $mode = Auth_OpenID::arrayGet($ax_args, 'mode');
109 if ($mode != $this->mode) {
110 return new Auth_OpenID_AX_Error(
111 sprintf(
112 "Expected mode '%s'; got '%s'",
113 $this->mode, $mode));
114 }
115
116 return true;
117 }
118
119 /**
120 * Return a set of attribute exchange arguments containing the
121 * basic information that must be in every attribute exchange
122 * message.
123 *
124 * @access private
125 */
126 function _newArgs()
127 {
128 return array('mode' => $this->mode);
129 }
130}
131
132/**
133 * Represents a single attribute in an attribute exchange
134 * request. This should be added to an AXRequest object in order to
135 * request the attribute.
136 *
137 * @package OpenID
138 */
139class Auth_OpenID_AX_AttrInfo {
140 /**
141 * Construct an attribute information object. Do not call this
142 * directly; call make(...) instead.
143 *
144 * @param string $type_uri The type URI for this attribute.
145 *
146 * @param int $count The number of values of this type to request.
147 *
148 * @param bool $required Whether the attribute will be marked as
149 * required in the request.
150 *
151 * @param string $alias The name that should be given to this
152 * attribute in the request.
153 */
154 function Auth_OpenID_AX_AttrInfo($type_uri, $count, $required,
155 $alias)
156 {
157 /**
158 * required: Whether the attribute will be marked as required
159 * when presented to the subject of the attribute exchange
160 * request.
161 */
162 $this->required = $required;
163
164 /**
165 * count: How many values of this type to request from the
166 * subject. Defaults to one.
167 */
168 $this->count = $count;
169
170 /**
171 * type_uri: The identifier that determines what the attribute
172 * represents and how it is serialized. For example, one type
173 * URI representing dates could represent a Unix timestamp in
174 * base 10 and another could represent a human-readable
175 * string.
176 */
177 $this->type_uri = $type_uri;
178
179 /**
180 * alias: The name that should be given to this attribute in
181 * the request. If it is not supplied, a generic name will be
182 * assigned. For example, if you want to call a Unix timestamp
183 * value 'tstamp', set its alias to that value. If two
184 * attributes in the same message request to use the same
185 * alias, the request will fail to be generated.
186 */
187 $this->alias = $alias;
188 }
189
190 /**
191 * Construct an attribute information object. For parameter
192 * details, see the constructor.
193 */
194 static function make($type_uri, $count=1, $required=false,
195 $alias=null)
196 {
197 if ($alias !== null) {
198 $result = Auth_OpenID_AX_checkAlias($alias);
199
200 if (Auth_OpenID_AX::isError($result)) {
201 return $result;
202 }
203 }
204
205 return new Auth_OpenID_AX_AttrInfo($type_uri, $count, $required,
206 $alias);
207 }
208
209 /**
210 * When processing a request for this attribute, the OP should
211 * call this method to determine whether all available attribute
212 * values were requested. If self.count == UNLIMITED_VALUES, this
213 * returns True. Otherwise this returns False, in which case
214 * self.count is an integer.
215 */
216 function wantsUnlimitedValues()
217 {
218 return $this->count === Auth_OpenID_AX_UNLIMITED_VALUES;
219 }
220}
221
222/**
223 * Given a namespace mapping and a string containing a comma-separated
224 * list of namespace aliases, return a list of type URIs that
225 * correspond to those aliases.
226 *
227 * @param $namespace_map The mapping from namespace URI to alias
228 * @param $alias_list_s The string containing the comma-separated
229 * list of aliases. May also be None for convenience.
230 *
231 * @return $seq The list of namespace URIs that corresponds to the
232 * supplied list of aliases. If the string was zero-length or None, an
233 * empty list will be returned.
234 *
235 * return null If an alias is present in the list of aliases but
236 * is not present in the namespace map.
237 */
238function Auth_OpenID_AX_toTypeURIs($namespace_map, $alias_list_s)
239{
240 $uris = array();
241
242 if ($alias_list_s) {
243 foreach (explode(',', $alias_list_s) as $alias) {
244 $type_uri = $namespace_map->getNamespaceURI($alias);
245 if ($type_uri === null) {
246 // raise KeyError(
247 // 'No type is defined for attribute name %r' % (alias,))
248 return new Auth_OpenID_AX_Error(
249 sprintf('No type is defined for attribute name %s',
250 $alias)
251 );
252 } else {
253 $uris[] = $type_uri;
254 }
255 }
256 }
257
258 return $uris;
259}
260
261/**
262 * An attribute exchange 'fetch_request' message. This message is sent
263 * by a relying party when it wishes to obtain attributes about the
264 * subject of an OpenID authentication request.
265 *
266 * @package OpenID
267 */
268class Auth_OpenID_AX_FetchRequest extends Auth_OpenID_AX_Message {
269
270 var $mode = 'fetch_request';
271
272 function Auth_OpenID_AX_FetchRequest($update_url=null)
273 {
274 /**
275 * requested_attributes: The attributes that have been
276 * requested thus far, indexed by the type URI.
277 */
278 $this->requested_attributes = array();
279
280 /**
281 * update_url: A URL that will accept responses for this
282 * attribute exchange request, even in the absence of the user
283 * who made this request.
284 */
285 $this->update_url = $update_url;
286 }
287
288 /**
289 * Add an attribute to this attribute exchange request.
290 *
291 * @param attribute: The attribute that is being requested
292 * @return true on success, false when the requested attribute is
293 * already present in this fetch request.
294 */
295 function add($attribute)
296 {
297 if ($this->contains($attribute->type_uri)) {
298 return new Auth_OpenID_AX_Error(
299 sprintf("The attribute %s has already been requested",
300 $attribute->type_uri));
301 }
302
303 $this->requested_attributes[$attribute->type_uri] = $attribute;
304
305 return true;
306 }
307
308 /**
309 * Get the serialized form of this attribute fetch request.
310 *
311 * @returns Auth_OpenID_AX_FetchRequest The fetch request message parameters
312 */
313 function getExtensionArgs()
314 {
315 $aliases = new Auth_OpenID_NamespaceMap();
316
317 $required = array();
318 $if_available = array();
319
320 $ax_args = $this->_newArgs();
321
322 foreach ($this->requested_attributes as $type_uri => $attribute) {
323 if ($attribute->alias === null) {
324 $alias = $aliases->add($type_uri);
325 } else {
326 $alias = $aliases->addAlias($type_uri, $attribute->alias);
327
328 if ($alias === null) {
329 return new Auth_OpenID_AX_Error(
330 sprintf("Could not add alias %s for URI %s",
331 $attribute->alias, $type_uri
332 ));
333 }
334 }
335
336 if ($attribute->required) {
337 $required[] = $alias;
338 } else {
339 $if_available[] = $alias;
340 }
341
342 if ($attribute->count != 1) {
343 $ax_args['count.' . $alias] = strval($attribute->count);
344 }
345
346 $ax_args['type.' . $alias] = $type_uri;
347 }
348
349 if ($required) {
350 $ax_args['required'] = implode(',', $required);
351 }
352
353 if ($if_available) {
354 $ax_args['if_available'] = implode(',', $if_available);
355 }
356
357 return $ax_args;
358 }
359
360 /**
361 * Get the type URIs for all attributes that have been marked as
362 * required.
363 *
364 * @return A list of the type URIs for attributes that have been
365 * marked as required.
366 */
367 function getRequiredAttrs()
368 {
369 $required = array();
370 foreach ($this->requested_attributes as $type_uri => $attribute) {
371 if ($attribute->required) {
372 $required[] = $type_uri;
373 }
374 }
375
376 return $required;
377 }
378
379 /**
380 * Extract a FetchRequest from an OpenID message
381 *
382 * @param request: The OpenID request containing the attribute
383 * fetch request
384 *
385 * @returns mixed An Auth_OpenID_AX_Error or the
386 * Auth_OpenID_AX_FetchRequest extracted from the request message if
387 * successful
388 */
389 static function fromOpenIDRequest($request)
390 {
391 $m = $request->message;
392 $obj = new Auth_OpenID_AX_FetchRequest();
393 $ax_args = $m->getArgs($obj->ns_uri);
394
395 $result = $obj->parseExtensionArgs($ax_args);
396
397 if (Auth_OpenID_AX::isError($result)) {
398 return $result;
399 }
400
401 if ($obj->update_url) {
402 // Update URL must match the openid.realm of the
403 // underlying OpenID 2 message.
404 $realm = $m->getArg(Auth_OpenID_OPENID_NS, 'realm',
405 $m->getArg(
406 Auth_OpenID_OPENID_NS,
407 'return_to'));
408
409 if (!$realm) {
410 $obj = new Auth_OpenID_AX_Error(
411 sprintf("Cannot validate update_url %s " .
412 "against absent realm", $obj->update_url));
413 } else if (!Auth_OpenID_TrustRoot::match($realm,
414 $obj->update_url)) {
415 $obj = new Auth_OpenID_AX_Error(
416 sprintf("Update URL %s failed validation against realm %s",
417 $obj->update_url, $realm));
418 }
419 }
420
421 return $obj;
422 }
423
424 /**
425 * Given attribute exchange arguments, populate this FetchRequest.
426 *
427 * @return $result Auth_OpenID_AX_Error if the data to be parsed
428 * does not follow the attribute exchange specification. At least
429 * when 'if_available' or 'required' is not specified for a
430 * particular attribute type. Returns true otherwise.
431 */
432 function parseExtensionArgs($ax_args)
433 {
434 $result = $this->_checkMode($ax_args);
435 if (Auth_OpenID_AX::isError($result)) {
436 return $result;
437 }
438
439 $aliases = new Auth_OpenID_NamespaceMap();
440
441 foreach ($ax_args as $key => $value) {
442 if (strpos($key, 'type.') === 0) {
443 $alias = substr($key, 5);
444 $type_uri = $value;
445
446 $alias = $aliases->addAlias($type_uri, $alias);
447
448 if ($alias === null) {
449 return new Auth_OpenID_AX_Error(
450 sprintf("Could not add alias %s for URI %s",
451 $alias, $type_uri)
452 );
453 }
454
455 $count_s = Auth_OpenID::arrayGet($ax_args, 'count.' . $alias);
456 if ($count_s) {
457 $count = Auth_OpenID::intval($count_s);
458 if (($count === false) &&
459 ($count_s === Auth_OpenID_AX_UNLIMITED_VALUES)) {
460 $count = $count_s;
461 }
462 } else {
463 $count = 1;
464 }
465
466 if ($count === false) {
467 return new Auth_OpenID_AX_Error(
468 sprintf("Integer value expected for %s, got %s",
469 'count.' . $alias, $count_s));
470 }
471
472 $attrinfo = Auth_OpenID_AX_AttrInfo::make($type_uri, $count,
473 false, $alias);
474
475 if (Auth_OpenID_AX::isError($attrinfo)) {
476 return $attrinfo;
477 }
478
479 $this->add($attrinfo);
480 }
481 }
482
483 $required = Auth_OpenID_AX_toTypeURIs($aliases,
484 Auth_OpenID::arrayGet($ax_args, 'required'));
485
486 foreach ($required as $type_uri) {
487 $attrib = $this->requested_attributes[$type_uri];
488 $attrib->required = true;
489 }
490
491 $if_available = Auth_OpenID_AX_toTypeURIs($aliases,
492 Auth_OpenID::arrayGet($ax_args, 'if_available'));
493
494 $all_type_uris = array_merge($required, $if_available);
495
496 foreach ($aliases->iterNamespaceURIs() as $type_uri) {
497 if (!in_array($type_uri, $all_type_uris)) {
498 return new Auth_OpenID_AX_Error(
499 sprintf('Type URI %s was in the request but not ' .
500 'present in "required" or "if_available"',
501 $type_uri));
502
503 }
504 }
505
506 $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url');
507
508 return true;
509 }
510
511 /**
512 * Iterate over the AttrInfo objects that are contained in this
513 * fetch_request.
514 */
515 function iterAttrs()
516 {
517 return array_values($this->requested_attributes);
518 }
519
520 function iterTypes()
521 {
522 return array_keys($this->requested_attributes);
523 }
524
525 /**
526 * Is the given type URI present in this fetch_request?
527 */
528 function contains($type_uri)
529 {
530 return in_array($type_uri, $this->iterTypes());
531 }
532}
533
534/**
535 * An abstract class that implements a message that has attribute keys
536 * and values. It contains the common code between fetch_response and
537 * store_request.
538 *
539 * @package OpenID
540 */
541class Auth_OpenID_AX_KeyValueMessage extends Auth_OpenID_AX_Message {
542
543 function Auth_OpenID_AX_KeyValueMessage()
544 {
545 $this->data = array();
546 }
547
548 /**
549 * Add a single value for the given attribute type to the
550 * message. If there are already values specified for this type,
551 * this value will be sent in addition to the values already
552 * specified.
553 *
554 * @param type_uri: The URI for the attribute
555 * @param value: The value to add to the response to the relying
556 * party for this attribute
557 * @return null
558 */
559 function addValue($type_uri, $value)
560 {
561 if (!array_key_exists($type_uri, $this->data)) {
562 $this->data[$type_uri] = array();
563 }
564
565 $values =& $this->data[$type_uri];
566 $values[] = $value;
567 }
568
569 /**
570 * Set the values for the given attribute type. This replaces any
571 * values that have already been set for this attribute.
572 *
573 * @param type_uri: The URI for the attribute
574 * @param values: A list of values to send for this attribute.
575 */
576 function setValues($type_uri, &$values)
577 {
578 $this->data[$type_uri] =& $values;
579 }
580
581 /**
582 * Get the extension arguments for the key/value pairs contained
583 * in this message.
584 *
585 * @param aliases: An alias mapping. Set to None if you don't care
586 * about the aliases for this request.
587 *
588 * @access private
589 */
590 function _getExtensionKVArgs($aliases)
591 {
592 if ($aliases === null) {
593 $aliases = new Auth_OpenID_NamespaceMap();
594 }
595
596 $ax_args = array();
597
598 foreach ($this->data as $type_uri => $values) {
599 $alias = $aliases->add($type_uri);
600
601 $ax_args['type.' . $alias] = $type_uri;
602 $ax_args['count.' . $alias] = strval(count($values));
603
604 foreach ($values as $i => $value) {
605 $key = sprintf('value.%s.%d', $alias, $i + 1);
606 $ax_args[$key] = $value;
607 }
608 }
609
610 return $ax_args;
611 }
612
613 /**
614 * Parse attribute exchange key/value arguments into this object.
615 *
616 * @param ax_args: The attribute exchange fetch_response
617 * arguments, with namespacing removed.
618 *
619 * @return Auth_OpenID_AX_Error or true
620 */
621 function parseExtensionArgs($ax_args)
622 {
623 $result = $this->_checkMode($ax_args);
624 if (Auth_OpenID_AX::isError($result)) {
625 return $result;
626 }
627
628 $aliases = new Auth_OpenID_NamespaceMap();
629
630 foreach ($ax_args as $key => $value) {
631 if (strpos($key, 'type.') === 0) {
632 $type_uri = $value;
633 $alias = substr($key, 5);
634
635 $result = Auth_OpenID_AX_checkAlias($alias);
636
637 if (Auth_OpenID_AX::isError($result)) {
638 return $result;
639 }
640
641 $alias = $aliases->addAlias($type_uri, $alias);
642
643 if ($alias === null) {
644 return new Auth_OpenID_AX_Error(
645 sprintf("Could not add alias %s for URI %s",
646 $alias, $type_uri)
647 );
648 }
649 }
650 }
651
652 foreach ($aliases->iteritems() as $pair) {
653 list($type_uri, $alias) = $pair;
654
655 if (array_key_exists('count.' . $alias, $ax_args) && ($ax_args['count.' . $alias] !== Auth_OpenID_AX_UNLIMITED_VALUES)) {
656
657 $count_key = 'count.' . $alias;
658 $count_s = $ax_args[$count_key];
659
660 $count = Auth_OpenID::intval($count_s);
661
662 if ($count === false) {
663 return new Auth_OpenID_AX_Error(
664 sprintf("Integer value expected for %s, got %s",
665 'count. %s' . $alias, $count_s,
666 Auth_OpenID_AX_UNLIMITED_VALUES)
667 );
668 }
669
670 $values = array();
671 for ($i = 1; $i < $count + 1; $i++) {
672 $value_key = sprintf('value.%s.%d', $alias, $i);
673
674 if (!array_key_exists($value_key, $ax_args)) {
675 return new Auth_OpenID_AX_Error(
676 sprintf(
677 "No value found for key %s",
678 $value_key));
679 }
680
681 $value = $ax_args[$value_key];
682 $values[] = $value;
683 }
684 } else {
685 $key = 'value.' . $alias;
686
687 if (!array_key_exists($key, $ax_args)) {
688 return new Auth_OpenID_AX_Error(
689 sprintf(
690 "No value found for key %s",
691 $key));
692 }
693
694 $value = $ax_args['value.' . $alias];
695
696 if ($value == '') {
697 $values = array();
698 } else {
699 $values = array($value);
700 }
701 }
702
703 $this->data[$type_uri] = $values;
704 }
705
706 return true;
707 }
708
709 /**
710 * Get a single value for an attribute. If no value was sent for
711 * this attribute, use the supplied default. If there is more than
712 * one value for this attribute, this method will fail.
713 *
714 * @param type_uri: The URI for the attribute
715 * @param default: The value to return if the attribute was not
716 * sent in the fetch_response.
717 *
718 * @return $value Auth_OpenID_AX_Error on failure or the value of
719 * the attribute in the fetch_response message, or the default
720 * supplied
721 */
722 function getSingle($type_uri, $default=null)
723 {
724 $values = Auth_OpenID::arrayGet($this->data, $type_uri);
725 if (!$values) {
726 return $default;
727 } else if (count($values) == 1) {
728 return $values[0];
729 } else {
730 return new Auth_OpenID_AX_Error(
731 sprintf('More than one value present for %s',
732 $type_uri)
733 );
734 }
735 }
736
737 /**
738 * Get the list of values for this attribute in the
739 * fetch_response.
740 *
741 * XXX: what to do if the values are not present? default
742 * parameter? this is funny because it's always supposed to return
743 * a list, so the default may break that, though it's provided by
744 * the user's code, so it might be okay. If no default is
745 * supplied, should the return be None or []?
746 *
747 * @param type_uri: The URI of the attribute
748 *
749 * @return $values The list of values for this attribute in the
750 * response. May be an empty list. If the attribute was not sent
751 * in the response, returns Auth_OpenID_AX_Error.
752 */
753 function get($type_uri)
754 {
755 if (array_key_exists($type_uri, $this->data)) {
756 return $this->data[$type_uri];
757 } else {
758 return new Auth_OpenID_AX_Error(
759 sprintf("Type URI %s not found in response",
760 $type_uri)
761 );
762 }
763 }
764
765 /**
766 * Get the number of responses for a particular attribute in this
767 * fetch_response message.
768 *
769 * @param type_uri: The URI of the attribute
770 *
771 * @returns int The number of values sent for this attribute. If
772 * the attribute was not sent in the response, returns
773 * Auth_OpenID_AX_Error.
774 */
775 function count($type_uri)
776 {
777 if (array_key_exists($type_uri, $this->data)) {
778 return count($this->get($type_uri));
779 } else {
780 return new Auth_OpenID_AX_Error(
781 sprintf("Type URI %s not found in response",
782 $type_uri)
783 );
784 }
785 }
786}
787
788/**
789 * A fetch_response attribute exchange message.
790 *
791 * @package OpenID
792 */
793class Auth_OpenID_AX_FetchResponse extends Auth_OpenID_AX_KeyValueMessage {
794 var $mode = 'fetch_response';
795
796 function Auth_OpenID_AX_FetchResponse($update_url=null)
797 {
798 $this->Auth_OpenID_AX_KeyValueMessage();
799 $this->update_url = $update_url;
800 }
801
802 /**
803 * Serialize this object into arguments in the attribute exchange
804 * namespace
805 *
806 * @return $args The dictionary of unqualified attribute exchange
807 * arguments that represent this fetch_response, or
808 * Auth_OpenID_AX_Error on error.
809 */
810 function getExtensionArgs($request=null)
811 {
812 $aliases = new Auth_OpenID_NamespaceMap();
813
814 $zero_value_types = array();
815
816 if ($request !== null) {
817 // Validate the data in the context of the request (the
818 // same attributes should be present in each, and the
819 // counts in the response must be no more than the counts
820 // in the request)
821
822 foreach ($this->data as $type_uri => $unused) {
823 if (!$request->contains($type_uri)) {
824 return new Auth_OpenID_AX_Error(
825 sprintf("Response attribute not present in request: %s",
826 $type_uri)
827 );
828 }
829 }
830
831 foreach ($request->iterAttrs() as $attr_info) {
832 // Copy the aliases from the request so that reading
833 // the response in light of the request is easier
834 if ($attr_info->alias === null) {
835 $aliases->add($attr_info->type_uri);
836 } else {
837 $alias = $aliases->addAlias($attr_info->type_uri,
838 $attr_info->alias);
839
840 if ($alias === null) {
841 return new Auth_OpenID_AX_Error(
842 sprintf("Could not add alias %s for URI %s",
843 $attr_info->alias, $attr_info->type_uri)
844 );
845 }
846 }
847
848 if (array_key_exists($attr_info->type_uri, $this->data)) {
849 $values = $this->data[$attr_info->type_uri];
850 } else {
851 $values = array();
852 $zero_value_types[] = $attr_info;
853 }
854
855 if (($attr_info->count != Auth_OpenID_AX_UNLIMITED_VALUES) &&
856 ($attr_info->count < count($values))) {
857 return new Auth_OpenID_AX_Error(
858 sprintf("More than the number of requested values " .
859 "were specified for %s",
860 $attr_info->type_uri)
861 );
862 }
863 }
864 }
865
866 $kv_args = $this->_getExtensionKVArgs($aliases);
867
868 // Add the KV args into the response with the args that are
869 // unique to the fetch_response
870 $ax_args = $this->_newArgs();
871
872 // For each requested attribute, put its type/alias and count
873 // into the response even if no data were returned.
874 foreach ($zero_value_types as $attr_info) {
875 $alias = $aliases->getAlias($attr_info->type_uri);
876 $kv_args['type.' . $alias] = $attr_info->type_uri;
877 $kv_args['count.' . $alias] = '0';
878 }
879
880 $update_url = null;
881 if ($request) {
882 $update_url = $request->update_url;
883 } else {
884 $update_url = $this->update_url;
885 }
886
887 if ($update_url) {
888 $ax_args['update_url'] = $update_url;
889 }
890
891 Auth_OpenID::update($ax_args, $kv_args);
892
893 return $ax_args;
894 }
895
896 /**
897 * @return $result Auth_OpenID_AX_Error on failure or true on
898 * success.
899 */
900 function parseExtensionArgs($ax_args)
901 {
902 $result = parent::parseExtensionArgs($ax_args);
903
904 if (Auth_OpenID_AX::isError($result)) {
905 return $result;
906 }
907
908 $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url');
909
910 return true;
911 }
912
913 /**
914 * Construct a FetchResponse object from an OpenID library
915 * SuccessResponse object.
916 *
917 * @param success_response: A successful id_res response object
918 *
919 * @param signed: Whether non-signed args should be processsed. If
920 * True (the default), only signed arguments will be processsed.
921 *
922 * @return $response A FetchResponse containing the data from the
923 * OpenID message
924 */
925 static function fromSuccessResponse($success_response, $signed=true)
926 {
927 $obj = new Auth_OpenID_AX_FetchResponse();
928 if ($signed) {
929 $ax_args = $success_response->getSignedNS($obj->ns_uri);
930 } else {
931 $ax_args = $success_response->message->getArgs($obj->ns_uri);
932 }
933 if ($ax_args === null || Auth_OpenID::isFailure($ax_args) ||
934 sizeof($ax_args) == 0) {
935 return null;
936 }
937
938 $result = $obj->parseExtensionArgs($ax_args);
939 if (Auth_OpenID_AX::isError($result)) {
940 #XXX log me
941 return null;
942 }
943 return $obj;
944 }
945}
946
947/**
948 * A store request attribute exchange message representation.
949 *
950 * @package OpenID
951 */
952class Auth_OpenID_AX_StoreRequest extends Auth_OpenID_AX_KeyValueMessage {
953 var $mode = 'store_request';
954
955 /**
956 * @param array $aliases The namespace aliases to use when making
957 * this store response. Leave as None to use defaults.
958 */
959 function getExtensionArgs($aliases=null)
960 {
961 $ax_args = $this->_newArgs();
962 $kv_args = $this->_getExtensionKVArgs($aliases);
963 Auth_OpenID::update($ax_args, $kv_args);
964 return $ax_args;
965 }
966}
967
968/**
969 * An indication that the store request was processed along with this
970 * OpenID transaction. Use make(), NOT the constructor, to create
971 * response objects.
972 *
973 * @package OpenID
974 */
975class Auth_OpenID_AX_StoreResponse extends Auth_OpenID_AX_Message {
976 var $SUCCESS_MODE = 'store_response_success';
977 var $FAILURE_MODE = 'store_response_failure';
978
979 /**
980 * Returns Auth_OpenID_AX_Error on error or an
981 * Auth_OpenID_AX_StoreResponse object on success.
982 */
983 function make($succeeded=true, $error_message=null)
984 {
985 if (($succeeded) && ($error_message !== null)) {
986 return new Auth_OpenID_AX_Error('An error message may only be '.
987 'included in a failing fetch response');
988 }
989
990 return new Auth_OpenID_AX_StoreResponse($succeeded, $error_message);
991 }
992
993 function Auth_OpenID_AX_StoreResponse($succeeded=true, $error_message=null)
994 {
995 if ($succeeded) {
996 $this->mode = $this->SUCCESS_MODE;
997 } else {
998 $this->mode = $this->FAILURE_MODE;
999 }
1000
1001 $this->error_message = $error_message;
1002 }
1003
1004 /**
1005 * Was this response a success response?
1006 */
1007 function succeeded()
1008 {
1009 return $this->mode == $this->SUCCESS_MODE;
1010 }
1011
1012 function getExtensionArgs()
1013 {
1014 $ax_args = $this->_newArgs();
1015 if ((!$this->succeeded()) && $this->error_message) {
1016 $ax_args['error'] = $this->error_message;
1017 }
1018
1019 return $ax_args;
1020 }
1021}
1022
01023
=== added file 'Auth/OpenID/Association.php'
--- Auth/OpenID/Association.php 1970-01-01 00:00:00 +0000
+++ Auth/OpenID/Association.php 2011-06-19 04:48:34 +0000
@@ -0,0 +1,610 @@
1<?php
2
3/**
4 * This module contains code for dealing with associations between
5 * consumers and servers.
6 *
7 * PHP versions 4 and 5
8 *
9 * LICENSE: See the COPYING file included in this distribution.
10 *
11 * @package OpenID
12 * @author JanRain, Inc. <openid@janrain.com>
13 * @copyright 2005-2008 Janrain, Inc.
14 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
15 */
16
17/**
18 * @access private
19 */
20require_once 'Auth/OpenID/CryptUtil.php';
21
22/**
23 * @access private
24 */
25require_once 'Auth/OpenID/KVForm.php';
26
27/**
28 * @access private
29 */
30require_once 'Auth/OpenID/HMAC.php';
31
32/**
33 * This class represents an association between a server and a
34 * consumer. In general, users of this library will never see
35 * instances of this object. The only exception is if you implement a
36 * custom {@link Auth_OpenID_OpenIDStore}.
37 *
38 * If you do implement such a store, it will need to store the values
39 * of the handle, secret, issued, lifetime, and assoc_type instance
40 * variables.
41 *
42 * @package OpenID
43 */
44class Auth_OpenID_Association {
45
46 /**
47 * This is a HMAC-SHA1 specific value.
48 *
49 * @access private
50 */
51 var $SIG_LENGTH = 20;
52
53 /**
54 * The ordering and name of keys as stored by serialize.
55 *
56 * @access private
57 */
58 var $assoc_keys = array(
59 'version',
60 'handle',
61 'secret',
62 'issued',
63 'lifetime',
64 'assoc_type'
65 );
66
67 var $_macs = array(
68 'HMAC-SHA1' => 'Auth_OpenID_HMACSHA1',
69 'HMAC-SHA256' => 'Auth_OpenID_HMACSHA256'
70 );
71
72 /**
73 * This is an alternate constructor (factory method) used by the
74 * OpenID consumer library to create associations. OpenID store
75 * implementations shouldn't use this constructor.
76 *
77 * @access private
78 *
79 * @param integer $expires_in This is the amount of time this
80 * association is good for, measured in seconds since the
81 * association was issued.
82 *
83 * @param string $handle This is the handle the server gave this
84 * association.
85 *
86 * @param string secret This is the shared secret the server
87 * generated for this association.
88 *
89 * @param assoc_type This is the type of association this
90 * instance represents. The only valid values of this field at
91 * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
92 * be defined in the future.
93 *
94 * @return association An {@link Auth_OpenID_Association}
95 * instance.
96 */
97 static function fromExpiresIn($expires_in, $handle, $secret, $assoc_type)
98 {
99 $issued = time();
100 $lifetime = $expires_in;
101 return new Auth_OpenID_Association($handle, $secret,
102 $issued, $lifetime, $assoc_type);
103 }
104
105 /**
106 * This is the standard constructor for creating an association.
107 * The library should create all of the necessary associations, so
108 * this constructor is not part of the external API.
109 *
110 * @access private
111 *
112 * @param string $handle This is the handle the server gave this
113 * association.
114 *
115 * @param string $secret This is the shared secret the server
116 * generated for this association.
117 *
118 * @param integer $issued This is the time this association was
119 * issued, in seconds since 00:00 GMT, January 1, 1970. (ie, a
120 * unix timestamp)
121 *
122 * @param integer $lifetime This is the amount of time this
123 * association is good for, measured in seconds since the
124 * association was issued.
125 *
126 * @param string $assoc_type This is the type of association this
127 * instance represents. The only valid values of this field at
128 * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
129 * be defined in the future.
130 */
131 function Auth_OpenID_Association(
132 $handle, $secret, $issued, $lifetime, $assoc_type)
133 {
134 if (!in_array($assoc_type,
135 Auth_OpenID_getSupportedAssociationTypes(), true)) {
136 $fmt = 'Unsupported association type (%s)';
137 trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR);
138 }
139
140 $this->handle = $handle;
141 $this->secret = $secret;
142 $this->issued = $issued;
143 $this->lifetime = $lifetime;
144 $this->assoc_type = $assoc_type;
145 }
146
147 /**
148 * This returns the number of seconds this association is still
149 * valid for, or 0 if the association is no longer valid.
150 *
151 * @return integer $seconds The number of seconds this association
152 * is still valid for, or 0 if the association is no longer valid.
153 */
154 function getExpiresIn($now = null)
155 {
156 if ($now == null) {
157 $now = time();
158 }
159
160 return max(0, $this->issued + $this->lifetime - $now);
161 }
162
163 /**
164 * This checks to see if two {@link Auth_OpenID_Association}
165 * instances represent the same association.
166 *
167 * @return bool $result true if the two instances represent the
168 * same association, false otherwise.
169 */
170 function equal($other)
171 {
172 return ((gettype($this) == gettype($other))
173 && ($this->handle == $other->handle)
174 && ($this->secret == $other->secret)
175 && ($this->issued == $other->issued)
176 && ($this->lifetime == $other->lifetime)
177 && ($this->assoc_type == $other->assoc_type));
178 }
179
180 /**
181 * Convert an association to KV form.
182 *
183 * @return string $result String in KV form suitable for
184 * deserialization by deserialize.
185 */
186 function serialize()
187 {
188 $data = array(
189 'version' => '2',
190 'handle' => $this->handle,
191 'secret' => base64_encode($this->secret),
192 'issued' => strval(intval($this->issued)),
193 'lifetime' => strval(intval($this->lifetime)),
194 'assoc_type' => $this->assoc_type
195 );
196
197 assert(array_keys($data) == $this->assoc_keys);
198
199 return Auth_OpenID_KVForm::fromArray($data, $strict = true);
200 }
201
202 /**
203 * Parse an association as stored by serialize(). This is the
204 * inverse of serialize.
205 *
206 * @param string $assoc_s Association as serialized by serialize()
207 * @return Auth_OpenID_Association $result instance of this class
208 */
209 static function deserialize($class_name, $assoc_s)
210 {
211 $pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true);
212 $keys = array();
213 $values = array();
214 foreach ($pairs as $key => $value) {
215 if (is_array($value)) {
216 list($key, $value) = $value;
217 }
218 $keys[] = $key;
219 $values[] = $value;
220 }
221
222 $class_vars = get_class_vars($class_name);
223 $class_assoc_keys = $class_vars['assoc_keys'];
224
225 sort($keys);
226 sort($class_assoc_keys);
227
228 if ($keys != $class_assoc_keys) {
229 trigger_error('Unexpected key values: ' . var_export($keys, true),
230 E_USER_WARNING);
231 return null;
232 }
233
234 $version = $pairs['version'];
235 $handle = $pairs['handle'];
236 $secret = $pairs['secret'];
237 $issued = $pairs['issued'];
238 $lifetime = $pairs['lifetime'];
239 $assoc_type = $pairs['assoc_type'];
240
241 if ($version != '2') {
242 trigger_error('Unknown version: ' . $version, E_USER_WARNING);
243 return null;
244 }
245
246 $issued = intval($issued);
247 $lifetime = intval($lifetime);
248 $secret = base64_decode($secret);
249
250 return new $class_name(
251 $handle, $secret, $issued, $lifetime, $assoc_type);
252 }
253
254 /**
255 * Generate a signature for a sequence of (key, value) pairs
256 *
257 * @access private
258 * @param array $pairs The pairs to sign, in order. This is an
259 * array of two-tuples.
260 * @return string $signature The binary signature of this sequence
261 * of pairs
262 */
263 function sign($pairs)
264 {
265 $kv = Auth_OpenID_KVForm::fromArray($pairs);
266
267 /* Invalid association types should be caught at constructor */
268 $callback = $this->_macs[$this->assoc_type];
269
270 return call_user_func_array($callback, array($this->secret, $kv));
271 }
272
273 /**
274 * Generate a signature for some fields in a dictionary
275 *
276 * @access private
277 * @param array $fields The fields to sign, in order; this is an
278 * array of strings.
279 * @param array $data Dictionary of values to sign (an array of
280 * string => string pairs).
281 * @return string $signature The signature, base64 encoded
282 */
283 function signMessage($message)
284 {
285 if ($message->hasKey(Auth_OpenID_OPENID_NS, 'sig') ||
286 $message->hasKey(Auth_OpenID_OPENID_NS, 'signed')) {
287 // Already has a sig
288 return null;
289 }
290
291 $extant_handle = $message->getArg(Auth_OpenID_OPENID_NS,
292 'assoc_handle');
293
294 if ($extant_handle && ($extant_handle != $this->handle)) {
295 // raise ValueError("Message has a different association handle")
296 return null;
297 }
298
299 $signed_message = $message;
300 $signed_message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
301 $this->handle);
302
303 $message_keys = array_keys($signed_message->toPostArgs());
304 $signed_list = array();
305 $signed_prefix = 'openid.';
306
307 foreach ($message_keys as $k) {
308 if (strpos($k, $signed_prefix) === 0) {
309 $signed_list[] = substr($k, strlen($signed_prefix));
310 }
311 }
312
313 $signed_list[] = 'signed';
314 sort($signed_list);
315
316 $signed_message->setArg(Auth_OpenID_OPENID_NS, 'signed',
317 implode(',', $signed_list));
318 $sig = $this->getMessageSignature($signed_message);
319 $signed_message->setArg(Auth_OpenID_OPENID_NS, 'sig', $sig);
320 return $signed_message;
321 }
322
323 /**
324 * Given a {@link Auth_OpenID_Message}, return the key/value pairs
325 * to be signed according to the signed list in the message. If
326 * the message lacks a signed list, return null.
327 *
328 * @access private
329 */
330 function _makePairs($message)
331 {
332 $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
333 if (!$signed || Auth_OpenID::isFailure($signed)) {
334 // raise ValueError('Message has no signed list: %s' % (message,))
335 return null;
336 }
337
338 $signed_list = explode(',', $signed);
339 $pairs = array();
340 $data = $message->toPostArgs();
341 foreach ($signed_list as $field) {
342 $pairs[] = array($field, Auth_OpenID::arrayGet($data,
343 'openid.' .
344 $field, ''));
345 }
346 return $pairs;
347 }
348
349 /**
350 * Given an {@link Auth_OpenID_Message}, return the signature for
351 * the signed list in the message.
352 *
353 * @access private
354 */
355 function getMessageSignature($message)
356 {
357 $pairs = $this->_makePairs($message);
358 return base64_encode($this->sign($pairs));
359 }
360
361 /**
362 * Confirm that the signature of these fields matches the
363 * signature contained in the data.
364 *
365 * @access private
366 */
367 function checkMessageSignature($message)
368 {
369 $sig = $message->getArg(Auth_OpenID_OPENID_NS,
370 'sig');
371
372 if (!$sig || Auth_OpenID::isFailure($sig)) {
373 return false;
374 }
375
376 $calculated_sig = $this->getMessageSignature($message);
377 return $calculated_sig == $sig;
378 }
379}
380
381function Auth_OpenID_getSecretSize($assoc_type)
382{
383 if ($assoc_type == 'HMAC-SHA1') {
384 return 20;
385 } else if ($assoc_type == 'HMAC-SHA256') {
386 return 32;
387 } else {
388 return null;
389 }
390}
391
392function Auth_OpenID_getAllAssociationTypes()
393{
394 return array('HMAC-SHA1', 'HMAC-SHA256');
395}
396
397function Auth_OpenID_getSupportedAssociationTypes()
398{
399 $a = array('HMAC-SHA1');
400
401 if (Auth_OpenID_HMACSHA256_SUPPORTED) {
402 $a[] = 'HMAC-SHA256';
403 }
404
405 return $a;
406}
407
408function Auth_OpenID_getSessionTypes($assoc_type)
409{
410 $assoc_to_session = array(
411 'HMAC-SHA1' => array('DH-SHA1', 'no-encryption'));
412
413 if (Auth_OpenID_HMACSHA256_SUPPORTED) {
414 $assoc_to_session['HMAC-SHA256'] =
415 array('DH-SHA256', 'no-encryption');
416 }
417
418 return Auth_OpenID::arrayGet($assoc_to_session, $assoc_type, array());
419}
420
421function Auth_OpenID_checkSessionType($assoc_type, $session_type)
422{
423 if (!in_array($session_type,
424 Auth_OpenID_getSessionTypes($assoc_type))) {
425 return false;
426 }
427
428 return true;
429}
430
431function Auth_OpenID_getDefaultAssociationOrder()
432{
433 $order = array();
434
435 if (!Auth_OpenID_noMathSupport()) {
436 $order[] = array('HMAC-SHA1', 'DH-SHA1');
437
438 if (Auth_OpenID_HMACSHA256_SUPPORTED) {
439 $order[] = array('HMAC-SHA256', 'DH-SHA256');
440 }
441 }
442
443 $order[] = array('HMAC-SHA1', 'no-encryption');
444
445 if (Auth_OpenID_HMACSHA256_SUPPORTED) {
446 $order[] = array('HMAC-SHA256', 'no-encryption');
447 }
448
449 return $order;
450}
451
452function Auth_OpenID_getOnlyEncryptedOrder()
453{
454 $result = array();
455
456 foreach (Auth_OpenID_getDefaultAssociationOrder() as $pair) {
457 list($assoc, $session) = $pair;
458
459 if ($session != 'no-encryption') {
460 if (Auth_OpenID_HMACSHA256_SUPPORTED &&
461 ($assoc == 'HMAC-SHA256')) {
462 $result[] = $pair;
463 } else if ($assoc != 'HMAC-SHA256') {
464 $result[] = $pair;
465 }
466 }
467 }
468
469 return $result;
470}
471
472function Auth_OpenID_getDefaultNegotiator()
473{
474 return new Auth_OpenID_SessionNegotiator(
475 Auth_OpenID_getDefaultAssociationOrder());
476}
477
478function Auth_OpenID_getEncryptedNegotiator()
479{
480 return new Auth_OpenID_SessionNegotiator(
481 Auth_OpenID_getOnlyEncryptedOrder());
482}
483
484/**
485 * A session negotiator controls the allowed and preferred association
486 * types and association session types. Both the {@link
487 * Auth_OpenID_Consumer} and {@link Auth_OpenID_Server} use
488 * negotiators when creating associations.
489 *
490 * You can create and use negotiators if you:
491
492 * - Do not want to do Diffie-Hellman key exchange because you use
493 * transport-layer encryption (e.g. SSL)
494 *
495 * - Want to use only SHA-256 associations
496 *
497 * - Do not want to support plain-text associations over a non-secure
498 * channel
499 *
500 * It is up to you to set a policy for what kinds of associations to
501 * accept. By default, the library will make any kind of association
502 * that is allowed in the OpenID 2.0 specification.
503 *
504 * Use of negotiators in the library
505 * =================================
506 *
507 * When a consumer makes an association request, it calls {@link
508 * getAllowedType} to get the preferred association type and
509 * association session type.
510 *
511 * The server gets a request for a particular association/session type
512 * and calls {@link isAllowed} to determine if it should create an
513 * association. If it is supported, negotiation is complete. If it is
514 * not, the server calls {@link getAllowedType} to get an allowed
515 * association type to return to the consumer.
516 *
517 * If the consumer gets an error response indicating that the
518 * requested association/session type is not supported by the server
519 * that contains an assocation/session type to try, it calls {@link
520 * isAllowed} to determine if it should try again with the given
521 * combination of association/session type.
522 *
523 * @package OpenID
524 */
525class Auth_OpenID_SessionNegotiator {
526 function Auth_OpenID_SessionNegotiator($allowed_types)
527 {
528 $this->allowed_types = array();
529 $this->setAllowedTypes($allowed_types);
530 }
531
532 /**
533 * Set the allowed association types, checking to make sure each
534 * combination is valid.
535 *
536 * @access private
537 */
538 function setAllowedTypes($allowed_types)
539 {
540 foreach ($allowed_types as $pair) {
541 list($assoc_type, $session_type) = $pair;
542 if (!Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
543 return false;
544 }
545 }
546
547 $this->allowed_types = $allowed_types;
548 return true;
549 }
550
551 /**
552 * Add an association type and session type to the allowed types
553 * list. The assocation/session pairs are tried in the order that
554 * they are added.
555 *
556 * @access private
557 */
558 function addAllowedType($assoc_type, $session_type = null)
559 {
560 if ($this->allowed_types === null) {
561 $this->allowed_types = array();
562 }
563
564 if ($session_type === null) {
565 $available = Auth_OpenID_getSessionTypes($assoc_type);
566
567 if (!$available) {
568 return false;
569 }
570
571 foreach ($available as $session_type) {
572 $this->addAllowedType($assoc_type, $session_type);
573 }
574 } else {
575 if (Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
576 $this->allowed_types[] = array($assoc_type, $session_type);
577 } else {
578 return false;
579 }
580 }
581
582 return true;
583 }
584
585 // Is this combination of association type and session type allowed?
586 function isAllowed($assoc_type, $session_type)
587 {
588 $assoc_good = in_array(array($assoc_type, $session_type),
589 $this->allowed_types);
590
591 $matches = in_array($session_type,
592 Auth_OpenID_getSessionTypes($assoc_type));
593
594 return ($assoc_good && $matches);
595 }
596
597 /**
598 * Get a pair of assocation type and session type that are
599 * supported.
600 */
601 function getAllowedType()
602 {
603 if (!$this->allowed_types) {
604 return array(null, null);
605 }
606
607 return $this->allowed_types[0];
608 }
609}
610
0611
=== added file 'Auth/OpenID/BigMath.php'
--- Auth/OpenID/BigMath.php 1970-01-01 00:00:00 +0000
+++ Auth/OpenID/BigMath.php 2011-06-19 04:48:34 +0000
@@ -0,0 +1,452 @@
1<?php
2
3/**
4 * BigMath: A math library wrapper that abstracts out the underlying
5 * long integer library.
6 *
7 * PHP versions 4 and 5
8 *
9 * LICENSE: See the COPYING file included in this distribution.
10 *
11 * @access private
12 * @package OpenID
13 * @author JanRain, Inc. <openid@janrain.com>
14 * @copyright 2005-2008 Janrain, Inc.
15 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
16 */
17
18/**
19 * Needed for random number generation
20 */
21require_once 'Auth/OpenID/CryptUtil.php';
22
23/**
24 * Need Auth_OpenID::bytes().
25 */
26require_once 'Auth/OpenID.php';
27
28/**
29 * The superclass of all big-integer math implementations
30 * @access private
31 * @package OpenID
32 */
33class Auth_OpenID_MathLibrary {
34 /**
35 * Given a long integer, returns the number converted to a binary
36 * string. This function accepts long integer values of arbitrary
37 * magnitude and uses the local large-number math library when
38 * available.
39 *
40 * @param integer $long The long number (can be a normal PHP
41 * integer or a number created by one of the available long number
42 * libraries)
43 * @return string $binary The binary version of $long
44 */
45 function longToBinary($long)
46 {
47 $cmp = $this->cmp($long, 0);
48 if ($cmp < 0) {
49 $msg = __FUNCTION__ . " takes only positive integers.";
50 trigger_error($msg, E_USER_ERROR);
51 return null;
52 }
53
54 if ($cmp == 0) {
55 return "\x00";
56 }
57
58 $bytes = array();
59
60 while ($this->cmp($long, 0) > 0) {
61 array_unshift($bytes, $this->mod($long, 256));
62 $long = $this->div($long, pow(2, 8));
63 }
64
65 if ($bytes && ($bytes[0] > 127)) {
66 array_unshift($bytes, 0);
67 }
68
69 $string = '';
70 foreach ($bytes as $byte) {
71 $string .= pack('C', $byte);
72 }
73
74 return $string;
75 }
76
77 /**
78 * Given a binary string, returns the binary string converted to a
79 * long number.
80 *
81 * @param string $binary The binary version of a long number,
82 * probably as a result of calling longToBinary
83 * @return integer $long The long number equivalent of the binary
84 * string $str
85 */
86 function binaryToLong($str)
87 {
88 if ($str === null) {
89 return null;
90 }
91
92 // Use array_merge to return a zero-indexed array instead of a
93 // one-indexed array.
94 $bytes = array_merge(unpack('C*', $str));
95
96 $n = $this->init(0);
97
98 if ($bytes && ($bytes[0] > 127)) {
99 trigger_error("bytesToNum works only for positive integers.",
100 E_USER_WARNING);
101 return null;
102 }
103
104 foreach ($bytes as $byte) {
105 $n = $this->mul($n, pow(2, 8));
106 $n = $this->add($n, $byte);
107 }
108
109 return $n;
110 }
111
112 function base64ToLong($str)
113 {
114 $b64 = base64_decode($str);
115
116 if ($b64 === false) {
117 return false;
118 }
119
120 return $this->binaryToLong($b64);
121 }
122
123 function longToBase64($str)
124 {
125 return base64_encode($this->longToBinary($str));
126 }
127
128 /**
129 * Returns a random number in the specified range. This function
130 * accepts $start, $stop, and $step values of arbitrary magnitude
131 * and will utilize the local large-number math library when
132 * available.
133 *
134 * @param integer $start The start of the range, or the minimum
135 * random number to return
136 * @param integer $stop The end of the range, or the maximum
137 * random number to return
138 * @param integer $step The step size, such that $result - ($step
139 * * N) = $start for some N
140 * @return integer $result The resulting randomly-generated number
141 */
142 function rand($stop)
143 {
144 static $duplicate_cache = array();
145
146 // Used as the key for the duplicate cache
147 $rbytes = $this->longToBinary($stop);
148
149 if (array_key_exists($rbytes, $duplicate_cache)) {
150 list($duplicate, $nbytes) = $duplicate_cache[$rbytes];
151 } else {
152 if ($rbytes[0] == "\x00") {
153 $nbytes = Auth_OpenID::bytes($rbytes) - 1;
154 } else {
155 $nbytes = Auth_OpenID::bytes($rbytes);
156 }
157
158 $mxrand = $this->pow(256, $nbytes);
159
160 // If we get a number less than this, then it is in the
161 // duplicated range.
162 $duplicate = $this->mod($mxrand, $stop);
163
164 if (count($duplicate_cache) > 10) {
165 $duplicate_cache = array();
166 }
167
168 $duplicate_cache[$rbytes] = array($duplicate, $nbytes);
169 }
170
171 do {
172 $bytes = "\x00" . Auth_OpenID_CryptUtil::getBytes($nbytes);
173 $n = $this->binaryToLong($bytes);
174 // Keep looping if this value is in the low duplicated range
175 } while ($this->cmp($n, $duplicate) < 0);
176
177 return $this->mod($n, $stop);
178 }
179}
180
181/**
182 * Exposes BCmath math library functionality.
183 *
184 * {@link Auth_OpenID_BcMathWrapper} wraps the functionality provided
185 * by the BCMath extension.
186 *
187 * @access private
188 * @package OpenID
189 */
190class Auth_OpenID_BcMathWrapper extends Auth_OpenID_MathLibrary{
191 var $type = 'bcmath';
192
193 function add($x, $y)
194 {
195 return bcadd($x, $y);
196 }
197
198 function sub($x, $y)
199 {
200 return bcsub($x, $y);
201 }
202
203 function pow($base, $exponent)
204 {
205 return bcpow($base, $exponent);
206 }
207
208 function cmp($x, $y)
209 {
210 return bccomp($x, $y);
211 }
212
213 function init($number, $base = 10)
214 {
215 return $number;
216 }
217
218 function mod($base, $modulus)
219 {
220 return bcmod($base, $modulus);
221 }
222
223 function mul($x, $y)
224 {
225 return bcmul($x, $y);
226 }
227
228 function div($x, $y)
229 {
230 return bcdiv($x, $y);
231 }
232
233 /**
234 * Same as bcpowmod when bcpowmod is missing
235 *
236 * @access private
237 */
238 function _powmod($base, $exponent, $modulus)
239 {
240 $square = $this->mod($base, $modulus);
241 $result = 1;
242 while($this->cmp($exponent, 0) > 0) {
243 if ($this->mod($exponent, 2)) {
244 $result = $this->mod($this->mul($result, $square), $modulus);
245 }
246 $square = $this->mod($this->mul($square, $square), $modulus);
247 $exponent = $this->div($exponent, 2);
248 }
249 return $result;
250 }
251
252 function powmod($base, $exponent, $modulus)
253 {
254 if (function_exists('bcpowmod')) {
255 return bcpowmod($base, $exponent, $modulus);
256 } else {
257 return $this->_powmod($base, $exponent, $modulus);
258 }
259 }
260
261 function toString($num)
262 {
263 return $num;
264 }
265}
266
267/**
268 * Exposes GMP math library functionality.
269 *
270 * {@link Auth_OpenID_GmpMathWrapper} wraps the functionality provided
271 * by the GMP extension.
272 *
273 * @access private
274 * @package OpenID
275 */
276class Auth_OpenID_GmpMathWrapper extends Auth_OpenID_MathLibrary{
277 var $type = 'gmp';
278
279 function add($x, $y)
280 {
281 return gmp_add($x, $y);
282 }
283
284 function sub($x, $y)
285 {
286 return gmp_sub($x, $y);
287 }
288
289 function pow($base, $exponent)
290 {
291 return gmp_pow($base, $exponent);
292 }
293
294 function cmp($x, $y)
295 {
296 return gmp_cmp($x, $y);
297 }
298
299 function init($number, $base = 10)
300 {
301 return gmp_init($number, $base);
302 }
303
304 function mod($base, $modulus)
305 {
306 return gmp_mod($base, $modulus);
307 }
308
309 function mul($x, $y)
310 {
311 return gmp_mul($x, $y);
312 }
313
314 function div($x, $y)
315 {
316 return gmp_div_q($x, $y);
317 }
318
319 function powmod($base, $exponent, $modulus)
320 {
321 return gmp_powm($base, $exponent, $modulus);
322 }
323
324 function toString($num)
325 {
326 return gmp_strval($num);
327 }
328}
329
330/**
331 * Define the supported extensions. An extension array has keys
332 * 'modules', 'extension', and 'class'. 'modules' is an array of PHP
333 * module names which the loading code will attempt to load. These
334 * values will be suffixed with a library file extension (e.g. ".so").
335 * 'extension' is the name of a PHP extension which will be tested
336 * before 'modules' are loaded. 'class' is the string name of a
337 * {@link Auth_OpenID_MathWrapper} subclass which should be
338 * instantiated if a given extension is present.
339 *
340 * You can define new math library implementations and add them to
341 * this array.
342 */
343function Auth_OpenID_math_extensions()
344{
345 $result = array();
346
347 if (!defined('Auth_OpenID_BUGGY_GMP')) {
348 $result[] =
349 array('modules' => array('gmp', 'php_gmp'),
350 'extension' => 'gmp',
351 'class' => 'Auth_OpenID_GmpMathWrapper');
352 }
353
354 $result[] = array('modules' => array('bcmath', 'php_bcmath'),
355 'extension' => 'bcmath',
356 'class' => 'Auth_OpenID_BcMathWrapper');
357
358 return $result;
359}
360
361/**
362 * Detect which (if any) math library is available
363 */
364function Auth_OpenID_detectMathLibrary($exts)
365{
366 $loaded = false;
367
368 $hasDl = function_exists('dl');
369 foreach ($exts as $extension) {
370 if (extension_loaded($extension['extension'])) {
371 return $extension;
372 }
373 }
374
375 return false;
376}
377
378/**
379 * {@link Auth_OpenID_getMathLib} checks for the presence of long
380 * number extension modules and returns an instance of
381 * {@link Auth_OpenID_MathWrapper} which exposes the module's
382 * functionality.
383 *
384 * Checks for the existence of an extension module described by the
385 * result of {@link Auth_OpenID_math_extensions()} and returns an
386 * instance of a wrapper for that extension module. If no extension
387 * module is found, an instance of {@link Auth_OpenID_MathWrapper} is
388 * returned, which wraps the native PHP integer implementation. The
389 * proper calling convention for this method is $lib =
390 * Auth_OpenID_getMathLib().
391 *
392 * This function checks for the existence of specific long number
393 * implementations in the following order: GMP followed by BCmath.
394 *
395 * @return Auth_OpenID_MathWrapper $instance An instance of
396 * {@link Auth_OpenID_MathWrapper} or one of its subclasses
397 *
398 * @package OpenID
399 */
400function Auth_OpenID_getMathLib()
401{
402 // The instance of Auth_OpenID_MathWrapper that we choose to
403 // supply will be stored here, so that subseqent calls to this
404 // method will return a reference to the same object.
405 static $lib = null;
406
407 if (isset($lib)) {
408 return $lib;
409 }
410
411 if (Auth_OpenID_noMathSupport()) {
412 $null = null;
413 return $null;
414 }
415
416 // If this method has not been called before, look at
417 // Auth_OpenID_math_extensions and try to find an extension that
418 // works.
419 $ext = Auth_OpenID_detectMathLibrary(Auth_OpenID_math_extensions());
420 if ($ext === false) {
421 $tried = array();
422 foreach (Auth_OpenID_math_extensions() as $extinfo) {
423 $tried[] = $extinfo['extension'];
424 }
425 $triedstr = implode(", ", $tried);
426
427 Auth_OpenID_setNoMathSupport();
428
429 $result = null;
430 return $result;
431 }
432
433 // Instantiate a new wrapper
434 $class = $ext['class'];
435 $lib = new $class();
436
437 return $lib;
438}
439
440function Auth_OpenID_setNoMathSupport()
441{
442 if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) {
443 define('Auth_OpenID_NO_MATH_SUPPORT', true);
444 }
445}
446
447function Auth_OpenID_noMathSupport()
448{
449 return defined('Auth_OpenID_NO_MATH_SUPPORT');
450}
451
452
0453
=== added file 'Auth/OpenID/Consumer.php'
--- Auth/OpenID/Consumer.php 1970-01-01 00:00:00 +0000
+++ Auth/OpenID/Consumer.php 2011-06-19 04:48:34 +0000
@@ -0,0 +1,2230 @@
1<?php
2
3/**
4 * This module documents the main interface with the OpenID consumer
5 * library. The only part of the library which has to be used and
6 * isn't documented in full here is the store required to create an
7 * Auth_OpenID_Consumer instance. More on the abstract store type and
8 * concrete implementations of it that are provided in the
9 * documentation for the Auth_OpenID_Consumer constructor.
10 *
11 * OVERVIEW
12 *
13 * The OpenID identity verification process most commonly uses the
14 * following steps, as visible to the user of this library:
15 *
16 * 1. The user enters their OpenID into a field on the consumer's
17 * site, and hits a login button.
18 * 2. The consumer site discovers the user's OpenID server using the
19 * YADIS protocol.
20 * 3. The consumer site sends the browser a redirect to the identity
21 * server. This is the authentication request as described in
22 * the OpenID specification.
23 * 4. The identity server's site sends the browser a redirect back
24 * to the consumer site. This redirect contains the server's
25 * response to the authentication request.
26 *
27 * The most important part of the flow to note is the consumer's site
28 * must handle two separate HTTP requests in order to perform the full
29 * identity check.
30 *
31 * LIBRARY DESIGN
32 *
33 * This consumer library is designed with that flow in mind. The goal
34 * is to make it as easy as possible to perform the above steps
35 * securely.
36 *
37 * At a high level, there are two important parts in the consumer
38 * library. The first important part is this module, which contains
39 * the interface to actually use this library. The second is the
40 * Auth_OpenID_Interface class, which describes the interface to use
41 * if you need to create a custom method for storing the state this
42 * library needs to maintain between requests.
43 *
44 * In general, the second part is less important for users of the
45 * library to know about, as several implementations are provided
46 * which cover a wide variety of situations in which consumers may use
47 * the library.
48 *
49 * This module contains a class, Auth_OpenID_Consumer, with methods
50 * corresponding to the actions necessary in each of steps 2, 3, and 4
51 * described in the overview. Use of this library should be as easy
52 * as creating an Auth_OpenID_Consumer instance and calling the
53 * methods appropriate for the action the site wants to take.
54 *
55 * STORES AND DUMB MODE
56 *
57 * OpenID is a protocol that works best when the consumer site is able
58 * to store some state. This is the normal mode of operation for the
59 * protocol, and is sometimes referred to as smart mode. There is
60 * also a fallback mode, known as dumb mode, which is available when
61 * the consumer site is not able to store state. This mode should be
62 * avoided when possible, as it leaves the implementation more
63 * vulnerable to replay attacks.
64 *
65 * The mode the library works in for normal operation is determined by
66 * the store that it is given. The store is an abstraction that
67 * handles the data that the consumer needs to manage between http
68 * requests in order to operate efficiently and securely.
69 *
70 * Several store implementation are provided, and the interface is
71 * fully documented so that custom stores can be used as well. See
72 * the documentation for the Auth_OpenID_Consumer class for more
73 * information on the interface for stores. The implementations that
74 * are provided allow the consumer site to store the necessary data in
75 * several different ways, including several SQL databases and normal
76 * files on disk.
77 *
78 * There is an additional concrete store provided that puts the system
79 * in dumb mode. This is not recommended, as it removes the library's
80 * ability to stop replay attacks reliably. It still uses time-based
81 * checking to make replay attacks only possible within a small
82 * window, but they remain possible within that window. This store
83 * should only be used if the consumer site has no way to retain data
84 * between requests at all.
85 *
86 * IMMEDIATE MODE
87 *
88 * In the flow described above, the user may need to confirm to the
89 * lidentity server that it's ok to authorize his or her identity.
90 * The server may draw pages asking for information from the user
91 * before it redirects the browser back to the consumer's site. This
92 * is generally transparent to the consumer site, so it is typically
93 * ignored as an implementation detail.
94 *
95 * There can be times, however, where the consumer site wants to get a
96 * response immediately. When this is the case, the consumer can put
97 * the library in immediate mode. In immediate mode, there is an
98 * extra response possible from the server, which is essentially the
99 * server reporting that it doesn't have enough information to answer
100 * the question yet.
101 *
102 * USING THIS LIBRARY
103 *
104 * Integrating this library into an application is usually a
105 * relatively straightforward process. The process should basically
106 * follow this plan:
107 *
108 * Add an OpenID login field somewhere on your site. When an OpenID
109 * is entered in that field and the form is submitted, it should make
110 * a request to the your site which includes that OpenID URL.
111 *
112 * First, the application should instantiate the Auth_OpenID_Consumer
113 * class using the store of choice (Auth_OpenID_FileStore or one of
114 * the SQL-based stores). If the application has a custom
115 * session-management implementation, an object implementing the
116 * {@link Auth_Yadis_PHPSession} interface should be passed as the
117 * second parameter. Otherwise, the default uses $_SESSION.
118 *
119 * Next, the application should call the Auth_OpenID_Consumer object's
120 * 'begin' method. This method takes the OpenID URL. The 'begin'
121 * method returns an Auth_OpenID_AuthRequest object.
122 *
123 * Next, the application should call the 'redirectURL' method of the
124 * Auth_OpenID_AuthRequest object. The 'return_to' URL parameter is
125 * the URL that the OpenID server will send the user back to after
126 * attempting to verify his or her identity. The 'trust_root' is the
127 * URL (or URL pattern) that identifies your web site to the user when
128 * he or she is authorizing it. Send a redirect to the resulting URL
129 * to the user's browser.
130 *
131 * That's the first half of the authentication process. The second
132 * half of the process is done after the user's ID server sends the
133 * user's browser a redirect back to your site to complete their
134 * login.
135 *
136 * When that happens, the user will contact your site at the URL given
137 * as the 'return_to' URL to the Auth_OpenID_AuthRequest::redirectURL
138 * call made above. The request will have several query parameters
139 * added to the URL by the identity server as the information
140 * necessary to finish the request.
141 *
142 * Lastly, instantiate an Auth_OpenID_Consumer instance as above and
143 * call its 'complete' method, passing in all the received query
144 * arguments.
145 *
146 * There are multiple possible return types possible from that
147 * method. These indicate the whether or not the login was successful,
148 * and include any additional information appropriate for their type.
149 *
150 * PHP versions 4 and 5
151 *
152 * LICENSE: See the COPYING file included in this distribution.
153 *
154 * @package OpenID
155 * @author JanRain, Inc. <openid@janrain.com>
156 * @copyright 2005-2008 Janrain, Inc.
157 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
158 */
159
160/**
161 * Require utility classes and functions for the consumer.
162 */
163require_once "Auth/OpenID.php";
164require_once "Auth/OpenID/Message.php";
165require_once "Auth/OpenID/HMAC.php";
166require_once "Auth/OpenID/Association.php";
167require_once "Auth/OpenID/CryptUtil.php";
168require_once "Auth/OpenID/DiffieHellman.php";
169require_once "Auth/OpenID/KVForm.php";
170require_once "Auth/OpenID/Nonce.php";
171require_once "Auth/OpenID/Discover.php";
172require_once "Auth/OpenID/URINorm.php";
173require_once "Auth/Yadis/Manager.php";
174require_once "Auth/Yadis/XRI.php";
175
176/**
177 * This is the status code returned when the complete method returns
178 * successfully.
179 */
180define('Auth_OpenID_SUCCESS', 'success');
181
182/**
183 * Status to indicate cancellation of OpenID authentication.
184 */
185define('Auth_OpenID_CANCEL', 'cancel');
186
187/**
188 * This is the status code completeAuth returns when the value it
189 * received indicated an invalid login.
190 */
191define('Auth_OpenID_FAILURE', 'failure');
192
193/**
194 * This is the status code completeAuth returns when the
195 * {@link Auth_OpenID_Consumer} instance is in immediate mode, and the
196 * identity server sends back a URL to send the user to to complete his
197 * or her login.
198 */
199define('Auth_OpenID_SETUP_NEEDED', 'setup needed');
200
201/**
202 * This is the status code beginAuth returns when the page fetched
203 * from the entered OpenID URL doesn't contain the necessary link tags
204 * to function as an identity page.
205 */
206define('Auth_OpenID_PARSE_ERROR', 'parse error');
207
208/**
209 * An OpenID consumer implementation that performs discovery and does
210 * session management. See the Consumer.php file documentation for
211 * more information.
212 *
213 * @package OpenID
214 */
215class Auth_OpenID_Consumer {
216
217 /**
218 * @access private
219 */
220 var $discoverMethod = 'Auth_OpenID_discover';
221
222 /**
223 * @access private
224 */
225 var $session_key_prefix = "_openid_consumer_";
226
227 /**
228 * @access private
229 */
230 var $_token_suffix = "last_token";
231
232 /**
233 * Initialize a Consumer instance.
234 *
235 * You should create a new instance of the Consumer object with
236 * every HTTP request that handles OpenID transactions.
237 *
238 * @param Auth_OpenID_OpenIDStore $store This must be an object
239 * that implements the interface in {@link
240 * Auth_OpenID_OpenIDStore}. Several concrete implementations are
241 * provided, to cover most common use cases. For stores backed by
242 * MySQL, PostgreSQL, or SQLite, see the {@link
243 * Auth_OpenID_SQLStore} class and its sublcasses. For a
244 * filesystem-backed store, see the {@link Auth_OpenID_FileStore}
245 * module. As a last resort, if it isn't possible for the server
246 * to store state at all, an instance of {@link
247 * Auth_OpenID_DumbStore} can be used.
248 *
249 * @param mixed $session An object which implements the interface
250 * of the {@link Auth_Yadis_PHPSession} class. Particularly, this
251 * object is expected to have these methods: get($key), set($key),
252 * $value), and del($key). This defaults to a session object
253 * which wraps PHP's native session machinery. You should only
254 * need to pass something here if you have your own sessioning
255 * implementation.
256 *
257 * @param str $consumer_cls The name of the class to instantiate
258 * when creating the internal consumer object. This is used for
259 * testing.
260 */
261 function Auth_OpenID_Consumer($store, $session = null,
262 $consumer_cls = null)
263 {
264 if ($session === null) {
265 $session = new Auth_Yadis_PHPSession();
266 }
267
268 $this->session = $session;
269
270 if ($consumer_cls !== null) {
271 $this->consumer = new $consumer_cls($store);
272 } else {
273 $this->consumer = new Auth_OpenID_GenericConsumer($store);
274 }
275
276 $this->_token_key = $this->session_key_prefix . $this->_token_suffix;
277 }
278
279 /**
280 * Used in testing to define the discovery mechanism.
281 *
282 * @access private
283 */
284 function getDiscoveryObject($session, $openid_url,
285 $session_key_prefix)
286 {
287 return new Auth_Yadis_Discovery($session, $openid_url,
288 $session_key_prefix);
289 }
290
291 /**
292 * Start the OpenID authentication process. See steps 1-2 in the
293 * overview at the top of this file.
294 *
295 * @param string $user_url Identity URL given by the user. This
296 * method performs a textual transformation of the URL to try and
297 * make sure it is normalized. For example, a user_url of
298 * example.com will be normalized to http://example.com/
299 * normalizing and resolving any redirects the server might issue.
300 *
301 * @param bool $anonymous True if the OpenID request is to be sent
302 * to the server without any identifier information. Use this
303 * when you want to transport data but don't want to do OpenID
304 * authentication with identifiers.
305 *
306 * @return Auth_OpenID_AuthRequest $auth_request An object
307 * containing the discovered information will be returned, with a
308 * method for building a redirect URL to the server, as described
309 * in step 3 of the overview. This object may also be used to add
310 * extension arguments to the request, using its 'addExtensionArg'
311 * method.
312 */
313 function begin($user_url, $anonymous=false)
314 {
315 $openid_url = $user_url;
316
317 $disco = $this->getDiscoveryObject($this->session,
318 $openid_url,
319 $this->session_key_prefix);
320
321 // Set the 'stale' attribute of the manager. If discovery
322 // fails in a fatal way, the stale flag will cause the manager
323 // to be cleaned up next time discovery is attempted.
324
325 $m = $disco->getManager();
326 $loader = new Auth_Yadis_ManagerLoader();
327
328 if ($m) {
329 if ($m->stale) {
330 $disco->destroyManager();
331 } else {
332 $m->stale = true;
333 $disco->session->set($disco->session_key,
334 serialize($loader->toSession($m)));
335 }
336 }
337
338 $endpoint = $disco->getNextService($this->discoverMethod,
339 $this->consumer->fetcher);
340
341 // Reset the 'stale' attribute of the manager.
342 $m = $disco->getManager();
343 if ($m) {
344 $m->stale = false;
345 $disco->session->set($disco->session_key,
346 serialize($loader->toSession($m)));
347 }
348
349 if ($endpoint === null) {
350 return null;
351 } else {
352 return $this->beginWithoutDiscovery($endpoint,
353 $anonymous);
354 }
355 }
356
357 /**
358 * Start OpenID verification without doing OpenID server
359 * discovery. This method is used internally by Consumer.begin
360 * after discovery is performed, and exists to provide an
361 * interface for library users needing to perform their own
362 * discovery.
363 *
364 * @param Auth_OpenID_ServiceEndpoint $endpoint an OpenID service
365 * endpoint descriptor.
366 *
367 * @param bool anonymous Set to true if you want to perform OpenID
368 * without identifiers.
369 *
370 * @return Auth_OpenID_AuthRequest $auth_request An OpenID
371 * authentication request object.
372 */
373 function beginWithoutDiscovery($endpoint, $anonymous=false)
374 {
375 $loader = new Auth_OpenID_ServiceEndpointLoader();
376 $auth_req = $this->consumer->begin($endpoint);
377 $this->session->set($this->_token_key,
378 $loader->toSession($auth_req->endpoint));
379 if (!$auth_req->setAnonymous($anonymous)) {
380 return new Auth_OpenID_FailureResponse(null,
381 "OpenID 1 requests MUST include the identifier " .
382 "in the request.");
383 }
384 return $auth_req;
385 }
386
387 /**
388 * Called to interpret the server's response to an OpenID
389 * request. It is called in step 4 of the flow described in the
390 * consumer overview.
391 *
392 * @param string $current_url The URL used to invoke the application.
393 * Extract the URL from your application's web
394 * request framework and specify it here to have it checked
395 * against the openid.current_url value in the response. If
396 * the current_url URL check fails, the status of the
397 * completion will be FAILURE.
398 *
399 * @param array $query An array of the query parameters (key =>
400 * value pairs) for this HTTP request. Defaults to null. If
401 * null, the GET or POST data are automatically gotten from the
402 * PHP environment. It is only useful to override $query for
403 * testing.
404 *
405 * @return Auth_OpenID_ConsumerResponse $response A instance of an
406 * Auth_OpenID_ConsumerResponse subclass. The type of response is
407 * indicated by the status attribute, which will be one of
408 * SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED.
409 */
410 function complete($current_url, $query=null)
411 {
412 if ($current_url && !is_string($current_url)) {
413 // This is ugly, but we need to complain loudly when
414 // someone uses the API incorrectly.
415 trigger_error("current_url must be a string; see NEWS file " .
416 "for upgrading notes.",
417 E_USER_ERROR);
418 }
419
420 if ($query === null) {
421 $query = Auth_OpenID::getQuery();
422 }
423
424 $loader = new Auth_OpenID_ServiceEndpointLoader();
425 $endpoint_data = $this->session->get($this->_token_key);
426 $endpoint =
427 $loader->fromSession($endpoint_data);
428
429 $message = Auth_OpenID_Message::fromPostArgs($query);
430 $response = $this->consumer->complete($message, $endpoint,
431 $current_url);
432 $this->session->del($this->_token_key);
433
434 if (in_array($response->status, array(Auth_OpenID_SUCCESS,
435 Auth_OpenID_CANCEL))) {
436 if ($response->identity_url !== null) {
437 $disco = $this->getDiscoveryObject($this->session,
438 $response->identity_url,
439 $this->session_key_prefix);
440 $disco->cleanup(true);
441 }
442 }
443
444 return $response;
445 }
446}
447
448/**
449 * A class implementing HMAC/DH-SHA1 consumer sessions.
450 *
451 * @package OpenID
452 */
453class Auth_OpenID_DiffieHellmanSHA1ConsumerSession {
454 var $session_type = 'DH-SHA1';
455 var $hash_func = 'Auth_OpenID_SHA1';
456 var $secret_size = 20;
457 var $allowed_assoc_types = array('HMAC-SHA1');
458
459 function Auth_OpenID_DiffieHellmanSHA1ConsumerSession($dh = null)
460 {
461 if ($dh === null) {
462 $dh = new Auth_OpenID_DiffieHellman();
463 }
464
465 $this->dh = $dh;
466 }
467
468 function getRequest()
469 {
470 $math = Auth_OpenID_getMathLib();
471
472 $cpub = $math->longToBase64($this->dh->public);
473
474 $args = array('dh_consumer_public' => $cpub);
475
476 if (!$this->dh->usingDefaultValues()) {
477 $args = array_merge($args, array(
478 'dh_modulus' =>
479 $math->longToBase64($this->dh->mod),
480 'dh_gen' =>
481 $math->longToBase64($this->dh->gen)));
482 }
483
484 return $args;
485 }
486
487 function extractSecret($response)
488 {
489 if (!$response->hasKey(Auth_OpenID_OPENID_NS,
490 'dh_server_public')) {
491 return null;
492 }
493
494 if (!$response->hasKey(Auth_OpenID_OPENID_NS,
495 'enc_mac_key')) {
496 return null;
497 }
498
499 $math = Auth_OpenID_getMathLib();
500
501 $spub = $math->base64ToLong($response->getArg(Auth_OpenID_OPENID_NS,
502 'dh_server_public'));
503 $enc_mac_key = base64_decode($response->getArg(Auth_OpenID_OPENID_NS,
504 'enc_mac_key'));
505
506 return $this->dh->xorSecret($spub, $enc_mac_key, $this->hash_func);
507 }
508}
509
510/**
511 * A class implementing HMAC/DH-SHA256 consumer sessions.
512 *
513 * @package OpenID
514 */
515class Auth_OpenID_DiffieHellmanSHA256ConsumerSession extends
516 Auth_OpenID_DiffieHellmanSHA1ConsumerSession {
517 var $session_type = 'DH-SHA256';
518 var $hash_func = 'Auth_OpenID_SHA256';
519 var $secret_size = 32;
520 var $allowed_assoc_types = array('HMAC-SHA256');
521}
522
523/**
524 * A class implementing plaintext consumer sessions.
525 *
526 * @package OpenID
527 */
528class Auth_OpenID_PlainTextConsumerSession {
529 var $session_type = 'no-encryption';
530 var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256');
531
532 function getRequest()
533 {
534 return array();
535 }
536
537 function extractSecret($response)
538 {
539 if (!$response->hasKey(Auth_OpenID_OPENID_NS, 'mac_key')) {
540 return null;
541 }
542
543 return base64_decode($response->getArg(Auth_OpenID_OPENID_NS,
544 'mac_key'));
545 }
546}
547
548/**
549 * Returns available session types.
550 */
551function Auth_OpenID_getAvailableSessionTypes()
552{
553 $types = array(
554 'no-encryption' => 'Auth_OpenID_PlainTextConsumerSession',
555 'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ConsumerSession',
556 'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ConsumerSession');
557
558 return $types;
559}
560
561/**
562 * This class is the interface to the OpenID consumer logic.
563 * Instances of it maintain no per-request state, so they can be
564 * reused (or even used by multiple threads concurrently) as needed.
565 *
566 * @package OpenID
567 */
568class Auth_OpenID_GenericConsumer {
569 /**
570 * @access private
571 */
572 var $discoverMethod = 'Auth_OpenID_discover';
573
574 /**
575 * This consumer's store object.
576 */
577 var $store;
578
579 /**
580 * @access private
581 */
582 var $_use_assocs;
583
584 /**
585 * @access private
586 */
587 var $openid1_nonce_query_arg_name = 'janrain_nonce';
588
589 /**
590 * Another query parameter that gets added to the return_to for
591 * OpenID 1; if the user's session state is lost, use this claimed
592 * identifier to do discovery when verifying the response.
593 */
594 var $openid1_return_to_identifier_name = 'openid1_claimed_id';
595
596 /**
597 * This method initializes a new {@link Auth_OpenID_Consumer}
598 * instance to access the library.
599 *
600 * @param Auth_OpenID_OpenIDStore $store This must be an object
601 * that implements the interface in {@link Auth_OpenID_OpenIDStore}.
602 * Several concrete implementations are provided, to cover most common use
603 * cases. For stores backed by MySQL, PostgreSQL, or SQLite, see
604 * the {@link Auth_OpenID_SQLStore} class and its sublcasses. For a
605 * filesystem-backed store, see the {@link Auth_OpenID_FileStore} module.
606 * As a last resort, if it isn't possible for the server to store
607 * state at all, an instance of {@link Auth_OpenID_DumbStore} can be used.
608 *
609 * @param bool $immediate This is an optional boolean value. It
610 * controls whether the library uses immediate mode, as explained
611 * in the module description. The default value is False, which
612 * disables immediate mode.
613 */
614 function Auth_OpenID_GenericConsumer($store)
615 {
616 $this->store = $store;
617 $this->negotiator = Auth_OpenID_getDefaultNegotiator();
618 $this->_use_assocs = (is_null($this->store) ? false : true);
619
620 $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
621
622 $this->session_types = Auth_OpenID_getAvailableSessionTypes();
623 }
624
625 /**
626 * Called to begin OpenID authentication using the specified
627 * {@link Auth_OpenID_ServiceEndpoint}.
628 *
629 * @access private
630 */
631 function begin($service_endpoint)
632 {
633 $assoc = $this->_getAssociation($service_endpoint);
634 $r = new Auth_OpenID_AuthRequest($service_endpoint, $assoc);
635 $r->return_to_args[$this->openid1_nonce_query_arg_name] =
636 Auth_OpenID_mkNonce();
637
638 if ($r->message->isOpenID1()) {
639 $r->return_to_args[$this->openid1_return_to_identifier_name] =
640 $r->endpoint->claimed_id;
641 }
642
643 return $r;
644 }
645
646 /**
647 * Given an {@link Auth_OpenID_Message}, {@link
648 * Auth_OpenID_ServiceEndpoint} and optional return_to URL,
649 * complete OpenID authentication.
650 *
651 * @access private
652 */
653 function complete($message, $endpoint, $return_to)
654 {
655 $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode',
656 '<no mode set>');
657
658 $mode_methods = array(
659 'cancel' => '_complete_cancel',
660 'error' => '_complete_error',
661 'setup_needed' => '_complete_setup_needed',
662 'id_res' => '_complete_id_res',
663 );
664
665 $method = Auth_OpenID::arrayGet($mode_methods, $mode,
666 '_completeInvalid');
667
668 return call_user_func_array(array($this, $method),
669 array($message, &$endpoint, $return_to));
670 }
671
672 /**
673 * @access private
674 */
675 function _completeInvalid($message, $endpoint, $unused)
676 {
677 $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode',
678 '<No mode set>');
679
680 return new Auth_OpenID_FailureResponse($endpoint,
681 sprintf("Invalid openid.mode '%s'", $mode));
682 }
683
684 /**
685 * @access private
686 */
687 function _complete_cancel($message, $endpoint, $unused)
688 {
689 return new Auth_OpenID_CancelResponse($endpoint);
690 }
691
692 /**
693 * @access private
694 */
695 function _complete_error($message, $endpoint, $unused)
696 {
697 $error = $message->getArg(Auth_OpenID_OPENID_NS, 'error');
698 $contact = $message->getArg(Auth_OpenID_OPENID_NS, 'contact');
699 $reference = $message->getArg(Auth_OpenID_OPENID_NS, 'reference');
700
701 return new Auth_OpenID_FailureResponse($endpoint, $error,
702 $contact, $reference);
703 }
704
705 /**
706 * @access private
707 */
708 function _complete_setup_needed($message, $endpoint, $unused)
709 {
710 if (!$message->isOpenID2()) {
711 return $this->_completeInvalid($message, $endpoint);
712 }
713
714 $user_setup_url = $message->getArg(Auth_OpenID_OPENID2_NS,
715 'user_setup_url');
716 return new Auth_OpenID_SetupNeededResponse($endpoint, $user_setup_url);
717 }
718
719 /**
720 * @access private
721 */
722 function _complete_id_res($message, $endpoint, $return_to)
723 {
724 $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS,
725 'user_setup_url');
726
727 if ($this->_checkSetupNeeded($message)) {
728 return new Auth_OpenID_SetupNeededResponse(
729 $endpoint, $user_setup_url);
730 } else {
731 return $this->_doIdRes($message, $endpoint, $return_to);
732 }
733 }
734
735 /**
736 * @access private
737 */
738 function _checkSetupNeeded($message)
739 {
740 // In OpenID 1, we check to see if this is a cancel from
741 // immediate mode by the presence of the user_setup_url
742 // parameter.
743 if ($message->isOpenID1()) {
744 $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS,
745 'user_setup_url');
746 if ($user_setup_url !== null) {
747 return true;
748 }
749 }
750
751 return false;
752 }
753
754 /**
755 * @access private
756 */
757 function _doIdRes($message, $endpoint, $return_to)
758 {
759 // Checks for presence of appropriate fields (and checks
760 // signed list fields)
761 $result = $this->_idResCheckForFields($message);
762
763 if (Auth_OpenID::isFailure($result)) {
764 return $result;
765 }
766
767 if (!$this->_checkReturnTo($message, $return_to)) {
768 return new Auth_OpenID_FailureResponse(null,
769 sprintf("return_to does not match return URL. Expected %s, got %s",
770 $return_to,
771 $message->getArg(Auth_OpenID_OPENID_NS, 'return_to')));
772 }
773
774 // Verify discovery information:
775 $result = $this->_verifyDiscoveryResults($message, $endpoint);
776
777 if (Auth_OpenID::isFailure($result)) {
778 return $result;
779 }
780
781 $endpoint = $result;
782
783 $result = $this->_idResCheckSignature($message,
784 $endpoint->server_url);
785
786 if (Auth_OpenID::isFailure($result)) {
787 return $result;
788 }
789
790 $result = $this->_idResCheckNonce($message, $endpoint);
791
792 if (Auth_OpenID::isFailure($result)) {
793 return $result;
794 }
795
796 $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, 'signed',
797 Auth_OpenID_NO_DEFAULT);
798 if (Auth_OpenID::isFailure($signed_list_str)) {
799 return $signed_list_str;
800 }
801 $signed_list = explode(',', $signed_list_str);
802
803 $signed_fields = Auth_OpenID::addPrefix($signed_list, "openid.");
804
805 return new Auth_OpenID_SuccessResponse($endpoint, $message,
806 $signed_fields);
807
808 }
809
810 /**
811 * @access private
812 */
813 function _checkReturnTo($message, $return_to)
814 {
815 // Check an OpenID message and its openid.return_to value
816 // against a return_to URL from an application. Return True
817 // on success, False on failure.
818
819 // Check the openid.return_to args against args in the
820 // original message.
821 $result = Auth_OpenID_GenericConsumer::_verifyReturnToArgs(
822 $message->toPostArgs());
823 if (Auth_OpenID::isFailure($result)) {
824 return false;
825 }
826
827 // Check the return_to base URL against the one in the
828 // message.
829 $msg_return_to = $message->getArg(Auth_OpenID_OPENID_NS,
830 'return_to');
831 if (Auth_OpenID::isFailure($return_to)) {
832 // XXX log me
833 return false;
834 }
835
836 $return_to_parts = parse_url(Auth_OpenID_urinorm($return_to));
837 $msg_return_to_parts = parse_url(Auth_OpenID_urinorm($msg_return_to));
838
839 // If port is absent from both, add it so it's equal in the
840 // check below.
841 if ((!array_key_exists('port', $return_to_parts)) &&
842 (!array_key_exists('port', $msg_return_to_parts))) {
843 $return_to_parts['port'] = null;
844 $msg_return_to_parts['port'] = null;
845 }
846
847 // If path is absent from both, add it so it's equal in the
848 // check below.
849 if ((!array_key_exists('path', $return_to_parts)) &&
850 (!array_key_exists('path', $msg_return_to_parts))) {
851 $return_to_parts['path'] = null;
852 $msg_return_to_parts['path'] = null;
853 }
854
855 // The URL scheme, authority, and path MUST be the same
856 // between the two URLs.
857 foreach (array('scheme', 'host', 'port', 'path') as $component) {
858 // If the url component is absent in either URL, fail.
859 // There should always be a scheme, host, port, and path.
860 if (!array_key_exists($component, $return_to_parts)) {
861 return false;
862 }
863
864 if (!array_key_exists($component, $msg_return_to_parts)) {
865 return false;
866 }
867
868 if (Auth_OpenID::arrayGet($return_to_parts, $component) !==
869 Auth_OpenID::arrayGet($msg_return_to_parts, $component)) {
870 return false;
871 }
872 }
873
874 return true;
875 }
876
877 /**
878 * @access private
879 */
880 function _verifyReturnToArgs($query)
881 {
882 // Verify that the arguments in the return_to URL are present in this
883 // response.
884
885 $message = Auth_OpenID_Message::fromPostArgs($query);
886 $return_to = $message->getArg(Auth_OpenID_OPENID_NS, 'return_to');
887
888 if (Auth_OpenID::isFailure($return_to)) {
889 return $return_to;
890 }
891 // XXX: this should be checked by _idResCheckForFields
892 if (!$return_to) {
893 return new Auth_OpenID_FailureResponse(null,
894 "Response has no return_to");
895 }
896
897 $parsed_url = parse_url($return_to);
898
899 $q = array();
900 if (array_key_exists('query', $parsed_url)) {
901 $rt_query = $parsed_url['query'];
902 $q = Auth_OpenID::parse_str($rt_query);
903 }
904
905 foreach ($q as $rt_key => $rt_value) {
906 if (!array_key_exists($rt_key, $query)) {
907 return new Auth_OpenID_FailureResponse(null,
908 sprintf("return_to parameter %s absent from query", $rt_key));
909 } else {
910 $value = $query[$rt_key];
911 if ($rt_value != $value) {
912 return new Auth_OpenID_FailureResponse(null,
913 sprintf("parameter %s value %s does not match " .
914 "return_to value %s", $rt_key,
915 $value, $rt_value));
916 }
917 }
918 }
919
920 // Make sure all non-OpenID arguments in the response are also
921 // in the signed return_to.
922 $bare_args = $message->getArgs(Auth_OpenID_BARE_NS);
923 foreach ($bare_args as $key => $value) {
924 if (Auth_OpenID::arrayGet($q, $key) != $value) {
925 return new Auth_OpenID_FailureResponse(null,
926 sprintf("Parameter %s = %s not in return_to URL",
927 $key, $value));
928 }
929 }
930
931 return true;
932 }
933
934 /**
935 * @access private
936 */
937 function _idResCheckSignature($message, $server_url)
938 {
939 $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS,
940 'assoc_handle');
941 if (Auth_OpenID::isFailure($assoc_handle)) {
942 return $assoc_handle;
943 }
944
945 $assoc = $this->store->getAssociation($server_url, $assoc_handle);
946
947 if ($assoc) {
948 if ($assoc->getExpiresIn() <= 0) {
949 // XXX: It might be a good idea sometimes to re-start
950 // the authentication with a new association. Doing it
951 // automatically opens the possibility for
952 // denial-of-service by a server that just returns
953 // expired associations (or really short-lived
954 // associations)
955 return new Auth_OpenID_FailureResponse(null,
956 'Association with ' . $server_url . ' expired');
957 }
958
959 if (!$assoc->checkMessageSignature($message)) {
960 return new Auth_OpenID_FailureResponse(null,
961 "Bad signature");
962 }
963 } else {
964 // It's not an association we know about. Stateless mode
965 // is our only possible path for recovery. XXX - async
966 // framework will not want to block on this call to
967 // _checkAuth.
968 if (!$this->_checkAuth($message, $server_url)) {
969 return new Auth_OpenID_FailureResponse(null,
970 "Server denied check_authentication");
971 }
972 }
973
974 return null;
975 }
976
977 /**
978 * @access private
979 */
980 function _verifyDiscoveryResults($message, $endpoint=null)
981 {
982 if ($message->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS) {
983 return $this->_verifyDiscoveryResultsOpenID2($message,
984 $endpoint);
985 } else {
986 return $this->_verifyDiscoveryResultsOpenID1($message,
987 $endpoint);
988 }
989 }
990
991 /**
992 * @access private
993 */
994 function _verifyDiscoveryResultsOpenID1($message, $endpoint)
995 {
996 $claimed_id = $message->getArg(Auth_OpenID_BARE_NS,
997 $this->openid1_return_to_identifier_name);
998
999 if (($endpoint === null) && ($claimed_id === null)) {
1000 return new Auth_OpenID_FailureResponse($endpoint,
1001 'When using OpenID 1, the claimed ID must be supplied, ' .
1002 'either by passing it through as a return_to parameter ' .
1003 'or by using a session, and supplied to the GenericConsumer ' .
1004 'as the argument to complete()');
1005 } else if (($endpoint !== null) && ($claimed_id === null)) {
1006 $claimed_id = $endpoint->claimed_id;
1007 }
1008
1009 $to_match = new Auth_OpenID_ServiceEndpoint();
1010 $to_match->type_uris = array(Auth_OpenID_TYPE_1_1);
1011 $to_match->local_id = $message->getArg(Auth_OpenID_OPENID1_NS,
1012 'identity');
1013
1014 // Restore delegate information from the initiation phase
1015 $to_match->claimed_id = $claimed_id;
1016
1017 if ($to_match->local_id === null) {
1018 return new Auth_OpenID_FailureResponse($endpoint,
1019 "Missing required field openid.identity");
1020 }
1021
1022 $to_match_1_0 = $to_match->copy();
1023 $to_match_1_0->type_uris = array(Auth_OpenID_TYPE_1_0);
1024
1025 if ($endpoint !== null) {
1026 $result = $this->_verifyDiscoverySingle($endpoint, $to_match);
1027
1028 if (is_a($result, 'Auth_OpenID_TypeURIMismatch')) {
1029 $result = $this->_verifyDiscoverySingle($endpoint,
1030 $to_match_1_0);
1031 }
1032
1033 if (Auth_OpenID::isFailure($result)) {
1034 // oidutil.log("Error attempting to use stored
1035 // discovery information: " + str(e))
1036 // oidutil.log("Attempting discovery to
1037 // verify endpoint")
1038 } else {
1039 return $endpoint;
1040 }
1041 }
1042
1043 // Endpoint is either bad (failed verification) or None
1044 return $this->_discoverAndVerify($to_match->claimed_id,
1045 array($to_match, $to_match_1_0));
1046 }
1047
1048 /**
1049 * @access private
1050 */
1051 function _verifyDiscoverySingle($endpoint, $to_match)
1052 {
1053 // Every type URI that's in the to_match endpoint has to be
1054 // present in the discovered endpoint.
1055 foreach ($to_match->type_uris as $type_uri) {
1056 if (!$endpoint->usesExtension($type_uri)) {
1057 return new Auth_OpenID_TypeURIMismatch($endpoint,
1058 "Required type ".$type_uri." not present");
1059 }
1060 }
1061
1062 // Fragments do not influence discovery, so we can't compare a
1063 // claimed identifier with a fragment to discovered
1064 // information.
1065 list($defragged_claimed_id, $_) =
1066 Auth_OpenID::urldefrag($to_match->claimed_id);
1067
1068 if ($defragged_claimed_id != $endpoint->claimed_id) {
1069 return new Auth_OpenID_FailureResponse($endpoint,
1070 sprintf('Claimed ID does not match (different subjects!), ' .
1071 'Expected %s, got %s', $defragged_claimed_id,
1072 $endpoint->claimed_id));
1073 }
1074
1075 if ($to_match->getLocalID() != $endpoint->getLocalID()) {
1076 return new Auth_OpenID_FailureResponse($endpoint,
1077 sprintf('local_id mismatch. Expected %s, got %s',
1078 $to_match->getLocalID(), $endpoint->getLocalID()));
1079 }
1080
1081 // If the server URL is None, this must be an OpenID 1
1082 // response, because op_endpoint is a required parameter in
1083 // OpenID 2. In that case, we don't actually care what the
1084 // discovered server_url is, because signature checking or
1085 // check_auth should take care of that check for us.
1086 if ($to_match->server_url === null) {
1087 if ($to_match->preferredNamespace() != Auth_OpenID_OPENID1_NS) {
1088 return new Auth_OpenID_FailureResponse($endpoint,
1089 "Preferred namespace mismatch (bug)");
1090 }
1091 } else if ($to_match->server_url != $endpoint->server_url) {
1092 return new Auth_OpenID_FailureResponse($endpoint,
1093 sprintf('OP Endpoint mismatch. Expected %s, got %s',
1094 $to_match->server_url, $endpoint->server_url));
1095 }
1096
1097 return null;
1098 }
1099
1100 /**
1101 * @access private
1102 */
1103 function _verifyDiscoveryResultsOpenID2($message, $endpoint)
1104 {
1105 $to_match = new Auth_OpenID_ServiceEndpoint();
1106 $to_match->type_uris = array(Auth_OpenID_TYPE_2_0);
1107 $to_match->claimed_id = $message->getArg(Auth_OpenID_OPENID2_NS,
1108 'claimed_id');
1109
1110 $to_match->local_id = $message->getArg(Auth_OpenID_OPENID2_NS,
1111 'identity');
1112
1113 $to_match->server_url = $message->getArg(Auth_OpenID_OPENID2_NS,
1114 'op_endpoint');
1115
1116 if ($to_match->server_url === null) {
1117 return new Auth_OpenID_FailureResponse($endpoint,
1118 "OP Endpoint URL missing");
1119 }
1120
1121 // claimed_id and identifier must both be present or both be
1122 // absent
1123 if (($to_match->claimed_id === null) &&
1124 ($to_match->local_id !== null)) {
1125 return new Auth_OpenID_FailureResponse($endpoint,
1126 'openid.identity is present without openid.claimed_id');
1127 }
1128
1129 if (($to_match->claimed_id !== null) &&
1130 ($to_match->local_id === null)) {
1131 return new Auth_OpenID_FailureResponse($endpoint,
1132 'openid.claimed_id is present without openid.identity');
1133 }
1134
1135 if ($to_match->claimed_id === null) {
1136 // This is a response without identifiers, so there's
1137 // really no checking that we can do, so return an
1138 // endpoint that's for the specified `openid.op_endpoint'
1139 return Auth_OpenID_ServiceEndpoint::fromOPEndpointURL(
1140 $to_match->server_url);
1141 }
1142
1143 if (!$endpoint) {
1144 // The claimed ID doesn't match, so we have to do
1145 // discovery again. This covers not using sessions, OP
1146 // identifier endpoints and responses that didn't match
1147 // the original request.
1148 // oidutil.log('No pre-discovered information supplied.')
1149 return $this->_discoverAndVerify($to_match->claimed_id,
1150 array($to_match));
1151 } else {
1152
1153 // The claimed ID matches, so we use the endpoint that we
1154 // discovered in initiation. This should be the most
1155 // common case.
1156 $result = $this->_verifyDiscoverySingle($endpoint, $to_match);
1157
1158 if (Auth_OpenID::isFailure($result)) {
1159 $endpoint = $this->_discoverAndVerify($to_match->claimed_id,
1160 array($to_match));
1161 if (Auth_OpenID::isFailure($endpoint)) {
1162 return $endpoint;
1163 }
1164 }
1165 }
1166
1167 // The endpoint we return should have the claimed ID from the
1168 // message we just verified, fragment and all.
1169 if ($endpoint->claimed_id != $to_match->claimed_id) {
1170 $endpoint->claimed_id = $to_match->claimed_id;
1171 }
1172
1173 return $endpoint;
1174 }
1175
1176 /**
1177 * @access private
1178 */
1179 function _discoverAndVerify($claimed_id, $to_match_endpoints)
1180 {
1181 // oidutil.log('Performing discovery on %s' % (claimed_id,))
1182 list($unused, $services) = call_user_func($this->discoverMethod,
1183 $claimed_id,
1184 &$this->fetcher);
1185
1186 if (!$services) {
1187 return new Auth_OpenID_FailureResponse(null,
1188 sprintf("No OpenID information found at %s",
1189 $claimed_id));
1190 }
1191
1192 return $this->_verifyDiscoveryServices($claimed_id, $services,
1193 $to_match_endpoints);
1194 }
1195
1196 /**
1197 * @access private
1198 */
1199 function _verifyDiscoveryServices($claimed_id,
1200 $services, $to_match_endpoints)
1201 {
1202 // Search the services resulting from discovery to find one
1203 // that matches the information from the assertion
1204
1205 foreach ($services as $endpoint) {
1206 foreach ($to_match_endpoints as $to_match_endpoint) {
1207 $result = $this->_verifyDiscoverySingle($endpoint,
1208 $to_match_endpoint);
1209
1210 if (!Auth_OpenID::isFailure($result)) {
1211 // It matches, so discover verification has
1212 // succeeded. Return this endpoint.
1213 return $endpoint;
1214 }
1215 }
1216 }
1217
1218 return new Auth_OpenID_FailureResponse(null,
1219 sprintf('No matching endpoint found after discovering %s: %s',
1220 $claimed_id, $result->message));
1221 }
1222
1223 /**
1224 * Extract the nonce from an OpenID 1 response. Return the nonce
1225 * from the BARE_NS since we independently check the return_to
1226 * arguments are the same as those in the response message.
1227 *
1228 * See the openid1_nonce_query_arg_name class variable
1229 *
1230 * @returns $nonce The nonce as a string or null
1231 *
1232 * @access private
1233 */
1234 function _idResGetNonceOpenID1($message, $endpoint)
1235 {
1236 return $message->getArg(Auth_OpenID_BARE_NS,
1237 $this->openid1_nonce_query_arg_name);
1238 }
1239
1240 /**
1241 * @access private
1242 */
1243 function _idResCheckNonce($message, $endpoint)
1244 {
1245 if ($message->isOpenID1()) {
1246 // This indicates that the nonce was generated by the consumer
1247 $nonce = $this->_idResGetNonceOpenID1($message, $endpoint);
1248 $server_url = '';
1249 } else {
1250 $nonce = $message->getArg(Auth_OpenID_OPENID2_NS,
1251 'response_nonce');
1252
1253 $server_url = $endpoint->server_url;
1254 }
1255
1256 if ($nonce === null) {
1257 return new Auth_OpenID_FailureResponse($endpoint,
1258 "Nonce missing from response");
1259 }
1260
1261 $parts = Auth_OpenID_splitNonce($nonce);
1262
1263 if ($parts === null) {
1264 return new Auth_OpenID_FailureResponse($endpoint,
1265 "Malformed nonce in response");
1266 }
1267
1268 list($timestamp, $salt) = $parts;
1269
1270 if (!$this->store->useNonce($server_url, $timestamp, $salt)) {
1271 return new Auth_OpenID_FailureResponse($endpoint,
1272 "Nonce already used or out of range");
1273 }
1274
1275 return null;
1276 }
1277
1278 /**
1279 * @access private
1280 */
1281 function _idResCheckForFields($message)
1282 {
1283 $basic_fields = array('return_to', 'assoc_handle', 'sig', 'signed');
1284 $basic_sig_fields = array('return_to', 'identity');
1285
1286 $require_fields = array(
1287 Auth_OpenID_OPENID2_NS => array_merge($basic_fields,
1288 array('op_endpoint')),
1289
1290 Auth_OpenID_OPENID1_NS => array_merge($basic_fields,
1291 array('identity'))
1292 );
1293
1294 $require_sigs = array(
1295 Auth_OpenID_OPENID2_NS => array_merge($basic_sig_fields,
1296 array('response_nonce',
1297 'claimed_id',
1298 'assoc_handle',
1299 'op_endpoint')),
1300 Auth_OpenID_OPENID1_NS => array_merge($basic_sig_fields,
1301 array('nonce'))
1302 );
1303
1304 foreach ($require_fields[$message->getOpenIDNamespace()] as $field) {
1305 if (!$message->hasKey(Auth_OpenID_OPENID_NS, $field)) {
1306 return new Auth_OpenID_FailureResponse(null,
1307 "Missing required field '".$field."'");
1308 }
1309 }
1310
1311 $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS,
1312 'signed',
1313 Auth_OpenID_NO_DEFAULT);
1314 if (Auth_OpenID::isFailure($signed_list_str)) {
1315 return $signed_list_str;
1316 }
1317 $signed_list = explode(',', $signed_list_str);
1318
1319 foreach ($require_sigs[$message->getOpenIDNamespace()] as $field) {
1320 // Field is present and not in signed list
1321 if ($message->hasKey(Auth_OpenID_OPENID_NS, $field) &&
1322 (!in_array($field, $signed_list))) {
1323 return new Auth_OpenID_FailureResponse(null,
1324 "'".$field."' not signed");
1325 }
1326 }
1327
1328 return null;
1329 }
1330
1331 /**
1332 * @access private
1333 */
1334 function _checkAuth($message, $server_url)
1335 {
1336 $request = $this->_createCheckAuthRequest($message);
1337 if ($request === null) {
1338 return false;
1339 }
1340
1341 $resp_message = $this->_makeKVPost($request, $server_url);
1342 if (($resp_message === null) ||
1343 (is_a($resp_message, 'Auth_OpenID_ServerErrorContainer'))) {
1344 return false;
1345 }
1346
1347 return $this->_processCheckAuthResponse($resp_message, $server_url);
1348 }
1349
1350 /**
1351 * @access private
1352 */
1353 function _createCheckAuthRequest($message)
1354 {
1355 $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
1356 if ($signed) {
1357 foreach (explode(',', $signed) as $k) {
1358 $value = $message->getAliasedArg($k);
1359 if ($value === null) {
1360 return null;
1361 }
1362 }
1363 }
1364 $ca_message = $message->copy();
1365 $ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode',
1366 'check_authentication');
1367 return $ca_message;
1368 }
1369
1370 /**
1371 * @access private
1372 */
1373 function _processCheckAuthResponse($response, $server_url)
1374 {
1375 $is_valid = $response->getArg(Auth_OpenID_OPENID_NS, 'is_valid',
1376 'false');
1377
1378 $invalidate_handle = $response->getArg(Auth_OpenID_OPENID_NS,
1379 'invalidate_handle');
1380
1381 if ($invalidate_handle !== null) {
1382 $this->store->removeAssociation($server_url,
1383 $invalidate_handle);
1384 }
1385
1386 if ($is_valid == 'true') {
1387 return true;
1388 }
1389
1390 return false;
1391 }
1392
1393 /**
1394 * Adapt a POST response to a Message.
1395 *
1396 * @param $response Result of a POST to an OpenID endpoint.
1397 *
1398 * @access private
1399 */
1400 static function _httpResponseToMessage($response, $server_url)
1401 {
1402 // Should this function be named Message.fromHTTPResponse instead?
1403 $response_message = Auth_OpenID_Message::fromKVForm($response->body);
1404
1405 if ($response->status == 400) {
1406 return Auth_OpenID_ServerErrorContainer::fromMessage(
1407 $response_message);
1408 } else if ($response->status != 200 and $response->status != 206) {
1409 return null;
1410 }
1411
1412 return $response_message;
1413 }
1414
1415 /**
1416 * @access private
1417 */
1418 function _makeKVPost($message, $server_url)
1419 {
1420 $body = $message->toURLEncoded();
1421 $resp = $this->fetcher->post($server_url, $body);
1422
1423 if ($resp === null) {
1424 return null;
1425 }
1426
1427 return $this->_httpResponseToMessage($resp, $server_url);
1428 }
1429
1430 /**
1431 * @access private
1432 */
1433 function _getAssociation($endpoint)
1434 {
1435 if (!$this->_use_assocs) {
1436 return null;
1437 }
1438
1439 $assoc = $this->store->getAssociation($endpoint->server_url);
1440
1441 if (($assoc === null) ||
1442 ($assoc->getExpiresIn() <= 0)) {
1443
1444 $assoc = $this->_negotiateAssociation($endpoint);
1445
1446 if ($assoc !== null) {
1447 $this->store->storeAssociation($endpoint->server_url,
1448 $assoc);
1449 }
1450 }
1451
1452 return $assoc;
1453 }
1454
1455 /**
1456 * Handle ServerErrors resulting from association requests.
1457 *
1458 * @return $result If server replied with an C{unsupported-type}
1459 * error, return a tuple of supported C{association_type},
1460 * C{session_type}. Otherwise logs the error and returns null.
1461 *
1462 * @access private
1463 */
1464 function _extractSupportedAssociationType($server_error, $endpoint,
1465 $assoc_type)
1466 {
1467 // Any error message whose code is not 'unsupported-type'
1468 // should be considered a total failure.
1469 if (($server_error->error_code != 'unsupported-type') ||
1470 ($server_error->message->isOpenID1())) {
1471 return null;
1472 }
1473
1474 // The server didn't like the association/session type that we
1475 // sent, and it sent us back a message that might tell us how
1476 // to handle it.
1477
1478 // Extract the session_type and assoc_type from the error
1479 // message
1480 $assoc_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS,
1481 'assoc_type');
1482
1483 $session_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS,
1484 'session_type');
1485
1486 if (($assoc_type === null) || ($session_type === null)) {
1487 return null;
1488 } else if (!$this->negotiator->isAllowed($assoc_type,
1489 $session_type)) {
1490 return null;
1491 } else {
1492 return array($assoc_type, $session_type);
1493 }
1494 }
1495
1496 /**
1497 * @access private
1498 */
1499 function _negotiateAssociation($endpoint)
1500 {
1501 // Get our preferred session/association type from the negotiatior.
1502 list($assoc_type, $session_type) = $this->negotiator->getAllowedType();
1503
1504 $assoc = $this->_requestAssociation(
1505 $endpoint, $assoc_type, $session_type);
1506
1507 if (Auth_OpenID::isFailure($assoc)) {
1508 return null;
1509 }
1510
1511 if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) {
1512 $why = $assoc;
1513
1514 $supportedTypes = $this->_extractSupportedAssociationType(
1515 $why, $endpoint, $assoc_type);
1516
1517 if ($supportedTypes !== null) {
1518 list($assoc_type, $session_type) = $supportedTypes;
1519
1520 // Attempt to create an association from the assoc_type
1521 // and session_type that the server told us it
1522 // supported.
1523 $assoc = $this->_requestAssociation(
1524 $endpoint, $assoc_type, $session_type);
1525
1526 if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) {
1527 // Do not keep trying, since it rejected the
1528 // association type that it told us to use.
1529 // oidutil.log('Server %s refused its suggested association
1530 // 'type: session_type=%s, assoc_type=%s'
1531 // % (endpoint.server_url, session_type,
1532 // assoc_type))
1533 return null;
1534 } else {
1535 return $assoc;
1536 }
1537 } else {
1538 return null;
1539 }
1540 } else {
1541 return $assoc;
1542 }
1543 }
1544
1545 /**
1546 * @access private
1547 */
1548 function _requestAssociation($endpoint, $assoc_type, $session_type)
1549 {
1550 list($assoc_session, $args) = $this->_createAssociateRequest(
1551 $endpoint, $assoc_type, $session_type);
1552
1553 $response_message = $this->_makeKVPost($args, $endpoint->server_url);
1554
1555 if ($response_message === null) {
1556 // oidutil.log('openid.associate request failed: %s' % (why[0],))
1557 return null;
1558 } else if (is_a($response_message,
1559 'Auth_OpenID_ServerErrorContainer')) {
1560 return $response_message;
1561 }
1562
1563 return $this->_extractAssociation($response_message, $assoc_session);
1564 }
1565
1566 /**
1567 * @access private
1568 */
1569 function _extractAssociation($assoc_response, $assoc_session)
1570 {
1571 // Extract the common fields from the response, raising an
1572 // exception if they are not found
1573 $assoc_type = $assoc_response->getArg(
1574 Auth_OpenID_OPENID_NS, 'assoc_type',
1575 Auth_OpenID_NO_DEFAULT);
1576
1577 if (Auth_OpenID::isFailure($assoc_type)) {
1578 return $assoc_type;
1579 }
1580
1581 $assoc_handle = $assoc_response->getArg(
1582 Auth_OpenID_OPENID_NS, 'assoc_handle',
1583 Auth_OpenID_NO_DEFAULT);
1584
1585 if (Auth_OpenID::isFailure($assoc_handle)) {
1586 return $assoc_handle;
1587 }
1588
1589 // expires_in is a base-10 string. The Python parsing will
1590 // accept literals that have whitespace around them and will
1591 // accept negative values. Neither of these are really in-spec,
1592 // but we think it's OK to accept them.
1593 $expires_in_str = $assoc_response->getArg(
1594 Auth_OpenID_OPENID_NS, 'expires_in',
1595 Auth_OpenID_NO_DEFAULT);
1596
1597 if (Auth_OpenID::isFailure($expires_in_str)) {
1598 return $expires_in_str;
1599 }
1600
1601 $expires_in = Auth_OpenID::intval($expires_in_str);
1602 if ($expires_in === false) {
1603
1604 $err = sprintf("Could not parse expires_in from association ".
1605 "response %s", print_r($assoc_response, true));
1606 return new Auth_OpenID_FailureResponse(null, $err);
1607 }
1608
1609 // OpenID 1 has funny association session behaviour.
1610 if ($assoc_response->isOpenID1()) {
1611 $session_type = $this->_getOpenID1SessionType($assoc_response);
1612 } else {
1613 $session_type = $assoc_response->getArg(
1614 Auth_OpenID_OPENID2_NS, 'session_type',
1615 Auth_OpenID_NO_DEFAULT);
1616
1617 if (Auth_OpenID::isFailure($session_type)) {
1618 return $session_type;
1619 }
1620 }
1621
1622 // Session type mismatch
1623 if ($assoc_session->session_type != $session_type) {
1624 if ($assoc_response->isOpenID1() &&
1625 ($session_type == 'no-encryption')) {
1626 // In OpenID 1, any association request can result in
1627 // a 'no-encryption' association response. Setting
1628 // assoc_session to a new no-encryption session should
1629 // make the rest of this function work properly for
1630 // that case.
1631 $assoc_session = new Auth_OpenID_PlainTextConsumerSession();
1632 } else {
1633 // Any other mismatch, regardless of protocol version
1634 // results in the failure of the association session
1635 // altogether.
1636 return null;
1637 }
1638 }
1639
1640 // Make sure assoc_type is valid for session_type
1641 if (!in_array($assoc_type, $assoc_session->allowed_assoc_types)) {
1642 return null;
1643 }
1644
1645 // Delegate to the association session to extract the secret
1646 // from the response, however is appropriate for that session
1647 // type.
1648 $secret = $assoc_session->extractSecret($assoc_response);
1649
1650 if ($secret === null) {
1651 return null;
1652 }
1653
1654 return Auth_OpenID_Association::fromExpiresIn(
1655 $expires_in, $assoc_handle, $secret, $assoc_type);
1656 }
1657
1658 /**
1659 * @access private
1660 */
1661 function _createAssociateRequest($endpoint, $assoc_type, $session_type)
1662 {
1663 if (array_key_exists($session_type, $this->session_types)) {
1664 $session_type_class = $this->session_types[$session_type];
1665
1666 if (is_callable($session_type_class)) {
1667 $assoc_session = $session_type_class();
1668 } else {
1669 $assoc_session = new $session_type_class();
1670 }
1671 } else {
1672 return null;
1673 }
1674
1675 $args = array(
1676 'mode' => 'associate',
1677 'assoc_type' => $assoc_type);
1678
1679 if (!$endpoint->compatibilityMode()) {
1680 $args['ns'] = Auth_OpenID_OPENID2_NS;
1681 }
1682
1683 // Leave out the session type if we're in compatibility mode
1684 // *and* it's no-encryption.
1685 if ((!$endpoint->compatibilityMode()) ||
1686 ($assoc_session->session_type != 'no-encryption')) {
1687 $args['session_type'] = $assoc_session->session_type;
1688 }
1689
1690 $args = array_merge($args, $assoc_session->getRequest());
1691 $message = Auth_OpenID_Message::fromOpenIDArgs($args);
1692 return array($assoc_session, $message);
1693 }
1694
1695 /**
1696 * Given an association response message, extract the OpenID 1.X
1697 * session type.
1698 *
1699 * This function mostly takes care of the 'no-encryption' default
1700 * behavior in OpenID 1.
1701 *
1702 * If the association type is plain-text, this function will
1703 * return 'no-encryption'
1704 *
1705 * @access private
1706 * @return $typ The association type for this message
1707 */
1708 function _getOpenID1SessionType($assoc_response)
1709 {
1710 // If it's an OpenID 1 message, allow session_type to default
1711 // to None (which signifies "no-encryption")
1712 $session_type = $assoc_response->getArg(Auth_OpenID_OPENID1_NS,
1713 'session_type');
1714
1715 // Handle the differences between no-encryption association
1716 // respones in OpenID 1 and 2:
1717
1718 // no-encryption is not really a valid session type for OpenID
1719 // 1, but we'll accept it anyway, while issuing a warning.
1720 if ($session_type == 'no-encryption') {
1721 // oidutil.log('WARNING: OpenID server sent "no-encryption"'
1722 // 'for OpenID 1.X')
1723 } else if (($session_type == '') || ($session_type === null)) {
1724 // Missing or empty session type is the way to flag a
1725 // 'no-encryption' response. Change the session type to
1726 // 'no-encryption' so that it can be handled in the same
1727 // way as OpenID 2 'no-encryption' respones.
1728 $session_type = 'no-encryption';
1729 }
1730
1731 return $session_type;
1732 }
1733}
1734
1735/**
1736 * This class represents an authentication request from a consumer to
1737 * an OpenID server.
1738 *
1739 * @package OpenID
1740 */
1741class Auth_OpenID_AuthRequest {
1742
1743 /**
1744 * Initialize an authentication request with the specified token,
1745 * association, and endpoint.
1746 *
1747 * Users of this library should not create instances of this
1748 * class. Instances of this class are created by the library when
1749 * needed.
1750 */
1751 function Auth_OpenID_AuthRequest($endpoint, $assoc)
1752 {
1753 $this->assoc = $assoc;
1754 $this->endpoint = $endpoint;
1755 $this->return_to_args = array();
1756 $this->message = new Auth_OpenID_Message(
1757 $endpoint->preferredNamespace());
1758 $this->_anonymous = false;
1759 }
1760
1761 /**
1762 * Add an extension to this checkid request.
1763 *
1764 * $extension_request: An object that implements the extension
1765 * request interface for adding arguments to an OpenID message.
1766 */
1767 function addExtension($extension_request)
1768 {
1769 $extension_request->toMessage($this->message);
1770 }
1771
1772 /**
1773 * Add an extension argument to this OpenID authentication
1774 * request.
1775 *
1776 * Use caution when adding arguments, because they will be
1777 * URL-escaped and appended to the redirect URL, which can easily
1778 * get quite long.
1779 *
1780 * @param string $namespace The namespace for the extension. For
1781 * example, the simple registration extension uses the namespace
1782 * 'sreg'.
1783 *
1784 * @param string $key The key within the extension namespace. For
1785 * example, the nickname field in the simple registration
1786 * extension's key is 'nickname'.
1787 *
1788 * @param string $value The value to provide to the server for
1789 * this argument.
1790 */
1791 function addExtensionArg($namespace, $key, $value)
1792 {
1793 return $this->message->setArg($namespace, $key, $value);
1794 }
1795
1796 /**
1797 * Set whether this request should be made anonymously. If a
1798 * request is anonymous, the identifier will not be sent in the
1799 * request. This is only useful if you are making another kind of
1800 * request with an extension in this request.
1801 *
1802 * Anonymous requests are not allowed when the request is made
1803 * with OpenID 1.
1804 */
1805 function setAnonymous($is_anonymous)
1806 {
1807 if ($is_anonymous && $this->message->isOpenID1()) {
1808 return false;
1809 } else {
1810 $this->_anonymous = $is_anonymous;
1811 return true;
1812 }
1813 }
1814
1815 /**
1816 * Produce a {@link Auth_OpenID_Message} representing this
1817 * request.
1818 *
1819 * @param string $realm The URL (or URL pattern) that identifies
1820 * your web site to the user when she is authorizing it.
1821 *
1822 * @param string $return_to The URL that the OpenID provider will
1823 * send the user back to after attempting to verify her identity.
1824 *
1825 * Not specifying a return_to URL means that the user will not be
1826 * returned to the site issuing the request upon its completion.
1827 *
1828 * @param bool $immediate If true, the OpenID provider is to send
1829 * back a response immediately, useful for behind-the-scenes
1830 * authentication attempts. Otherwise the OpenID provider may
1831 * engage the user before providing a response. This is the
1832 * default case, as the user may need to provide credentials or
1833 * approve the request before a positive response can be sent.
1834 */
1835 function getMessage($realm, $return_to=null, $immediate=false)
1836 {
1837 if ($return_to) {
1838 $return_to = Auth_OpenID::appendArgs($return_to,
1839 $this->return_to_args);
1840 } else if ($immediate) {
1841 // raise ValueError(
1842 // '"return_to" is mandatory when
1843 //using "checkid_immediate"')
1844 return new Auth_OpenID_FailureResponse(null,
1845 "'return_to' is mandatory when using checkid_immediate");
1846 } else if ($this->message->isOpenID1()) {
1847 // raise ValueError('"return_to" is
1848 // mandatory for OpenID 1 requests')
1849 return new Auth_OpenID_FailureResponse(null,
1850 "'return_to' is mandatory for OpenID 1 requests");
1851 } else if ($this->return_to_args) {
1852 // raise ValueError('extra "return_to" arguments
1853 // were specified, but no return_to was specified')
1854 return new Auth_OpenID_FailureResponse(null,
1855 "extra 'return_to' arguments where specified, " .
1856 "but no return_to was specified");
1857 }
1858
1859 if ($immediate) {
1860 $mode = 'checkid_immediate';
1861 } else {
1862 $mode = 'checkid_setup';
1863 }
1864
1865 $message = $this->message->copy();
1866 if ($message->isOpenID1()) {
1867 $realm_key = 'trust_root';
1868 } else {
1869 $realm_key = 'realm';
1870 }
1871
1872 $message->updateArgs(Auth_OpenID_OPENID_NS,
1873 array(
1874 $realm_key => $realm,
1875 'mode' => $mode,
1876 'return_to' => $return_to));
1877
1878 if (!$this->_anonymous) {
1879 if ($this->endpoint->isOPIdentifier()) {
1880 // This will never happen when we're in compatibility
1881 // mode, as long as isOPIdentifier() returns False
1882 // whenever preferredNamespace() returns OPENID1_NS.
1883 $claimed_id = $request_identity =
1884 Auth_OpenID_IDENTIFIER_SELECT;
1885 } else {
1886 $request_identity = $this->endpoint->getLocalID();
1887 $claimed_id = $this->endpoint->claimed_id;
1888 }
1889
1890 // This is true for both OpenID 1 and 2
1891 $message->setArg(Auth_OpenID_OPENID_NS, 'identity',
1892 $request_identity);
1893
1894 if ($message->isOpenID2()) {
1895 $message->setArg(Auth_OpenID_OPENID2_NS, 'claimed_id',
1896 $claimed_id);
1897 }
1898 }
1899
1900 if ($this->assoc) {
1901 $message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
1902 $this->assoc->handle);
1903 }
1904
1905 return $message;
1906 }
1907
1908 function redirectURL($realm, $return_to = null,
1909 $immediate = false)
1910 {
1911 $message = $this->getMessage($realm, $return_to, $immediate);
1912
1913 if (Auth_OpenID::isFailure($message)) {
1914 return $message;
1915 }
1916
1917 return $message->toURL($this->endpoint->server_url);
1918 }
1919
1920 /**
1921 * Get html for a form to submit this request to the IDP.
1922 *
1923 * form_tag_attrs: An array of attributes to be added to the form
1924 * tag. 'accept-charset' and 'enctype' have defaults that can be
1925 * overridden. If a value is supplied for 'action' or 'method', it
1926 * will be replaced.
1927 */
1928 function formMarkup($realm, $return_to=null, $immediate=false,
1929 $form_tag_attrs=null)
1930 {
1931 $message = $this->getMessage($realm, $return_to, $immediate);
1932
1933 if (Auth_OpenID::isFailure($message)) {
1934 return $message;
1935 }
1936
1937 return $message->toFormMarkup($this->endpoint->server_url,
1938 $form_tag_attrs);
1939 }
1940
1941 /**
1942 * Get a complete html document that will autosubmit the request
1943 * to the IDP.
1944 *
1945 * Wraps formMarkup. See the documentation for that function.
1946 */
1947 function htmlMarkup($realm, $return_to=null, $immediate=false,
1948 $form_tag_attrs=null)
1949 {
1950 $form = $this->formMarkup($realm, $return_to, $immediate,
1951 $form_tag_attrs);
1952
1953 if (Auth_OpenID::isFailure($form)) {
1954 return $form;
1955 }
1956 return Auth_OpenID::autoSubmitHTML($form);
1957 }
1958
1959 function shouldSendRedirect()
1960 {
1961 return $this->endpoint->compatibilityMode();
1962 }
1963}
1964
1965/**
1966 * The base class for responses from the Auth_OpenID_Consumer.
1967 *
1968 * @package OpenID
1969 */
1970class Auth_OpenID_ConsumerResponse {
1971 var $status = null;
1972
1973 function setEndpoint($endpoint)
1974 {
1975 $this->endpoint = $endpoint;
1976 if ($endpoint === null) {
1977 $this->identity_url = null;
1978 } else {
1979 $this->identity_url = $endpoint->claimed_id;
1980 }
1981 }
1982
1983 /**
1984 * Return the display identifier for this response.
1985 *
1986 * The display identifier is related to the Claimed Identifier, but the
1987 * two are not always identical. The display identifier is something the
1988 * user should recognize as what they entered, whereas the response's
1989 * claimed identifier (in the identity_url attribute) may have extra
1990 * information for better persistence.
1991 *
1992 * URLs will be stripped of their fragments for display. XRIs will
1993 * display the human-readable identifier (i-name) instead of the
1994 * persistent identifier (i-number).
1995 *
1996 * Use the display identifier in your user interface. Use
1997 * identity_url for querying your database or authorization server.
1998 *
1999 */
2000 function getDisplayIdentifier()
2001 {
2002 if ($this->endpoint !== null) {
2003 return $this->endpoint->getDisplayIdentifier();
2004 }
2005 return null;
2006 }
2007}
2008
2009/**
2010 * A response with a status of Auth_OpenID_SUCCESS. Indicates that
2011 * this request is a successful acknowledgement from the OpenID server
2012 * that the supplied URL is, indeed controlled by the requesting
2013 * agent. This has three relevant attributes:
2014 *
2015 * claimed_id - The identity URL that has been authenticated
2016 *
2017 * signed_args - The arguments in the server's response that were
2018 * signed and verified.
2019 *
2020 * status - Auth_OpenID_SUCCESS.
2021 *
2022 * @package OpenID
2023 */
2024class Auth_OpenID_SuccessResponse extends Auth_OpenID_ConsumerResponse {
2025 var $status = Auth_OpenID_SUCCESS;
2026
2027 /**
2028 * @access private
2029 */
2030 function Auth_OpenID_SuccessResponse($endpoint, $message, $signed_args=null)
2031 {
2032 $this->endpoint = $endpoint;
2033 $this->identity_url = $endpoint->claimed_id;
2034 $this->signed_args = $signed_args;
2035 $this->message = $message;
2036
2037 if ($this->signed_args === null) {
2038 $this->signed_args = array();
2039 }
2040 }
2041
2042 /**
2043 * Extract signed extension data from the server's response.
2044 *
2045 * @param string $prefix The extension namespace from which to
2046 * extract the extension data.
2047 */
2048 function extensionResponse($namespace_uri, $require_signed)
2049 {
2050 if ($require_signed) {
2051 return $this->getSignedNS($namespace_uri);
2052 } else {
2053 return $this->message->getArgs($namespace_uri);
2054 }
2055 }
2056
2057 function isOpenID1()
2058 {
2059 return $this->message->isOpenID1();
2060 }
2061
2062 function isSigned($ns_uri, $ns_key)
2063 {
2064 // Return whether a particular key is signed, regardless of
2065 // its namespace alias
2066 return in_array($this->message->getKey($ns_uri, $ns_key),
2067 $this->signed_args);
2068 }
2069
2070 function getSigned($ns_uri, $ns_key, $default = null)
2071 {
2072 // Return the specified signed field if available, otherwise
2073 // return default
2074 if ($this->isSigned($ns_uri, $ns_key)) {
2075 return $this->message->getArg($ns_uri, $ns_key, $default);
2076 } else {
2077 return $default;
2078 }
2079 }
2080
2081 function getSignedNS($ns_uri)
2082 {
2083 $args = array();
2084
2085 $msg_args = $this->message->getArgs($ns_uri);
2086 if (Auth_OpenID::isFailure($msg_args)) {
2087 return null;
2088 }
2089
2090 foreach ($msg_args as $key => $value) {
2091 if (!$this->isSigned($ns_uri, $key)) {
2092 unset($msg_args[$key]);
2093 }
2094 }
2095
2096 return $msg_args;
2097 }
2098
2099 /**
2100 * Get the openid.return_to argument from this response.
2101 *
2102 * This is useful for verifying that this request was initiated by
2103 * this consumer.
2104 *
2105 * @return string $return_to The return_to URL supplied to the
2106 * server on the initial request, or null if the response did not
2107 * contain an 'openid.return_to' argument.
2108 */
2109 function getReturnTo()
2110 {
2111 return $this->getSigned(Auth_OpenID_OPENID_NS, 'return_to');
2112 }
2113}
2114
2115/**
2116 * A response with a status of Auth_OpenID_FAILURE. Indicates that the
2117 * OpenID protocol has failed. This could be locally or remotely
2118 * triggered. This has three relevant attributes:
2119 *
2120 * claimed_id - The identity URL for which authentication was
2121 * attempted, if it can be determined. Otherwise, null.
2122 *
2123 * message - A message indicating why the request failed, if one is
2124 * supplied. Otherwise, null.
2125 *
2126 * status - Auth_OpenID_FAILURE.
2127 *
2128 * @package OpenID
2129 */
2130class Auth_OpenID_FailureResponse extends Auth_OpenID_ConsumerResponse {
2131 var $status = Auth_OpenID_FAILURE;
2132
2133 function Auth_OpenID_FailureResponse($endpoint, $message = null,
2134 $contact = null, $reference = null)
2135 {
2136 $this->setEndpoint($endpoint);
2137 $this->message = $message;
2138 $this->contact = $contact;
2139 $this->reference = $reference;
2140 }
2141}
2142
2143/**
2144 * A specific, internal failure used to detect type URI mismatch.
2145 *
2146 * @package OpenID
2147 */
2148class Auth_OpenID_TypeURIMismatch extends Auth_OpenID_FailureResponse {
2149}
2150
2151/**
2152 * Exception that is raised when the server returns a 400 response
2153 * code to a direct request.
2154 *
2155 * @package OpenID
2156 */
2157class Auth_OpenID_ServerErrorContainer {
2158 function Auth_OpenID_ServerErrorContainer($error_text,
2159 $error_code,
2160 $message)
2161 {
2162 $this->error_text = $error_text;
2163 $this->error_code = $error_code;
2164 $this->message = $message;
2165 }
2166
2167 /**
2168 * @access private
2169 */
2170 static function fromMessage($message)
2171 {
2172 $error_text = $message->getArg(
2173 Auth_OpenID_OPENID_NS, 'error', '<no error message supplied>');
2174 $error_code = $message->getArg(Auth_OpenID_OPENID_NS, 'error_code');
2175 return new Auth_OpenID_ServerErrorContainer($error_text,
2176 $error_code,
2177 $message);
2178 }
2179}
2180
2181/**
2182 * A response with a status of Auth_OpenID_CANCEL. Indicates that the
2183 * user cancelled the OpenID authentication request. This has two
2184 * relevant attributes:
2185 *
2186 * claimed_id - The identity URL for which authentication was
2187 * attempted, if it can be determined. Otherwise, null.
2188 *
2189 * status - Auth_OpenID_SUCCESS.
2190 *
2191 * @package OpenID
2192 */
2193class Auth_OpenID_CancelResponse extends Auth_OpenID_ConsumerResponse {
2194 var $status = Auth_OpenID_CANCEL;
2195
2196 function Auth_OpenID_CancelResponse($endpoint)
2197 {
2198 $this->setEndpoint($endpoint);
2199 }
2200}
2201
2202/**
2203 * A response with a status of Auth_OpenID_SETUP_NEEDED. Indicates
2204 * that the request was in immediate mode, and the server is unable to
2205 * authenticate the user without further interaction.
2206 *
2207 * claimed_id - The identity URL for which authentication was
2208 * attempted.
2209 *
2210 * setup_url - A URL that can be used to send the user to the server
2211 * to set up for authentication. The user should be redirected in to
2212 * the setup_url, either in the current window or in a new browser
2213 * window. Null in OpenID 2.
2214 *
2215 * status - Auth_OpenID_SETUP_NEEDED.
2216 *
2217 * @package OpenID
2218 */
2219class Auth_OpenID_SetupNeededResponse extends Auth_OpenID_ConsumerResponse {
2220 var $status = Auth_OpenID_SETUP_NEEDED;
2221
2222 function Auth_OpenID_SetupNeededResponse($endpoint,
2223 $setup_url = null)
2224 {
2225 $this->setEndpoint($endpoint);
2226 $this->setup_url = $setup_url;
2227 }
2228}
2229
2230
02231
=== added file 'Auth/OpenID/CryptUtil.php'
--- Auth/OpenID/CryptUtil.php 1970-01-01 00:00:00 +0000
+++ Auth/OpenID/CryptUtil.php 2011-06-19 04:48:34 +0000
@@ -0,0 +1,108 @@
1<?php
2
3/**
4 * CryptUtil: A suite of wrapper utility functions for the OpenID
5 * library.
6 *
7 * PHP versions 4 and 5
8 *
9 * LICENSE: See the COPYING file included in this distribution.
10 *
11 * @access private
12 * @package OpenID
13 * @author JanRain, Inc. <openid@janrain.com>
14 * @copyright 2005-2008 Janrain, Inc.
15 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
16 */
17
18if (!defined('Auth_OpenID_RAND_SOURCE')) {
19 /**
20 * The filename for a source of random bytes. Define this yourself
21 * if you have a different source of randomness.
22 */
23 define('Auth_OpenID_RAND_SOURCE', '/dev/urandom');
24}
25
26class Auth_OpenID_CryptUtil {
27 /**
28 * Get the specified number of random bytes.
29 *
30 * Attempts to use a cryptographically secure (not predictable)
31 * source of randomness if available. If there is no high-entropy
32 * randomness source available, it will fail. As a last resort,
33 * for non-critical systems, define
34 * <code>Auth_OpenID_RAND_SOURCE</code> as <code>null</code>, and
35 * the code will fall back on a pseudo-random number generator.
36 *
37 * @param int $num_bytes The length of the return value
38 * @return string $bytes random bytes
39 */
40 static function getBytes($num_bytes)
41 {
42 static $f = null;
43 $bytes = '';
44 if ($f === null) {
45 if (Auth_OpenID_RAND_SOURCE === null) {
46 $f = false;
47 } else {
48 $f = @fopen(Auth_OpenID_RAND_SOURCE, "r");
49 if ($f === false) {
50 $msg = 'Define Auth_OpenID_RAND_SOURCE as null to ' .
51 ' continue with an insecure random number generator.';
52 trigger_error($msg, E_USER_ERROR);
53 }
54 }
55 }
56 if ($f === false) {
57 // pseudorandom used
58 $bytes = '';
59 for ($i = 0; $i < $num_bytes; $i += 4) {
60 $bytes .= pack('L', mt_rand());
61 }
62 $bytes = substr($bytes, 0, $num_bytes);
63 } else {
64 $bytes = fread($f, $num_bytes);
65 }
66 return $bytes;
67 }
68
69 /**
70 * Produce a string of length random bytes, chosen from chrs. If
71 * $chrs is null, the resulting string may contain any characters.
72 *
73 * @param integer $length The length of the resulting
74 * randomly-generated string
75 * @param string $chrs A string of characters from which to choose
76 * to build the new string
77 * @return string $result A string of randomly-chosen characters
78 * from $chrs
79 */
80 static function randomString($length, $population = null)
81 {
82 if ($population === null) {
83 return Auth_OpenID_CryptUtil::getBytes($length);
84 }
85
86 $popsize = strlen($population);
87
88 if ($popsize > 256) {
89 $msg = 'More than 256 characters supplied to ' . __FUNCTION__;
90 trigger_error($msg, E_USER_ERROR);
91 }
92
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches