Merge lp:ubuntu-uy-website into lp:ubuntu-uy-website/trunk
- dev
- Merge into Desarrollo
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 |
Related bugs: | |
Related blueprints: |
Foro del sitio
(High)
Slide de noticias
(Low)
Formulario para pedido de CD's
(Medium)
Estilo de programacion
(High)
Galeria de Imagenes
(Undefined)
Links de Footer
(High)
Planet
(Undefined)
Update de carĂ¡tulas de eventos realizados
(Undefined)
|
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.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'Auth' |
2 | === added directory 'Auth/OpenID' |
3 | === added file 'Auth/OpenID.php' |
4 | --- Auth/OpenID.php 1970-01-01 00:00:00 +0000 |
5 | +++ Auth/OpenID.php 2011-06-19 04:48:34 +0000 |
6 | @@ -0,0 +1,563 @@ |
7 | +<?php |
8 | + |
9 | +/** |
10 | + * This is the PHP OpenID library by JanRain, Inc. |
11 | + * |
12 | + * This module contains core utility functionality used by the |
13 | + * library. See Consumer.php and Server.php for the consumer and |
14 | + * server implementations. |
15 | + * |
16 | + * PHP versions 4 and 5 |
17 | + * |
18 | + * LICENSE: See the COPYING file included in this distribution. |
19 | + * |
20 | + * @package OpenID |
21 | + * @author JanRain, Inc. <openid@janrain.com> |
22 | + * @copyright 2005-2008 Janrain, Inc. |
23 | + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache |
24 | + */ |
25 | + |
26 | +/** |
27 | + * The library version string |
28 | + */ |
29 | +define('Auth_OpenID_VERSION', '2.2.2'); |
30 | + |
31 | +/** |
32 | + * Require the fetcher code. |
33 | + */ |
34 | +require_once "Auth/Yadis/PlainHTTPFetcher.php"; |
35 | +require_once "Auth/Yadis/ParanoidHTTPFetcher.php"; |
36 | +require_once "Auth/OpenID/BigMath.php"; |
37 | +require_once "Auth/OpenID/URINorm.php"; |
38 | + |
39 | +/** |
40 | + * Status code returned by the server when the only option is to show |
41 | + * an error page, since we do not have enough information to redirect |
42 | + * back to the consumer. The associated value is an error message that |
43 | + * should be displayed on an HTML error page. |
44 | + * |
45 | + * @see Auth_OpenID_Server |
46 | + */ |
47 | +define('Auth_OpenID_LOCAL_ERROR', 'local_error'); |
48 | + |
49 | +/** |
50 | + * Status code returned when there is an error to return in key-value |
51 | + * form to the consumer. The caller should return a 400 Bad Request |
52 | + * response with content-type text/plain and the value as the body. |
53 | + * |
54 | + * @see Auth_OpenID_Server |
55 | + */ |
56 | +define('Auth_OpenID_REMOTE_ERROR', 'remote_error'); |
57 | + |
58 | +/** |
59 | + * Status code returned when there is a key-value form OK response to |
60 | + * the consumer. The value associated with this code is the |
61 | + * response. The caller should return a 200 OK response with |
62 | + * content-type text/plain and the value as the body. |
63 | + * |
64 | + * @see Auth_OpenID_Server |
65 | + */ |
66 | +define('Auth_OpenID_REMOTE_OK', 'remote_ok'); |
67 | + |
68 | +/** |
69 | + * Status code returned when there is a redirect back to the |
70 | + * consumer. The value is the URL to redirect back to. The caller |
71 | + * should return a 302 Found redirect with a Location: header |
72 | + * containing the URL. |
73 | + * |
74 | + * @see Auth_OpenID_Server |
75 | + */ |
76 | +define('Auth_OpenID_REDIRECT', 'redirect'); |
77 | + |
78 | +/** |
79 | + * Status code returned when the caller needs to authenticate the |
80 | + * user. The associated value is a {@link Auth_OpenID_ServerRequest} |
81 | + * object that can be used to complete the authentication. If the user |
82 | + * has taken some authentication action, use the retry() method of the |
83 | + * {@link Auth_OpenID_ServerRequest} object to complete the request. |
84 | + * |
85 | + * @see Auth_OpenID_Server |
86 | + */ |
87 | +define('Auth_OpenID_DO_AUTH', 'do_auth'); |
88 | + |
89 | +/** |
90 | + * Status code returned when there were no OpenID arguments |
91 | + * passed. This code indicates that the caller should return a 200 OK |
92 | + * response and display an HTML page that says that this is an OpenID |
93 | + * server endpoint. |
94 | + * |
95 | + * @see Auth_OpenID_Server |
96 | + */ |
97 | +define('Auth_OpenID_DO_ABOUT', 'do_about'); |
98 | + |
99 | +/** |
100 | + * Defines for regexes and format checking. |
101 | + */ |
102 | +define('Auth_OpenID_letters', |
103 | + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); |
104 | + |
105 | +define('Auth_OpenID_digits', |
106 | + "0123456789"); |
107 | + |
108 | +define('Auth_OpenID_punct', |
109 | + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"); |
110 | + |
111 | +Auth_OpenID_include_init(); |
112 | + |
113 | +/** |
114 | + * The OpenID utility function class. |
115 | + * |
116 | + * @package OpenID |
117 | + * @access private |
118 | + */ |
119 | +class Auth_OpenID { |
120 | + |
121 | + /** |
122 | + * Return true if $thing is an Auth_OpenID_FailureResponse object; |
123 | + * false if not. |
124 | + * |
125 | + * @access private |
126 | + */ |
127 | + static function isFailure($thing) |
128 | + { |
129 | + return is_a($thing, 'Auth_OpenID_FailureResponse'); |
130 | + } |
131 | + |
132 | + /** |
133 | + * Gets the query data from the server environment based on the |
134 | + * request method used. If GET was used, this looks at |
135 | + * $_SERVER['QUERY_STRING'] directly. If POST was used, this |
136 | + * fetches data from the special php://input file stream. |
137 | + * |
138 | + * Returns an associative array of the query arguments. |
139 | + * |
140 | + * Skips invalid key/value pairs (i.e. keys with no '=value' |
141 | + * portion). |
142 | + * |
143 | + * Returns an empty array if neither GET nor POST was used, or if |
144 | + * POST was used but php://input cannot be opened. |
145 | + * |
146 | + * See background: |
147 | + * http://lists.openidenabled.com/pipermail/dev/2007-March/000395.html |
148 | + * |
149 | + * @access private |
150 | + */ |
151 | + static function getQuery($query_str=null) |
152 | + { |
153 | + $data = array(); |
154 | + |
155 | + if ($query_str !== null) { |
156 | + $data = Auth_OpenID::params_from_string($query_str); |
157 | + } else if (!array_key_exists('REQUEST_METHOD', $_SERVER)) { |
158 | + // Do nothing. |
159 | + } else { |
160 | + // XXX HACK FIXME HORRIBLE. |
161 | + // |
162 | + // POSTing to a URL with query parameters is acceptable, but |
163 | + // we don't have a clean way to distinguish those parameters |
164 | + // when we need to do things like return_to verification |
165 | + // which only want to look at one kind of parameter. We're |
166 | + // going to emulate the behavior of some other environments |
167 | + // by defaulting to GET and overwriting with POST if POST |
168 | + // data is available. |
169 | + $data = Auth_OpenID::params_from_string($_SERVER['QUERY_STRING']); |
170 | + |
171 | + if ($_SERVER['REQUEST_METHOD'] == 'POST') { |
172 | + $str = file_get_contents('php://input'); |
173 | + |
174 | + if ($str === false) { |
175 | + $post = array(); |
176 | + } else { |
177 | + $post = Auth_OpenID::params_from_string($str); |
178 | + } |
179 | + |
180 | + $data = array_merge($data, $post); |
181 | + } |
182 | + } |
183 | + |
184 | + return $data; |
185 | + } |
186 | + |
187 | + static function params_from_string($str) |
188 | + { |
189 | + $chunks = explode("&", $str); |
190 | + |
191 | + $data = array(); |
192 | + foreach ($chunks as $chunk) { |
193 | + $parts = explode("=", $chunk, 2); |
194 | + |
195 | + if (count($parts) != 2) { |
196 | + continue; |
197 | + } |
198 | + |
199 | + list($k, $v) = $parts; |
200 | + $data[urldecode($k)] = urldecode($v); |
201 | + } |
202 | + |
203 | + return $data; |
204 | + } |
205 | + |
206 | + /** |
207 | + * Create dir_name as a directory if it does not exist. If it |
208 | + * exists, make sure that it is, in fact, a directory. Returns |
209 | + * true if the operation succeeded; false if not. |
210 | + * |
211 | + * @access private |
212 | + */ |
213 | + static function ensureDir($dir_name) |
214 | + { |
215 | + if (is_dir($dir_name) || @mkdir($dir_name)) { |
216 | + return true; |
217 | + } else { |
218 | + $parent_dir = dirname($dir_name); |
219 | + |
220 | + // Terminal case; there is no parent directory to create. |
221 | + if ($parent_dir == $dir_name) { |
222 | + return true; |
223 | + } |
224 | + |
225 | + return (Auth_OpenID::ensureDir($parent_dir) && @mkdir($dir_name)); |
226 | + } |
227 | + } |
228 | + |
229 | + /** |
230 | + * Adds a string prefix to all values of an array. Returns a new |
231 | + * array containing the prefixed values. |
232 | + * |
233 | + * @access private |
234 | + */ |
235 | + static function addPrefix($values, $prefix) |
236 | + { |
237 | + $new_values = array(); |
238 | + foreach ($values as $s) { |
239 | + $new_values[] = $prefix . $s; |
240 | + } |
241 | + return $new_values; |
242 | + } |
243 | + |
244 | + /** |
245 | + * Convenience function for getting array values. Given an array |
246 | + * $arr and a key $key, get the corresponding value from the array |
247 | + * or return $default if the key is absent. |
248 | + * |
249 | + * @access private |
250 | + */ |
251 | + static function arrayGet($arr, $key, $fallback = null) |
252 | + { |
253 | + if (is_array($arr)) { |
254 | + if (array_key_exists($key, $arr)) { |
255 | + return $arr[$key]; |
256 | + } else { |
257 | + return $fallback; |
258 | + } |
259 | + } else { |
260 | + trigger_error("Auth_OpenID::arrayGet (key = ".$key.") expected " . |
261 | + "array as first parameter, got " . |
262 | + gettype($arr), E_USER_WARNING); |
263 | + |
264 | + return false; |
265 | + } |
266 | + } |
267 | + |
268 | + /** |
269 | + * Replacement for PHP's broken parse_str. |
270 | + */ |
271 | + static function parse_str($query) |
272 | + { |
273 | + if ($query === null) { |
274 | + return null; |
275 | + } |
276 | + |
277 | + $parts = explode('&', $query); |
278 | + |
279 | + $new_parts = array(); |
280 | + for ($i = 0; $i < count($parts); $i++) { |
281 | + $pair = explode('=', $parts[$i]); |
282 | + |
283 | + if (count($pair) != 2) { |
284 | + continue; |
285 | + } |
286 | + |
287 | + list($key, $value) = $pair; |
288 | + $new_parts[urldecode($key)] = urldecode($value); |
289 | + } |
290 | + |
291 | + return $new_parts; |
292 | + } |
293 | + |
294 | + /** |
295 | + * Implements the PHP 5 'http_build_query' functionality. |
296 | + * |
297 | + * @access private |
298 | + * @param array $data Either an array key/value pairs or an array |
299 | + * of arrays, each of which holding two values: a key and a value, |
300 | + * sequentially. |
301 | + * @return string $result The result of url-encoding the key/value |
302 | + * pairs from $data into a URL query string |
303 | + * (e.g. "username=bob&id=56"). |
304 | + */ |
305 | + static function httpBuildQuery($data) |
306 | + { |
307 | + $pairs = array(); |
308 | + foreach ($data as $key => $value) { |
309 | + if (is_array($value)) { |
310 | + $pairs[] = urlencode($value[0])."=".urlencode($value[1]); |
311 | + } else { |
312 | + $pairs[] = urlencode($key)."=".urlencode($value); |
313 | + } |
314 | + } |
315 | + return implode("&", $pairs); |
316 | + } |
317 | + |
318 | + /** |
319 | + * "Appends" query arguments onto a URL. The URL may or may not |
320 | + * already have arguments (following a question mark). |
321 | + * |
322 | + * @access private |
323 | + * @param string $url A URL, which may or may not already have |
324 | + * arguments. |
325 | + * @param array $args Either an array key/value pairs or an array of |
326 | + * arrays, each of which holding two values: a key and a value, |
327 | + * sequentially. If $args is an ordinary key/value array, the |
328 | + * parameters will be added to the URL in sorted alphabetical order; |
329 | + * if $args is an array of arrays, their order will be preserved. |
330 | + * @return string $url The original URL with the new parameters added. |
331 | + * |
332 | + */ |
333 | + static function appendArgs($url, $args) |
334 | + { |
335 | + if (count($args) == 0) { |
336 | + return $url; |
337 | + } |
338 | + |
339 | + // Non-empty array; if it is an array of arrays, use |
340 | + // multisort; otherwise use sort. |
341 | + if (array_key_exists(0, $args) && |
342 | + is_array($args[0])) { |
343 | + // Do nothing here. |
344 | + } else { |
345 | + $keys = array_keys($args); |
346 | + sort($keys); |
347 | + $new_args = array(); |
348 | + foreach ($keys as $key) { |
349 | + $new_args[] = array($key, $args[$key]); |
350 | + } |
351 | + $args = $new_args; |
352 | + } |
353 | + |
354 | + $sep = '?'; |
355 | + if (strpos($url, '?') !== false) { |
356 | + $sep = '&'; |
357 | + } |
358 | + |
359 | + return $url . $sep . Auth_OpenID::httpBuildQuery($args); |
360 | + } |
361 | + |
362 | + /** |
363 | + * Implements python's urlunparse, which is not available in PHP. |
364 | + * Given the specified components of a URL, this function rebuilds |
365 | + * and returns the URL. |
366 | + * |
367 | + * @access private |
368 | + * @param string $scheme The scheme (e.g. 'http'). Defaults to 'http'. |
369 | + * @param string $host The host. Required. |
370 | + * @param string $port The port. |
371 | + * @param string $path The path. |
372 | + * @param string $query The query. |
373 | + * @param string $fragment The fragment. |
374 | + * @return string $url The URL resulting from assembling the |
375 | + * specified components. |
376 | + */ |
377 | + static function urlunparse($scheme, $host, $port = null, $path = '/', |
378 | + $query = '', $fragment = '') |
379 | + { |
380 | + |
381 | + if (!$scheme) { |
382 | + $scheme = 'http'; |
383 | + } |
384 | + |
385 | + if (!$host) { |
386 | + return false; |
387 | + } |
388 | + |
389 | + if (!$path) { |
390 | + $path = ''; |
391 | + } |
392 | + |
393 | + $result = $scheme . "://" . $host; |
394 | + |
395 | + if ($port) { |
396 | + $result .= ":" . $port; |
397 | + } |
398 | + |
399 | + $result .= $path; |
400 | + |
401 | + if ($query) { |
402 | + $result .= "?" . $query; |
403 | + } |
404 | + |
405 | + if ($fragment) { |
406 | + $result .= "#" . $fragment; |
407 | + } |
408 | + |
409 | + return $result; |
410 | + } |
411 | + |
412 | + /** |
413 | + * Given a URL, this "normalizes" it by adding a trailing slash |
414 | + * and / or a leading http:// scheme where necessary. Returns |
415 | + * null if the original URL is malformed and cannot be normalized. |
416 | + * |
417 | + * @access private |
418 | + * @param string $url The URL to be normalized. |
419 | + * @return mixed $new_url The URL after normalization, or null if |
420 | + * $url was malformed. |
421 | + */ |
422 | + static function normalizeUrl($url) |
423 | + { |
424 | + @$parsed = parse_url($url); |
425 | + |
426 | + if (!$parsed) { |
427 | + return null; |
428 | + } |
429 | + |
430 | + if (isset($parsed['scheme']) && |
431 | + isset($parsed['host'])) { |
432 | + $scheme = strtolower($parsed['scheme']); |
433 | + if (!in_array($scheme, array('http', 'https'))) { |
434 | + return null; |
435 | + } |
436 | + } else { |
437 | + $url = 'http://' . $url; |
438 | + } |
439 | + |
440 | + $normalized = Auth_OpenID_urinorm($url); |
441 | + if ($normalized === null) { |
442 | + return null; |
443 | + } |
444 | + list($defragged, $frag) = Auth_OpenID::urldefrag($normalized); |
445 | + return $defragged; |
446 | + } |
447 | + |
448 | + /** |
449 | + * Replacement (wrapper) for PHP's intval() because it's broken. |
450 | + * |
451 | + * @access private |
452 | + */ |
453 | + static function intval($value) |
454 | + { |
455 | + $re = "/^\\d+$/"; |
456 | + |
457 | + if (!preg_match($re, $value)) { |
458 | + return false; |
459 | + } |
460 | + |
461 | + return intval($value); |
462 | + } |
463 | + |
464 | + /** |
465 | + * Count the number of bytes in a string independently of |
466 | + * multibyte support conditions. |
467 | + * |
468 | + * @param string $str The string of bytes to count. |
469 | + * @return int The number of bytes in $str. |
470 | + */ |
471 | + static function bytes($str) |
472 | + { |
473 | + return strlen(bin2hex($str)) / 2; |
474 | + } |
475 | + |
476 | + /** |
477 | + * Get the bytes in a string independently of multibyte support |
478 | + * conditions. |
479 | + */ |
480 | + static function toBytes($str) |
481 | + { |
482 | + $hex = bin2hex($str); |
483 | + |
484 | + if (!$hex) { |
485 | + return array(); |
486 | + } |
487 | + |
488 | + $b = array(); |
489 | + for ($i = 0; $i < strlen($hex); $i += 2) { |
490 | + $b[] = chr(base_convert(substr($hex, $i, 2), 16, 10)); |
491 | + } |
492 | + |
493 | + return $b; |
494 | + } |
495 | + |
496 | + static function urldefrag($url) |
497 | + { |
498 | + $parts = explode("#", $url, 2); |
499 | + |
500 | + if (count($parts) == 1) { |
501 | + return array($parts[0], ""); |
502 | + } else { |
503 | + return $parts; |
504 | + } |
505 | + } |
506 | + |
507 | + static function filter($callback, &$sequence) |
508 | + { |
509 | + $result = array(); |
510 | + |
511 | + foreach ($sequence as $item) { |
512 | + if (call_user_func_array($callback, array($item))) { |
513 | + $result[] = $item; |
514 | + } |
515 | + } |
516 | + |
517 | + return $result; |
518 | + } |
519 | + |
520 | + static function update(&$dest, &$src) |
521 | + { |
522 | + foreach ($src as $k => $v) { |
523 | + $dest[$k] = $v; |
524 | + } |
525 | + } |
526 | + |
527 | + /** |
528 | + * Wrap PHP's standard error_log functionality. Use this to |
529 | + * perform all logging. It will interpolate any additional |
530 | + * arguments into the format string before logging. |
531 | + * |
532 | + * @param string $format_string The sprintf format for the message |
533 | + */ |
534 | + static function log($format_string) |
535 | + { |
536 | + $args = func_get_args(); |
537 | + $message = call_user_func_array('sprintf', $args); |
538 | + error_log($message); |
539 | + } |
540 | + |
541 | + static function autoSubmitHTML($form, $title="OpenId transaction in progress") |
542 | + { |
543 | + return("<html>". |
544 | + "<head><title>". |
545 | + $title . |
546 | + "</title></head>". |
547 | + "<body onload='document.forms[0].submit();'>". |
548 | + $form . |
549 | + "<script>". |
550 | + "var elements = document.forms[0].elements;". |
551 | + "for (var i = 0; i < elements.length; i++) {". |
552 | + " elements[i].style.display = \"none\";". |
553 | + "}". |
554 | + "</script>". |
555 | + "</body>". |
556 | + "</html>"); |
557 | + } |
558 | +} |
559 | + |
560 | +/* |
561 | + * Function to run when this file is included. |
562 | + * Abstracted to a function to make life easier |
563 | + * for some PHP optimizers. |
564 | + */ |
565 | +function Auth_OpenID_include_init() { |
566 | + if (Auth_OpenID_getMathLib() === null) { |
567 | + Auth_OpenID_setNoMathSupport(); |
568 | + } |
569 | +} |
570 | |
571 | === added file 'Auth/OpenID/AX.php' |
572 | --- Auth/OpenID/AX.php 1970-01-01 00:00:00 +0000 |
573 | +++ Auth/OpenID/AX.php 2011-06-19 04:48:34 +0000 |
574 | @@ -0,0 +1,1022 @@ |
575 | +<?php |
576 | + |
577 | +/** |
578 | + * Implements the OpenID attribute exchange specification, version 1.0 |
579 | + * as of svn revision 370 from openid.net svn. |
580 | + * |
581 | + * @package OpenID |
582 | + */ |
583 | + |
584 | +/** |
585 | + * Require utility classes and functions for the consumer. |
586 | + */ |
587 | +require_once "Auth/OpenID/Extension.php"; |
588 | +require_once "Auth/OpenID/Message.php"; |
589 | +require_once "Auth/OpenID/TrustRoot.php"; |
590 | + |
591 | +define('Auth_OpenID_AX_NS_URI', |
592 | + 'http://openid.net/srv/ax/1.0'); |
593 | + |
594 | +// Use this as the 'count' value for an attribute in a FetchRequest to |
595 | +// ask for as many values as the OP can provide. |
596 | +define('Auth_OpenID_AX_UNLIMITED_VALUES', 'unlimited'); |
597 | + |
598 | +// Minimum supported alias length in characters. Here for |
599 | +// completeness. |
600 | +define('Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH', 32); |
601 | + |
602 | +/** |
603 | + * AX utility class. |
604 | + * |
605 | + * @package OpenID |
606 | + */ |
607 | +class Auth_OpenID_AX { |
608 | + /** |
609 | + * @param mixed $thing Any object which may be an |
610 | + * Auth_OpenID_AX_Error object. |
611 | + * |
612 | + * @return bool true if $thing is an Auth_OpenID_AX_Error; false |
613 | + * if not. |
614 | + */ |
615 | + static function isError($thing) |
616 | + { |
617 | + return is_a($thing, 'Auth_OpenID_AX_Error'); |
618 | + } |
619 | +} |
620 | + |
621 | +/** |
622 | + * Check an alias for invalid characters; raise AXError if any are |
623 | + * found. Return None if the alias is valid. |
624 | + */ |
625 | +function Auth_OpenID_AX_checkAlias($alias) |
626 | +{ |
627 | + if (strpos($alias, ',') !== false) { |
628 | + return new Auth_OpenID_AX_Error(sprintf( |
629 | + "Alias %s must not contain comma", $alias)); |
630 | + } |
631 | + if (strpos($alias, '.') !== false) { |
632 | + return new Auth_OpenID_AX_Error(sprintf( |
633 | + "Alias %s must not contain period", $alias)); |
634 | + } |
635 | + |
636 | + return true; |
637 | +} |
638 | + |
639 | +/** |
640 | + * Results from data that does not meet the attribute exchange 1.0 |
641 | + * specification |
642 | + * |
643 | + * @package OpenID |
644 | + */ |
645 | +class Auth_OpenID_AX_Error { |
646 | + function Auth_OpenID_AX_Error($message=null) |
647 | + { |
648 | + $this->message = $message; |
649 | + } |
650 | +} |
651 | + |
652 | +/** |
653 | + * Abstract class containing common code for attribute exchange |
654 | + * messages. |
655 | + * |
656 | + * @package OpenID |
657 | + */ |
658 | +class Auth_OpenID_AX_Message extends Auth_OpenID_Extension { |
659 | + /** |
660 | + * ns_alias: The preferred namespace alias for attribute exchange |
661 | + * messages |
662 | + */ |
663 | + var $ns_alias = 'ax'; |
664 | + |
665 | + /** |
666 | + * mode: The type of this attribute exchange message. This must be |
667 | + * overridden in subclasses. |
668 | + */ |
669 | + var $mode = null; |
670 | + |
671 | + var $ns_uri = Auth_OpenID_AX_NS_URI; |
672 | + |
673 | + /** |
674 | + * Return Auth_OpenID_AX_Error if the mode in the attribute |
675 | + * exchange arguments does not match what is expected for this |
676 | + * class; true otherwise. |
677 | + * |
678 | + * @access private |
679 | + */ |
680 | + function _checkMode($ax_args) |
681 | + { |
682 | + $mode = Auth_OpenID::arrayGet($ax_args, 'mode'); |
683 | + if ($mode != $this->mode) { |
684 | + return new Auth_OpenID_AX_Error( |
685 | + sprintf( |
686 | + "Expected mode '%s'; got '%s'", |
687 | + $this->mode, $mode)); |
688 | + } |
689 | + |
690 | + return true; |
691 | + } |
692 | + |
693 | + /** |
694 | + * Return a set of attribute exchange arguments containing the |
695 | + * basic information that must be in every attribute exchange |
696 | + * message. |
697 | + * |
698 | + * @access private |
699 | + */ |
700 | + function _newArgs() |
701 | + { |
702 | + return array('mode' => $this->mode); |
703 | + } |
704 | +} |
705 | + |
706 | +/** |
707 | + * Represents a single attribute in an attribute exchange |
708 | + * request. This should be added to an AXRequest object in order to |
709 | + * request the attribute. |
710 | + * |
711 | + * @package OpenID |
712 | + */ |
713 | +class Auth_OpenID_AX_AttrInfo { |
714 | + /** |
715 | + * Construct an attribute information object. Do not call this |
716 | + * directly; call make(...) instead. |
717 | + * |
718 | + * @param string $type_uri The type URI for this attribute. |
719 | + * |
720 | + * @param int $count The number of values of this type to request. |
721 | + * |
722 | + * @param bool $required Whether the attribute will be marked as |
723 | + * required in the request. |
724 | + * |
725 | + * @param string $alias The name that should be given to this |
726 | + * attribute in the request. |
727 | + */ |
728 | + function Auth_OpenID_AX_AttrInfo($type_uri, $count, $required, |
729 | + $alias) |
730 | + { |
731 | + /** |
732 | + * required: Whether the attribute will be marked as required |
733 | + * when presented to the subject of the attribute exchange |
734 | + * request. |
735 | + */ |
736 | + $this->required = $required; |
737 | + |
738 | + /** |
739 | + * count: How many values of this type to request from the |
740 | + * subject. Defaults to one. |
741 | + */ |
742 | + $this->count = $count; |
743 | + |
744 | + /** |
745 | + * type_uri: The identifier that determines what the attribute |
746 | + * represents and how it is serialized. For example, one type |
747 | + * URI representing dates could represent a Unix timestamp in |
748 | + * base 10 and another could represent a human-readable |
749 | + * string. |
750 | + */ |
751 | + $this->type_uri = $type_uri; |
752 | + |
753 | + /** |
754 | + * alias: The name that should be given to this attribute in |
755 | + * the request. If it is not supplied, a generic name will be |
756 | + * assigned. For example, if you want to call a Unix timestamp |
757 | + * value 'tstamp', set its alias to that value. If two |
758 | + * attributes in the same message request to use the same |
759 | + * alias, the request will fail to be generated. |
760 | + */ |
761 | + $this->alias = $alias; |
762 | + } |
763 | + |
764 | + /** |
765 | + * Construct an attribute information object. For parameter |
766 | + * details, see the constructor. |
767 | + */ |
768 | + static function make($type_uri, $count=1, $required=false, |
769 | + $alias=null) |
770 | + { |
771 | + if ($alias !== null) { |
772 | + $result = Auth_OpenID_AX_checkAlias($alias); |
773 | + |
774 | + if (Auth_OpenID_AX::isError($result)) { |
775 | + return $result; |
776 | + } |
777 | + } |
778 | + |
779 | + return new Auth_OpenID_AX_AttrInfo($type_uri, $count, $required, |
780 | + $alias); |
781 | + } |
782 | + |
783 | + /** |
784 | + * When processing a request for this attribute, the OP should |
785 | + * call this method to determine whether all available attribute |
786 | + * values were requested. If self.count == UNLIMITED_VALUES, this |
787 | + * returns True. Otherwise this returns False, in which case |
788 | + * self.count is an integer. |
789 | + */ |
790 | + function wantsUnlimitedValues() |
791 | + { |
792 | + return $this->count === Auth_OpenID_AX_UNLIMITED_VALUES; |
793 | + } |
794 | +} |
795 | + |
796 | +/** |
797 | + * Given a namespace mapping and a string containing a comma-separated |
798 | + * list of namespace aliases, return a list of type URIs that |
799 | + * correspond to those aliases. |
800 | + * |
801 | + * @param $namespace_map The mapping from namespace URI to alias |
802 | + * @param $alias_list_s The string containing the comma-separated |
803 | + * list of aliases. May also be None for convenience. |
804 | + * |
805 | + * @return $seq The list of namespace URIs that corresponds to the |
806 | + * supplied list of aliases. If the string was zero-length or None, an |
807 | + * empty list will be returned. |
808 | + * |
809 | + * return null If an alias is present in the list of aliases but |
810 | + * is not present in the namespace map. |
811 | + */ |
812 | +function Auth_OpenID_AX_toTypeURIs($namespace_map, $alias_list_s) |
813 | +{ |
814 | + $uris = array(); |
815 | + |
816 | + if ($alias_list_s) { |
817 | + foreach (explode(',', $alias_list_s) as $alias) { |
818 | + $type_uri = $namespace_map->getNamespaceURI($alias); |
819 | + if ($type_uri === null) { |
820 | + // raise KeyError( |
821 | + // 'No type is defined for attribute name %r' % (alias,)) |
822 | + return new Auth_OpenID_AX_Error( |
823 | + sprintf('No type is defined for attribute name %s', |
824 | + $alias) |
825 | + ); |
826 | + } else { |
827 | + $uris[] = $type_uri; |
828 | + } |
829 | + } |
830 | + } |
831 | + |
832 | + return $uris; |
833 | +} |
834 | + |
835 | +/** |
836 | + * An attribute exchange 'fetch_request' message. This message is sent |
837 | + * by a relying party when it wishes to obtain attributes about the |
838 | + * subject of an OpenID authentication request. |
839 | + * |
840 | + * @package OpenID |
841 | + */ |
842 | +class Auth_OpenID_AX_FetchRequest extends Auth_OpenID_AX_Message { |
843 | + |
844 | + var $mode = 'fetch_request'; |
845 | + |
846 | + function Auth_OpenID_AX_FetchRequest($update_url=null) |
847 | + { |
848 | + /** |
849 | + * requested_attributes: The attributes that have been |
850 | + * requested thus far, indexed by the type URI. |
851 | + */ |
852 | + $this->requested_attributes = array(); |
853 | + |
854 | + /** |
855 | + * update_url: A URL that will accept responses for this |
856 | + * attribute exchange request, even in the absence of the user |
857 | + * who made this request. |
858 | + */ |
859 | + $this->update_url = $update_url; |
860 | + } |
861 | + |
862 | + /** |
863 | + * Add an attribute to this attribute exchange request. |
864 | + * |
865 | + * @param attribute: The attribute that is being requested |
866 | + * @return true on success, false when the requested attribute is |
867 | + * already present in this fetch request. |
868 | + */ |
869 | + function add($attribute) |
870 | + { |
871 | + if ($this->contains($attribute->type_uri)) { |
872 | + return new Auth_OpenID_AX_Error( |
873 | + sprintf("The attribute %s has already been requested", |
874 | + $attribute->type_uri)); |
875 | + } |
876 | + |
877 | + $this->requested_attributes[$attribute->type_uri] = $attribute; |
878 | + |
879 | + return true; |
880 | + } |
881 | + |
882 | + /** |
883 | + * Get the serialized form of this attribute fetch request. |
884 | + * |
885 | + * @returns Auth_OpenID_AX_FetchRequest The fetch request message parameters |
886 | + */ |
887 | + function getExtensionArgs() |
888 | + { |
889 | + $aliases = new Auth_OpenID_NamespaceMap(); |
890 | + |
891 | + $required = array(); |
892 | + $if_available = array(); |
893 | + |
894 | + $ax_args = $this->_newArgs(); |
895 | + |
896 | + foreach ($this->requested_attributes as $type_uri => $attribute) { |
897 | + if ($attribute->alias === null) { |
898 | + $alias = $aliases->add($type_uri); |
899 | + } else { |
900 | + $alias = $aliases->addAlias($type_uri, $attribute->alias); |
901 | + |
902 | + if ($alias === null) { |
903 | + return new Auth_OpenID_AX_Error( |
904 | + sprintf("Could not add alias %s for URI %s", |
905 | + $attribute->alias, $type_uri |
906 | + )); |
907 | + } |
908 | + } |
909 | + |
910 | + if ($attribute->required) { |
911 | + $required[] = $alias; |
912 | + } else { |
913 | + $if_available[] = $alias; |
914 | + } |
915 | + |
916 | + if ($attribute->count != 1) { |
917 | + $ax_args['count.' . $alias] = strval($attribute->count); |
918 | + } |
919 | + |
920 | + $ax_args['type.' . $alias] = $type_uri; |
921 | + } |
922 | + |
923 | + if ($required) { |
924 | + $ax_args['required'] = implode(',', $required); |
925 | + } |
926 | + |
927 | + if ($if_available) { |
928 | + $ax_args['if_available'] = implode(',', $if_available); |
929 | + } |
930 | + |
931 | + return $ax_args; |
932 | + } |
933 | + |
934 | + /** |
935 | + * Get the type URIs for all attributes that have been marked as |
936 | + * required. |
937 | + * |
938 | + * @return A list of the type URIs for attributes that have been |
939 | + * marked as required. |
940 | + */ |
941 | + function getRequiredAttrs() |
942 | + { |
943 | + $required = array(); |
944 | + foreach ($this->requested_attributes as $type_uri => $attribute) { |
945 | + if ($attribute->required) { |
946 | + $required[] = $type_uri; |
947 | + } |
948 | + } |
949 | + |
950 | + return $required; |
951 | + } |
952 | + |
953 | + /** |
954 | + * Extract a FetchRequest from an OpenID message |
955 | + * |
956 | + * @param request: The OpenID request containing the attribute |
957 | + * fetch request |
958 | + * |
959 | + * @returns mixed An Auth_OpenID_AX_Error or the |
960 | + * Auth_OpenID_AX_FetchRequest extracted from the request message if |
961 | + * successful |
962 | + */ |
963 | + static function fromOpenIDRequest($request) |
964 | + { |
965 | + $m = $request->message; |
966 | + $obj = new Auth_OpenID_AX_FetchRequest(); |
967 | + $ax_args = $m->getArgs($obj->ns_uri); |
968 | + |
969 | + $result = $obj->parseExtensionArgs($ax_args); |
970 | + |
971 | + if (Auth_OpenID_AX::isError($result)) { |
972 | + return $result; |
973 | + } |
974 | + |
975 | + if ($obj->update_url) { |
976 | + // Update URL must match the openid.realm of the |
977 | + // underlying OpenID 2 message. |
978 | + $realm = $m->getArg(Auth_OpenID_OPENID_NS, 'realm', |
979 | + $m->getArg( |
980 | + Auth_OpenID_OPENID_NS, |
981 | + 'return_to')); |
982 | + |
983 | + if (!$realm) { |
984 | + $obj = new Auth_OpenID_AX_Error( |
985 | + sprintf("Cannot validate update_url %s " . |
986 | + "against absent realm", $obj->update_url)); |
987 | + } else if (!Auth_OpenID_TrustRoot::match($realm, |
988 | + $obj->update_url)) { |
989 | + $obj = new Auth_OpenID_AX_Error( |
990 | + sprintf("Update URL %s failed validation against realm %s", |
991 | + $obj->update_url, $realm)); |
992 | + } |
993 | + } |
994 | + |
995 | + return $obj; |
996 | + } |
997 | + |
998 | + /** |
999 | + * Given attribute exchange arguments, populate this FetchRequest. |
1000 | + * |
1001 | + * @return $result Auth_OpenID_AX_Error if the data to be parsed |
1002 | + * does not follow the attribute exchange specification. At least |
1003 | + * when 'if_available' or 'required' is not specified for a |
1004 | + * particular attribute type. Returns true otherwise. |
1005 | + */ |
1006 | + function parseExtensionArgs($ax_args) |
1007 | + { |
1008 | + $result = $this->_checkMode($ax_args); |
1009 | + if (Auth_OpenID_AX::isError($result)) { |
1010 | + return $result; |
1011 | + } |
1012 | + |
1013 | + $aliases = new Auth_OpenID_NamespaceMap(); |
1014 | + |
1015 | + foreach ($ax_args as $key => $value) { |
1016 | + if (strpos($key, 'type.') === 0) { |
1017 | + $alias = substr($key, 5); |
1018 | + $type_uri = $value; |
1019 | + |
1020 | + $alias = $aliases->addAlias($type_uri, $alias); |
1021 | + |
1022 | + if ($alias === null) { |
1023 | + return new Auth_OpenID_AX_Error( |
1024 | + sprintf("Could not add alias %s for URI %s", |
1025 | + $alias, $type_uri) |
1026 | + ); |
1027 | + } |
1028 | + |
1029 | + $count_s = Auth_OpenID::arrayGet($ax_args, 'count.' . $alias); |
1030 | + if ($count_s) { |
1031 | + $count = Auth_OpenID::intval($count_s); |
1032 | + if (($count === false) && |
1033 | + ($count_s === Auth_OpenID_AX_UNLIMITED_VALUES)) { |
1034 | + $count = $count_s; |
1035 | + } |
1036 | + } else { |
1037 | + $count = 1; |
1038 | + } |
1039 | + |
1040 | + if ($count === false) { |
1041 | + return new Auth_OpenID_AX_Error( |
1042 | + sprintf("Integer value expected for %s, got %s", |
1043 | + 'count.' . $alias, $count_s)); |
1044 | + } |
1045 | + |
1046 | + $attrinfo = Auth_OpenID_AX_AttrInfo::make($type_uri, $count, |
1047 | + false, $alias); |
1048 | + |
1049 | + if (Auth_OpenID_AX::isError($attrinfo)) { |
1050 | + return $attrinfo; |
1051 | + } |
1052 | + |
1053 | + $this->add($attrinfo); |
1054 | + } |
1055 | + } |
1056 | + |
1057 | + $required = Auth_OpenID_AX_toTypeURIs($aliases, |
1058 | + Auth_OpenID::arrayGet($ax_args, 'required')); |
1059 | + |
1060 | + foreach ($required as $type_uri) { |
1061 | + $attrib = $this->requested_attributes[$type_uri]; |
1062 | + $attrib->required = true; |
1063 | + } |
1064 | + |
1065 | + $if_available = Auth_OpenID_AX_toTypeURIs($aliases, |
1066 | + Auth_OpenID::arrayGet($ax_args, 'if_available')); |
1067 | + |
1068 | + $all_type_uris = array_merge($required, $if_available); |
1069 | + |
1070 | + foreach ($aliases->iterNamespaceURIs() as $type_uri) { |
1071 | + if (!in_array($type_uri, $all_type_uris)) { |
1072 | + return new Auth_OpenID_AX_Error( |
1073 | + sprintf('Type URI %s was in the request but not ' . |
1074 | + 'present in "required" or "if_available"', |
1075 | + $type_uri)); |
1076 | + |
1077 | + } |
1078 | + } |
1079 | + |
1080 | + $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url'); |
1081 | + |
1082 | + return true; |
1083 | + } |
1084 | + |
1085 | + /** |
1086 | + * Iterate over the AttrInfo objects that are contained in this |
1087 | + * fetch_request. |
1088 | + */ |
1089 | + function iterAttrs() |
1090 | + { |
1091 | + return array_values($this->requested_attributes); |
1092 | + } |
1093 | + |
1094 | + function iterTypes() |
1095 | + { |
1096 | + return array_keys($this->requested_attributes); |
1097 | + } |
1098 | + |
1099 | + /** |
1100 | + * Is the given type URI present in this fetch_request? |
1101 | + */ |
1102 | + function contains($type_uri) |
1103 | + { |
1104 | + return in_array($type_uri, $this->iterTypes()); |
1105 | + } |
1106 | +} |
1107 | + |
1108 | +/** |
1109 | + * An abstract class that implements a message that has attribute keys |
1110 | + * and values. It contains the common code between fetch_response and |
1111 | + * store_request. |
1112 | + * |
1113 | + * @package OpenID |
1114 | + */ |
1115 | +class Auth_OpenID_AX_KeyValueMessage extends Auth_OpenID_AX_Message { |
1116 | + |
1117 | + function Auth_OpenID_AX_KeyValueMessage() |
1118 | + { |
1119 | + $this->data = array(); |
1120 | + } |
1121 | + |
1122 | + /** |
1123 | + * Add a single value for the given attribute type to the |
1124 | + * message. If there are already values specified for this type, |
1125 | + * this value will be sent in addition to the values already |
1126 | + * specified. |
1127 | + * |
1128 | + * @param type_uri: The URI for the attribute |
1129 | + * @param value: The value to add to the response to the relying |
1130 | + * party for this attribute |
1131 | + * @return null |
1132 | + */ |
1133 | + function addValue($type_uri, $value) |
1134 | + { |
1135 | + if (!array_key_exists($type_uri, $this->data)) { |
1136 | + $this->data[$type_uri] = array(); |
1137 | + } |
1138 | + |
1139 | + $values =& $this->data[$type_uri]; |
1140 | + $values[] = $value; |
1141 | + } |
1142 | + |
1143 | + /** |
1144 | + * Set the values for the given attribute type. This replaces any |
1145 | + * values that have already been set for this attribute. |
1146 | + * |
1147 | + * @param type_uri: The URI for the attribute |
1148 | + * @param values: A list of values to send for this attribute. |
1149 | + */ |
1150 | + function setValues($type_uri, &$values) |
1151 | + { |
1152 | + $this->data[$type_uri] =& $values; |
1153 | + } |
1154 | + |
1155 | + /** |
1156 | + * Get the extension arguments for the key/value pairs contained |
1157 | + * in this message. |
1158 | + * |
1159 | + * @param aliases: An alias mapping. Set to None if you don't care |
1160 | + * about the aliases for this request. |
1161 | + * |
1162 | + * @access private |
1163 | + */ |
1164 | + function _getExtensionKVArgs($aliases) |
1165 | + { |
1166 | + if ($aliases === null) { |
1167 | + $aliases = new Auth_OpenID_NamespaceMap(); |
1168 | + } |
1169 | + |
1170 | + $ax_args = array(); |
1171 | + |
1172 | + foreach ($this->data as $type_uri => $values) { |
1173 | + $alias = $aliases->add($type_uri); |
1174 | + |
1175 | + $ax_args['type.' . $alias] = $type_uri; |
1176 | + $ax_args['count.' . $alias] = strval(count($values)); |
1177 | + |
1178 | + foreach ($values as $i => $value) { |
1179 | + $key = sprintf('value.%s.%d', $alias, $i + 1); |
1180 | + $ax_args[$key] = $value; |
1181 | + } |
1182 | + } |
1183 | + |
1184 | + return $ax_args; |
1185 | + } |
1186 | + |
1187 | + /** |
1188 | + * Parse attribute exchange key/value arguments into this object. |
1189 | + * |
1190 | + * @param ax_args: The attribute exchange fetch_response |
1191 | + * arguments, with namespacing removed. |
1192 | + * |
1193 | + * @return Auth_OpenID_AX_Error or true |
1194 | + */ |
1195 | + function parseExtensionArgs($ax_args) |
1196 | + { |
1197 | + $result = $this->_checkMode($ax_args); |
1198 | + if (Auth_OpenID_AX::isError($result)) { |
1199 | + return $result; |
1200 | + } |
1201 | + |
1202 | + $aliases = new Auth_OpenID_NamespaceMap(); |
1203 | + |
1204 | + foreach ($ax_args as $key => $value) { |
1205 | + if (strpos($key, 'type.') === 0) { |
1206 | + $type_uri = $value; |
1207 | + $alias = substr($key, 5); |
1208 | + |
1209 | + $result = Auth_OpenID_AX_checkAlias($alias); |
1210 | + |
1211 | + if (Auth_OpenID_AX::isError($result)) { |
1212 | + return $result; |
1213 | + } |
1214 | + |
1215 | + $alias = $aliases->addAlias($type_uri, $alias); |
1216 | + |
1217 | + if ($alias === null) { |
1218 | + return new Auth_OpenID_AX_Error( |
1219 | + sprintf("Could not add alias %s for URI %s", |
1220 | + $alias, $type_uri) |
1221 | + ); |
1222 | + } |
1223 | + } |
1224 | + } |
1225 | + |
1226 | + foreach ($aliases->iteritems() as $pair) { |
1227 | + list($type_uri, $alias) = $pair; |
1228 | + |
1229 | + if (array_key_exists('count.' . $alias, $ax_args) && ($ax_args['count.' . $alias] !== Auth_OpenID_AX_UNLIMITED_VALUES)) { |
1230 | + |
1231 | + $count_key = 'count.' . $alias; |
1232 | + $count_s = $ax_args[$count_key]; |
1233 | + |
1234 | + $count = Auth_OpenID::intval($count_s); |
1235 | + |
1236 | + if ($count === false) { |
1237 | + return new Auth_OpenID_AX_Error( |
1238 | + sprintf("Integer value expected for %s, got %s", |
1239 | + 'count. %s' . $alias, $count_s, |
1240 | + Auth_OpenID_AX_UNLIMITED_VALUES) |
1241 | + ); |
1242 | + } |
1243 | + |
1244 | + $values = array(); |
1245 | + for ($i = 1; $i < $count + 1; $i++) { |
1246 | + $value_key = sprintf('value.%s.%d', $alias, $i); |
1247 | + |
1248 | + if (!array_key_exists($value_key, $ax_args)) { |
1249 | + return new Auth_OpenID_AX_Error( |
1250 | + sprintf( |
1251 | + "No value found for key %s", |
1252 | + $value_key)); |
1253 | + } |
1254 | + |
1255 | + $value = $ax_args[$value_key]; |
1256 | + $values[] = $value; |
1257 | + } |
1258 | + } else { |
1259 | + $key = 'value.' . $alias; |
1260 | + |
1261 | + if (!array_key_exists($key, $ax_args)) { |
1262 | + return new Auth_OpenID_AX_Error( |
1263 | + sprintf( |
1264 | + "No value found for key %s", |
1265 | + $key)); |
1266 | + } |
1267 | + |
1268 | + $value = $ax_args['value.' . $alias]; |
1269 | + |
1270 | + if ($value == '') { |
1271 | + $values = array(); |
1272 | + } else { |
1273 | + $values = array($value); |
1274 | + } |
1275 | + } |
1276 | + |
1277 | + $this->data[$type_uri] = $values; |
1278 | + } |
1279 | + |
1280 | + return true; |
1281 | + } |
1282 | + |
1283 | + /** |
1284 | + * Get a single value for an attribute. If no value was sent for |
1285 | + * this attribute, use the supplied default. If there is more than |
1286 | + * one value for this attribute, this method will fail. |
1287 | + * |
1288 | + * @param type_uri: The URI for the attribute |
1289 | + * @param default: The value to return if the attribute was not |
1290 | + * sent in the fetch_response. |
1291 | + * |
1292 | + * @return $value Auth_OpenID_AX_Error on failure or the value of |
1293 | + * the attribute in the fetch_response message, or the default |
1294 | + * supplied |
1295 | + */ |
1296 | + function getSingle($type_uri, $default=null) |
1297 | + { |
1298 | + $values = Auth_OpenID::arrayGet($this->data, $type_uri); |
1299 | + if (!$values) { |
1300 | + return $default; |
1301 | + } else if (count($values) == 1) { |
1302 | + return $values[0]; |
1303 | + } else { |
1304 | + return new Auth_OpenID_AX_Error( |
1305 | + sprintf('More than one value present for %s', |
1306 | + $type_uri) |
1307 | + ); |
1308 | + } |
1309 | + } |
1310 | + |
1311 | + /** |
1312 | + * Get the list of values for this attribute in the |
1313 | + * fetch_response. |
1314 | + * |
1315 | + * XXX: what to do if the values are not present? default |
1316 | + * parameter? this is funny because it's always supposed to return |
1317 | + * a list, so the default may break that, though it's provided by |
1318 | + * the user's code, so it might be okay. If no default is |
1319 | + * supplied, should the return be None or []? |
1320 | + * |
1321 | + * @param type_uri: The URI of the attribute |
1322 | + * |
1323 | + * @return $values The list of values for this attribute in the |
1324 | + * response. May be an empty list. If the attribute was not sent |
1325 | + * in the response, returns Auth_OpenID_AX_Error. |
1326 | + */ |
1327 | + function get($type_uri) |
1328 | + { |
1329 | + if (array_key_exists($type_uri, $this->data)) { |
1330 | + return $this->data[$type_uri]; |
1331 | + } else { |
1332 | + return new Auth_OpenID_AX_Error( |
1333 | + sprintf("Type URI %s not found in response", |
1334 | + $type_uri) |
1335 | + ); |
1336 | + } |
1337 | + } |
1338 | + |
1339 | + /** |
1340 | + * Get the number of responses for a particular attribute in this |
1341 | + * fetch_response message. |
1342 | + * |
1343 | + * @param type_uri: The URI of the attribute |
1344 | + * |
1345 | + * @returns int The number of values sent for this attribute. If |
1346 | + * the attribute was not sent in the response, returns |
1347 | + * Auth_OpenID_AX_Error. |
1348 | + */ |
1349 | + function count($type_uri) |
1350 | + { |
1351 | + if (array_key_exists($type_uri, $this->data)) { |
1352 | + return count($this->get($type_uri)); |
1353 | + } else { |
1354 | + return new Auth_OpenID_AX_Error( |
1355 | + sprintf("Type URI %s not found in response", |
1356 | + $type_uri) |
1357 | + ); |
1358 | + } |
1359 | + } |
1360 | +} |
1361 | + |
1362 | +/** |
1363 | + * A fetch_response attribute exchange message. |
1364 | + * |
1365 | + * @package OpenID |
1366 | + */ |
1367 | +class Auth_OpenID_AX_FetchResponse extends Auth_OpenID_AX_KeyValueMessage { |
1368 | + var $mode = 'fetch_response'; |
1369 | + |
1370 | + function Auth_OpenID_AX_FetchResponse($update_url=null) |
1371 | + { |
1372 | + $this->Auth_OpenID_AX_KeyValueMessage(); |
1373 | + $this->update_url = $update_url; |
1374 | + } |
1375 | + |
1376 | + /** |
1377 | + * Serialize this object into arguments in the attribute exchange |
1378 | + * namespace |
1379 | + * |
1380 | + * @return $args The dictionary of unqualified attribute exchange |
1381 | + * arguments that represent this fetch_response, or |
1382 | + * Auth_OpenID_AX_Error on error. |
1383 | + */ |
1384 | + function getExtensionArgs($request=null) |
1385 | + { |
1386 | + $aliases = new Auth_OpenID_NamespaceMap(); |
1387 | + |
1388 | + $zero_value_types = array(); |
1389 | + |
1390 | + if ($request !== null) { |
1391 | + // Validate the data in the context of the request (the |
1392 | + // same attributes should be present in each, and the |
1393 | + // counts in the response must be no more than the counts |
1394 | + // in the request) |
1395 | + |
1396 | + foreach ($this->data as $type_uri => $unused) { |
1397 | + if (!$request->contains($type_uri)) { |
1398 | + return new Auth_OpenID_AX_Error( |
1399 | + sprintf("Response attribute not present in request: %s", |
1400 | + $type_uri) |
1401 | + ); |
1402 | + } |
1403 | + } |
1404 | + |
1405 | + foreach ($request->iterAttrs() as $attr_info) { |
1406 | + // Copy the aliases from the request so that reading |
1407 | + // the response in light of the request is easier |
1408 | + if ($attr_info->alias === null) { |
1409 | + $aliases->add($attr_info->type_uri); |
1410 | + } else { |
1411 | + $alias = $aliases->addAlias($attr_info->type_uri, |
1412 | + $attr_info->alias); |
1413 | + |
1414 | + if ($alias === null) { |
1415 | + return new Auth_OpenID_AX_Error( |
1416 | + sprintf("Could not add alias %s for URI %s", |
1417 | + $attr_info->alias, $attr_info->type_uri) |
1418 | + ); |
1419 | + } |
1420 | + } |
1421 | + |
1422 | + if (array_key_exists($attr_info->type_uri, $this->data)) { |
1423 | + $values = $this->data[$attr_info->type_uri]; |
1424 | + } else { |
1425 | + $values = array(); |
1426 | + $zero_value_types[] = $attr_info; |
1427 | + } |
1428 | + |
1429 | + if (($attr_info->count != Auth_OpenID_AX_UNLIMITED_VALUES) && |
1430 | + ($attr_info->count < count($values))) { |
1431 | + return new Auth_OpenID_AX_Error( |
1432 | + sprintf("More than the number of requested values " . |
1433 | + "were specified for %s", |
1434 | + $attr_info->type_uri) |
1435 | + ); |
1436 | + } |
1437 | + } |
1438 | + } |
1439 | + |
1440 | + $kv_args = $this->_getExtensionKVArgs($aliases); |
1441 | + |
1442 | + // Add the KV args into the response with the args that are |
1443 | + // unique to the fetch_response |
1444 | + $ax_args = $this->_newArgs(); |
1445 | + |
1446 | + // For each requested attribute, put its type/alias and count |
1447 | + // into the response even if no data were returned. |
1448 | + foreach ($zero_value_types as $attr_info) { |
1449 | + $alias = $aliases->getAlias($attr_info->type_uri); |
1450 | + $kv_args['type.' . $alias] = $attr_info->type_uri; |
1451 | + $kv_args['count.' . $alias] = '0'; |
1452 | + } |
1453 | + |
1454 | + $update_url = null; |
1455 | + if ($request) { |
1456 | + $update_url = $request->update_url; |
1457 | + } else { |
1458 | + $update_url = $this->update_url; |
1459 | + } |
1460 | + |
1461 | + if ($update_url) { |
1462 | + $ax_args['update_url'] = $update_url; |
1463 | + } |
1464 | + |
1465 | + Auth_OpenID::update($ax_args, $kv_args); |
1466 | + |
1467 | + return $ax_args; |
1468 | + } |
1469 | + |
1470 | + /** |
1471 | + * @return $result Auth_OpenID_AX_Error on failure or true on |
1472 | + * success. |
1473 | + */ |
1474 | + function parseExtensionArgs($ax_args) |
1475 | + { |
1476 | + $result = parent::parseExtensionArgs($ax_args); |
1477 | + |
1478 | + if (Auth_OpenID_AX::isError($result)) { |
1479 | + return $result; |
1480 | + } |
1481 | + |
1482 | + $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url'); |
1483 | + |
1484 | + return true; |
1485 | + } |
1486 | + |
1487 | + /** |
1488 | + * Construct a FetchResponse object from an OpenID library |
1489 | + * SuccessResponse object. |
1490 | + * |
1491 | + * @param success_response: A successful id_res response object |
1492 | + * |
1493 | + * @param signed: Whether non-signed args should be processsed. If |
1494 | + * True (the default), only signed arguments will be processsed. |
1495 | + * |
1496 | + * @return $response A FetchResponse containing the data from the |
1497 | + * OpenID message |
1498 | + */ |
1499 | + static function fromSuccessResponse($success_response, $signed=true) |
1500 | + { |
1501 | + $obj = new Auth_OpenID_AX_FetchResponse(); |
1502 | + if ($signed) { |
1503 | + $ax_args = $success_response->getSignedNS($obj->ns_uri); |
1504 | + } else { |
1505 | + $ax_args = $success_response->message->getArgs($obj->ns_uri); |
1506 | + } |
1507 | + if ($ax_args === null || Auth_OpenID::isFailure($ax_args) || |
1508 | + sizeof($ax_args) == 0) { |
1509 | + return null; |
1510 | + } |
1511 | + |
1512 | + $result = $obj->parseExtensionArgs($ax_args); |
1513 | + if (Auth_OpenID_AX::isError($result)) { |
1514 | + #XXX log me |
1515 | + return null; |
1516 | + } |
1517 | + return $obj; |
1518 | + } |
1519 | +} |
1520 | + |
1521 | +/** |
1522 | + * A store request attribute exchange message representation. |
1523 | + * |
1524 | + * @package OpenID |
1525 | + */ |
1526 | +class Auth_OpenID_AX_StoreRequest extends Auth_OpenID_AX_KeyValueMessage { |
1527 | + var $mode = 'store_request'; |
1528 | + |
1529 | + /** |
1530 | + * @param array $aliases The namespace aliases to use when making |
1531 | + * this store response. Leave as None to use defaults. |
1532 | + */ |
1533 | + function getExtensionArgs($aliases=null) |
1534 | + { |
1535 | + $ax_args = $this->_newArgs(); |
1536 | + $kv_args = $this->_getExtensionKVArgs($aliases); |
1537 | + Auth_OpenID::update($ax_args, $kv_args); |
1538 | + return $ax_args; |
1539 | + } |
1540 | +} |
1541 | + |
1542 | +/** |
1543 | + * An indication that the store request was processed along with this |
1544 | + * OpenID transaction. Use make(), NOT the constructor, to create |
1545 | + * response objects. |
1546 | + * |
1547 | + * @package OpenID |
1548 | + */ |
1549 | +class Auth_OpenID_AX_StoreResponse extends Auth_OpenID_AX_Message { |
1550 | + var $SUCCESS_MODE = 'store_response_success'; |
1551 | + var $FAILURE_MODE = 'store_response_failure'; |
1552 | + |
1553 | + /** |
1554 | + * Returns Auth_OpenID_AX_Error on error or an |
1555 | + * Auth_OpenID_AX_StoreResponse object on success. |
1556 | + */ |
1557 | + function make($succeeded=true, $error_message=null) |
1558 | + { |
1559 | + if (($succeeded) && ($error_message !== null)) { |
1560 | + return new Auth_OpenID_AX_Error('An error message may only be '. |
1561 | + 'included in a failing fetch response'); |
1562 | + } |
1563 | + |
1564 | + return new Auth_OpenID_AX_StoreResponse($succeeded, $error_message); |
1565 | + } |
1566 | + |
1567 | + function Auth_OpenID_AX_StoreResponse($succeeded=true, $error_message=null) |
1568 | + { |
1569 | + if ($succeeded) { |
1570 | + $this->mode = $this->SUCCESS_MODE; |
1571 | + } else { |
1572 | + $this->mode = $this->FAILURE_MODE; |
1573 | + } |
1574 | + |
1575 | + $this->error_message = $error_message; |
1576 | + } |
1577 | + |
1578 | + /** |
1579 | + * Was this response a success response? |
1580 | + */ |
1581 | + function succeeded() |
1582 | + { |
1583 | + return $this->mode == $this->SUCCESS_MODE; |
1584 | + } |
1585 | + |
1586 | + function getExtensionArgs() |
1587 | + { |
1588 | + $ax_args = $this->_newArgs(); |
1589 | + if ((!$this->succeeded()) && $this->error_message) { |
1590 | + $ax_args['error'] = $this->error_message; |
1591 | + } |
1592 | + |
1593 | + return $ax_args; |
1594 | + } |
1595 | +} |
1596 | + |
1597 | |
1598 | === added file 'Auth/OpenID/Association.php' |
1599 | --- Auth/OpenID/Association.php 1970-01-01 00:00:00 +0000 |
1600 | +++ Auth/OpenID/Association.php 2011-06-19 04:48:34 +0000 |
1601 | @@ -0,0 +1,610 @@ |
1602 | +<?php |
1603 | + |
1604 | +/** |
1605 | + * This module contains code for dealing with associations between |
1606 | + * consumers and servers. |
1607 | + * |
1608 | + * PHP versions 4 and 5 |
1609 | + * |
1610 | + * LICENSE: See the COPYING file included in this distribution. |
1611 | + * |
1612 | + * @package OpenID |
1613 | + * @author JanRain, Inc. <openid@janrain.com> |
1614 | + * @copyright 2005-2008 Janrain, Inc. |
1615 | + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache |
1616 | + */ |
1617 | + |
1618 | +/** |
1619 | + * @access private |
1620 | + */ |
1621 | +require_once 'Auth/OpenID/CryptUtil.php'; |
1622 | + |
1623 | +/** |
1624 | + * @access private |
1625 | + */ |
1626 | +require_once 'Auth/OpenID/KVForm.php'; |
1627 | + |
1628 | +/** |
1629 | + * @access private |
1630 | + */ |
1631 | +require_once 'Auth/OpenID/HMAC.php'; |
1632 | + |
1633 | +/** |
1634 | + * This class represents an association between a server and a |
1635 | + * consumer. In general, users of this library will never see |
1636 | + * instances of this object. The only exception is if you implement a |
1637 | + * custom {@link Auth_OpenID_OpenIDStore}. |
1638 | + * |
1639 | + * If you do implement such a store, it will need to store the values |
1640 | + * of the handle, secret, issued, lifetime, and assoc_type instance |
1641 | + * variables. |
1642 | + * |
1643 | + * @package OpenID |
1644 | + */ |
1645 | +class Auth_OpenID_Association { |
1646 | + |
1647 | + /** |
1648 | + * This is a HMAC-SHA1 specific value. |
1649 | + * |
1650 | + * @access private |
1651 | + */ |
1652 | + var $SIG_LENGTH = 20; |
1653 | + |
1654 | + /** |
1655 | + * The ordering and name of keys as stored by serialize. |
1656 | + * |
1657 | + * @access private |
1658 | + */ |
1659 | + var $assoc_keys = array( |
1660 | + 'version', |
1661 | + 'handle', |
1662 | + 'secret', |
1663 | + 'issued', |
1664 | + 'lifetime', |
1665 | + 'assoc_type' |
1666 | + ); |
1667 | + |
1668 | + var $_macs = array( |
1669 | + 'HMAC-SHA1' => 'Auth_OpenID_HMACSHA1', |
1670 | + 'HMAC-SHA256' => 'Auth_OpenID_HMACSHA256' |
1671 | + ); |
1672 | + |
1673 | + /** |
1674 | + * This is an alternate constructor (factory method) used by the |
1675 | + * OpenID consumer library to create associations. OpenID store |
1676 | + * implementations shouldn't use this constructor. |
1677 | + * |
1678 | + * @access private |
1679 | + * |
1680 | + * @param integer $expires_in This is the amount of time this |
1681 | + * association is good for, measured in seconds since the |
1682 | + * association was issued. |
1683 | + * |
1684 | + * @param string $handle This is the handle the server gave this |
1685 | + * association. |
1686 | + * |
1687 | + * @param string secret This is the shared secret the server |
1688 | + * generated for this association. |
1689 | + * |
1690 | + * @param assoc_type This is the type of association this |
1691 | + * instance represents. The only valid values of this field at |
1692 | + * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may |
1693 | + * be defined in the future. |
1694 | + * |
1695 | + * @return association An {@link Auth_OpenID_Association} |
1696 | + * instance. |
1697 | + */ |
1698 | + static function fromExpiresIn($expires_in, $handle, $secret, $assoc_type) |
1699 | + { |
1700 | + $issued = time(); |
1701 | + $lifetime = $expires_in; |
1702 | + return new Auth_OpenID_Association($handle, $secret, |
1703 | + $issued, $lifetime, $assoc_type); |
1704 | + } |
1705 | + |
1706 | + /** |
1707 | + * This is the standard constructor for creating an association. |
1708 | + * The library should create all of the necessary associations, so |
1709 | + * this constructor is not part of the external API. |
1710 | + * |
1711 | + * @access private |
1712 | + * |
1713 | + * @param string $handle This is the handle the server gave this |
1714 | + * association. |
1715 | + * |
1716 | + * @param string $secret This is the shared secret the server |
1717 | + * generated for this association. |
1718 | + * |
1719 | + * @param integer $issued This is the time this association was |
1720 | + * issued, in seconds since 00:00 GMT, January 1, 1970. (ie, a |
1721 | + * unix timestamp) |
1722 | + * |
1723 | + * @param integer $lifetime This is the amount of time this |
1724 | + * association is good for, measured in seconds since the |
1725 | + * association was issued. |
1726 | + * |
1727 | + * @param string $assoc_type This is the type of association this |
1728 | + * instance represents. The only valid values of this field at |
1729 | + * this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may |
1730 | + * be defined in the future. |
1731 | + */ |
1732 | + function Auth_OpenID_Association( |
1733 | + $handle, $secret, $issued, $lifetime, $assoc_type) |
1734 | + { |
1735 | + if (!in_array($assoc_type, |
1736 | + Auth_OpenID_getSupportedAssociationTypes(), true)) { |
1737 | + $fmt = 'Unsupported association type (%s)'; |
1738 | + trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR); |
1739 | + } |
1740 | + |
1741 | + $this->handle = $handle; |
1742 | + $this->secret = $secret; |
1743 | + $this->issued = $issued; |
1744 | + $this->lifetime = $lifetime; |
1745 | + $this->assoc_type = $assoc_type; |
1746 | + } |
1747 | + |
1748 | + /** |
1749 | + * This returns the number of seconds this association is still |
1750 | + * valid for, or 0 if the association is no longer valid. |
1751 | + * |
1752 | + * @return integer $seconds The number of seconds this association |
1753 | + * is still valid for, or 0 if the association is no longer valid. |
1754 | + */ |
1755 | + function getExpiresIn($now = null) |
1756 | + { |
1757 | + if ($now == null) { |
1758 | + $now = time(); |
1759 | + } |
1760 | + |
1761 | + return max(0, $this->issued + $this->lifetime - $now); |
1762 | + } |
1763 | + |
1764 | + /** |
1765 | + * This checks to see if two {@link Auth_OpenID_Association} |
1766 | + * instances represent the same association. |
1767 | + * |
1768 | + * @return bool $result true if the two instances represent the |
1769 | + * same association, false otherwise. |
1770 | + */ |
1771 | + function equal($other) |
1772 | + { |
1773 | + return ((gettype($this) == gettype($other)) |
1774 | + && ($this->handle == $other->handle) |
1775 | + && ($this->secret == $other->secret) |
1776 | + && ($this->issued == $other->issued) |
1777 | + && ($this->lifetime == $other->lifetime) |
1778 | + && ($this->assoc_type == $other->assoc_type)); |
1779 | + } |
1780 | + |
1781 | + /** |
1782 | + * Convert an association to KV form. |
1783 | + * |
1784 | + * @return string $result String in KV form suitable for |
1785 | + * deserialization by deserialize. |
1786 | + */ |
1787 | + function serialize() |
1788 | + { |
1789 | + $data = array( |
1790 | + 'version' => '2', |
1791 | + 'handle' => $this->handle, |
1792 | + 'secret' => base64_encode($this->secret), |
1793 | + 'issued' => strval(intval($this->issued)), |
1794 | + 'lifetime' => strval(intval($this->lifetime)), |
1795 | + 'assoc_type' => $this->assoc_type |
1796 | + ); |
1797 | + |
1798 | + assert(array_keys($data) == $this->assoc_keys); |
1799 | + |
1800 | + return Auth_OpenID_KVForm::fromArray($data, $strict = true); |
1801 | + } |
1802 | + |
1803 | + /** |
1804 | + * Parse an association as stored by serialize(). This is the |
1805 | + * inverse of serialize. |
1806 | + * |
1807 | + * @param string $assoc_s Association as serialized by serialize() |
1808 | + * @return Auth_OpenID_Association $result instance of this class |
1809 | + */ |
1810 | + static function deserialize($class_name, $assoc_s) |
1811 | + { |
1812 | + $pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true); |
1813 | + $keys = array(); |
1814 | + $values = array(); |
1815 | + foreach ($pairs as $key => $value) { |
1816 | + if (is_array($value)) { |
1817 | + list($key, $value) = $value; |
1818 | + } |
1819 | + $keys[] = $key; |
1820 | + $values[] = $value; |
1821 | + } |
1822 | + |
1823 | + $class_vars = get_class_vars($class_name); |
1824 | + $class_assoc_keys = $class_vars['assoc_keys']; |
1825 | + |
1826 | + sort($keys); |
1827 | + sort($class_assoc_keys); |
1828 | + |
1829 | + if ($keys != $class_assoc_keys) { |
1830 | + trigger_error('Unexpected key values: ' . var_export($keys, true), |
1831 | + E_USER_WARNING); |
1832 | + return null; |
1833 | + } |
1834 | + |
1835 | + $version = $pairs['version']; |
1836 | + $handle = $pairs['handle']; |
1837 | + $secret = $pairs['secret']; |
1838 | + $issued = $pairs['issued']; |
1839 | + $lifetime = $pairs['lifetime']; |
1840 | + $assoc_type = $pairs['assoc_type']; |
1841 | + |
1842 | + if ($version != '2') { |
1843 | + trigger_error('Unknown version: ' . $version, E_USER_WARNING); |
1844 | + return null; |
1845 | + } |
1846 | + |
1847 | + $issued = intval($issued); |
1848 | + $lifetime = intval($lifetime); |
1849 | + $secret = base64_decode($secret); |
1850 | + |
1851 | + return new $class_name( |
1852 | + $handle, $secret, $issued, $lifetime, $assoc_type); |
1853 | + } |
1854 | + |
1855 | + /** |
1856 | + * Generate a signature for a sequence of (key, value) pairs |
1857 | + * |
1858 | + * @access private |
1859 | + * @param array $pairs The pairs to sign, in order. This is an |
1860 | + * array of two-tuples. |
1861 | + * @return string $signature The binary signature of this sequence |
1862 | + * of pairs |
1863 | + */ |
1864 | + function sign($pairs) |
1865 | + { |
1866 | + $kv = Auth_OpenID_KVForm::fromArray($pairs); |
1867 | + |
1868 | + /* Invalid association types should be caught at constructor */ |
1869 | + $callback = $this->_macs[$this->assoc_type]; |
1870 | + |
1871 | + return call_user_func_array($callback, array($this->secret, $kv)); |
1872 | + } |
1873 | + |
1874 | + /** |
1875 | + * Generate a signature for some fields in a dictionary |
1876 | + * |
1877 | + * @access private |
1878 | + * @param array $fields The fields to sign, in order; this is an |
1879 | + * array of strings. |
1880 | + * @param array $data Dictionary of values to sign (an array of |
1881 | + * string => string pairs). |
1882 | + * @return string $signature The signature, base64 encoded |
1883 | + */ |
1884 | + function signMessage($message) |
1885 | + { |
1886 | + if ($message->hasKey(Auth_OpenID_OPENID_NS, 'sig') || |
1887 | + $message->hasKey(Auth_OpenID_OPENID_NS, 'signed')) { |
1888 | + // Already has a sig |
1889 | + return null; |
1890 | + } |
1891 | + |
1892 | + $extant_handle = $message->getArg(Auth_OpenID_OPENID_NS, |
1893 | + 'assoc_handle'); |
1894 | + |
1895 | + if ($extant_handle && ($extant_handle != $this->handle)) { |
1896 | + // raise ValueError("Message has a different association handle") |
1897 | + return null; |
1898 | + } |
1899 | + |
1900 | + $signed_message = $message; |
1901 | + $signed_message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle', |
1902 | + $this->handle); |
1903 | + |
1904 | + $message_keys = array_keys($signed_message->toPostArgs()); |
1905 | + $signed_list = array(); |
1906 | + $signed_prefix = 'openid.'; |
1907 | + |
1908 | + foreach ($message_keys as $k) { |
1909 | + if (strpos($k, $signed_prefix) === 0) { |
1910 | + $signed_list[] = substr($k, strlen($signed_prefix)); |
1911 | + } |
1912 | + } |
1913 | + |
1914 | + $signed_list[] = 'signed'; |
1915 | + sort($signed_list); |
1916 | + |
1917 | + $signed_message->setArg(Auth_OpenID_OPENID_NS, 'signed', |
1918 | + implode(',', $signed_list)); |
1919 | + $sig = $this->getMessageSignature($signed_message); |
1920 | + $signed_message->setArg(Auth_OpenID_OPENID_NS, 'sig', $sig); |
1921 | + return $signed_message; |
1922 | + } |
1923 | + |
1924 | + /** |
1925 | + * Given a {@link Auth_OpenID_Message}, return the key/value pairs |
1926 | + * to be signed according to the signed list in the message. If |
1927 | + * the message lacks a signed list, return null. |
1928 | + * |
1929 | + * @access private |
1930 | + */ |
1931 | + function _makePairs($message) |
1932 | + { |
1933 | + $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed'); |
1934 | + if (!$signed || Auth_OpenID::isFailure($signed)) { |
1935 | + // raise ValueError('Message has no signed list: %s' % (message,)) |
1936 | + return null; |
1937 | + } |
1938 | + |
1939 | + $signed_list = explode(',', $signed); |
1940 | + $pairs = array(); |
1941 | + $data = $message->toPostArgs(); |
1942 | + foreach ($signed_list as $field) { |
1943 | + $pairs[] = array($field, Auth_OpenID::arrayGet($data, |
1944 | + 'openid.' . |
1945 | + $field, '')); |
1946 | + } |
1947 | + return $pairs; |
1948 | + } |
1949 | + |
1950 | + /** |
1951 | + * Given an {@link Auth_OpenID_Message}, return the signature for |
1952 | + * the signed list in the message. |
1953 | + * |
1954 | + * @access private |
1955 | + */ |
1956 | + function getMessageSignature($message) |
1957 | + { |
1958 | + $pairs = $this->_makePairs($message); |
1959 | + return base64_encode($this->sign($pairs)); |
1960 | + } |
1961 | + |
1962 | + /** |
1963 | + * Confirm that the signature of these fields matches the |
1964 | + * signature contained in the data. |
1965 | + * |
1966 | + * @access private |
1967 | + */ |
1968 | + function checkMessageSignature($message) |
1969 | + { |
1970 | + $sig = $message->getArg(Auth_OpenID_OPENID_NS, |
1971 | + 'sig'); |
1972 | + |
1973 | + if (!$sig || Auth_OpenID::isFailure($sig)) { |
1974 | + return false; |
1975 | + } |
1976 | + |
1977 | + $calculated_sig = $this->getMessageSignature($message); |
1978 | + return $calculated_sig == $sig; |
1979 | + } |
1980 | +} |
1981 | + |
1982 | +function Auth_OpenID_getSecretSize($assoc_type) |
1983 | +{ |
1984 | + if ($assoc_type == 'HMAC-SHA1') { |
1985 | + return 20; |
1986 | + } else if ($assoc_type == 'HMAC-SHA256') { |
1987 | + return 32; |
1988 | + } else { |
1989 | + return null; |
1990 | + } |
1991 | +} |
1992 | + |
1993 | +function Auth_OpenID_getAllAssociationTypes() |
1994 | +{ |
1995 | + return array('HMAC-SHA1', 'HMAC-SHA256'); |
1996 | +} |
1997 | + |
1998 | +function Auth_OpenID_getSupportedAssociationTypes() |
1999 | +{ |
2000 | + $a = array('HMAC-SHA1'); |
2001 | + |
2002 | + if (Auth_OpenID_HMACSHA256_SUPPORTED) { |
2003 | + $a[] = 'HMAC-SHA256'; |
2004 | + } |
2005 | + |
2006 | + return $a; |
2007 | +} |
2008 | + |
2009 | +function Auth_OpenID_getSessionTypes($assoc_type) |
2010 | +{ |
2011 | + $assoc_to_session = array( |
2012 | + 'HMAC-SHA1' => array('DH-SHA1', 'no-encryption')); |
2013 | + |
2014 | + if (Auth_OpenID_HMACSHA256_SUPPORTED) { |
2015 | + $assoc_to_session['HMAC-SHA256'] = |
2016 | + array('DH-SHA256', 'no-encryption'); |
2017 | + } |
2018 | + |
2019 | + return Auth_OpenID::arrayGet($assoc_to_session, $assoc_type, array()); |
2020 | +} |
2021 | + |
2022 | +function Auth_OpenID_checkSessionType($assoc_type, $session_type) |
2023 | +{ |
2024 | + if (!in_array($session_type, |
2025 | + Auth_OpenID_getSessionTypes($assoc_type))) { |
2026 | + return false; |
2027 | + } |
2028 | + |
2029 | + return true; |
2030 | +} |
2031 | + |
2032 | +function Auth_OpenID_getDefaultAssociationOrder() |
2033 | +{ |
2034 | + $order = array(); |
2035 | + |
2036 | + if (!Auth_OpenID_noMathSupport()) { |
2037 | + $order[] = array('HMAC-SHA1', 'DH-SHA1'); |
2038 | + |
2039 | + if (Auth_OpenID_HMACSHA256_SUPPORTED) { |
2040 | + $order[] = array('HMAC-SHA256', 'DH-SHA256'); |
2041 | + } |
2042 | + } |
2043 | + |
2044 | + $order[] = array('HMAC-SHA1', 'no-encryption'); |
2045 | + |
2046 | + if (Auth_OpenID_HMACSHA256_SUPPORTED) { |
2047 | + $order[] = array('HMAC-SHA256', 'no-encryption'); |
2048 | + } |
2049 | + |
2050 | + return $order; |
2051 | +} |
2052 | + |
2053 | +function Auth_OpenID_getOnlyEncryptedOrder() |
2054 | +{ |
2055 | + $result = array(); |
2056 | + |
2057 | + foreach (Auth_OpenID_getDefaultAssociationOrder() as $pair) { |
2058 | + list($assoc, $session) = $pair; |
2059 | + |
2060 | + if ($session != 'no-encryption') { |
2061 | + if (Auth_OpenID_HMACSHA256_SUPPORTED && |
2062 | + ($assoc == 'HMAC-SHA256')) { |
2063 | + $result[] = $pair; |
2064 | + } else if ($assoc != 'HMAC-SHA256') { |
2065 | + $result[] = $pair; |
2066 | + } |
2067 | + } |
2068 | + } |
2069 | + |
2070 | + return $result; |
2071 | +} |
2072 | + |
2073 | +function Auth_OpenID_getDefaultNegotiator() |
2074 | +{ |
2075 | + return new Auth_OpenID_SessionNegotiator( |
2076 | + Auth_OpenID_getDefaultAssociationOrder()); |
2077 | +} |
2078 | + |
2079 | +function Auth_OpenID_getEncryptedNegotiator() |
2080 | +{ |
2081 | + return new Auth_OpenID_SessionNegotiator( |
2082 | + Auth_OpenID_getOnlyEncryptedOrder()); |
2083 | +} |
2084 | + |
2085 | +/** |
2086 | + * A session negotiator controls the allowed and preferred association |
2087 | + * types and association session types. Both the {@link |
2088 | + * Auth_OpenID_Consumer} and {@link Auth_OpenID_Server} use |
2089 | + * negotiators when creating associations. |
2090 | + * |
2091 | + * You can create and use negotiators if you: |
2092 | + |
2093 | + * - Do not want to do Diffie-Hellman key exchange because you use |
2094 | + * transport-layer encryption (e.g. SSL) |
2095 | + * |
2096 | + * - Want to use only SHA-256 associations |
2097 | + * |
2098 | + * - Do not want to support plain-text associations over a non-secure |
2099 | + * channel |
2100 | + * |
2101 | + * It is up to you to set a policy for what kinds of associations to |
2102 | + * accept. By default, the library will make any kind of association |
2103 | + * that is allowed in the OpenID 2.0 specification. |
2104 | + * |
2105 | + * Use of negotiators in the library |
2106 | + * ================================= |
2107 | + * |
2108 | + * When a consumer makes an association request, it calls {@link |
2109 | + * getAllowedType} to get the preferred association type and |
2110 | + * association session type. |
2111 | + * |
2112 | + * The server gets a request for a particular association/session type |
2113 | + * and calls {@link isAllowed} to determine if it should create an |
2114 | + * association. If it is supported, negotiation is complete. If it is |
2115 | + * not, the server calls {@link getAllowedType} to get an allowed |
2116 | + * association type to return to the consumer. |
2117 | + * |
2118 | + * If the consumer gets an error response indicating that the |
2119 | + * requested association/session type is not supported by the server |
2120 | + * that contains an assocation/session type to try, it calls {@link |
2121 | + * isAllowed} to determine if it should try again with the given |
2122 | + * combination of association/session type. |
2123 | + * |
2124 | + * @package OpenID |
2125 | + */ |
2126 | +class Auth_OpenID_SessionNegotiator { |
2127 | + function Auth_OpenID_SessionNegotiator($allowed_types) |
2128 | + { |
2129 | + $this->allowed_types = array(); |
2130 | + $this->setAllowedTypes($allowed_types); |
2131 | + } |
2132 | + |
2133 | + /** |
2134 | + * Set the allowed association types, checking to make sure each |
2135 | + * combination is valid. |
2136 | + * |
2137 | + * @access private |
2138 | + */ |
2139 | + function setAllowedTypes($allowed_types) |
2140 | + { |
2141 | + foreach ($allowed_types as $pair) { |
2142 | + list($assoc_type, $session_type) = $pair; |
2143 | + if (!Auth_OpenID_checkSessionType($assoc_type, $session_type)) { |
2144 | + return false; |
2145 | + } |
2146 | + } |
2147 | + |
2148 | + $this->allowed_types = $allowed_types; |
2149 | + return true; |
2150 | + } |
2151 | + |
2152 | + /** |
2153 | + * Add an association type and session type to the allowed types |
2154 | + * list. The assocation/session pairs are tried in the order that |
2155 | + * they are added. |
2156 | + * |
2157 | + * @access private |
2158 | + */ |
2159 | + function addAllowedType($assoc_type, $session_type = null) |
2160 | + { |
2161 | + if ($this->allowed_types === null) { |
2162 | + $this->allowed_types = array(); |
2163 | + } |
2164 | + |
2165 | + if ($session_type === null) { |
2166 | + $available = Auth_OpenID_getSessionTypes($assoc_type); |
2167 | + |
2168 | + if (!$available) { |
2169 | + return false; |
2170 | + } |
2171 | + |
2172 | + foreach ($available as $session_type) { |
2173 | + $this->addAllowedType($assoc_type, $session_type); |
2174 | + } |
2175 | + } else { |
2176 | + if (Auth_OpenID_checkSessionType($assoc_type, $session_type)) { |
2177 | + $this->allowed_types[] = array($assoc_type, $session_type); |
2178 | + } else { |
2179 | + return false; |
2180 | + } |
2181 | + } |
2182 | + |
2183 | + return true; |
2184 | + } |
2185 | + |
2186 | + // Is this combination of association type and session type allowed? |
2187 | + function isAllowed($assoc_type, $session_type) |
2188 | + { |
2189 | + $assoc_good = in_array(array($assoc_type, $session_type), |
2190 | + $this->allowed_types); |
2191 | + |
2192 | + $matches = in_array($session_type, |
2193 | + Auth_OpenID_getSessionTypes($assoc_type)); |
2194 | + |
2195 | + return ($assoc_good && $matches); |
2196 | + } |
2197 | + |
2198 | + /** |
2199 | + * Get a pair of assocation type and session type that are |
2200 | + * supported. |
2201 | + */ |
2202 | + function getAllowedType() |
2203 | + { |
2204 | + if (!$this->allowed_types) { |
2205 | + return array(null, null); |
2206 | + } |
2207 | + |
2208 | + return $this->allowed_types[0]; |
2209 | + } |
2210 | +} |
2211 | + |
2212 | |
2213 | === added file 'Auth/OpenID/BigMath.php' |
2214 | --- Auth/OpenID/BigMath.php 1970-01-01 00:00:00 +0000 |
2215 | +++ Auth/OpenID/BigMath.php 2011-06-19 04:48:34 +0000 |
2216 | @@ -0,0 +1,452 @@ |
2217 | +<?php |
2218 | + |
2219 | +/** |
2220 | + * BigMath: A math library wrapper that abstracts out the underlying |
2221 | + * long integer library. |
2222 | + * |
2223 | + * PHP versions 4 and 5 |
2224 | + * |
2225 | + * LICENSE: See the COPYING file included in this distribution. |
2226 | + * |
2227 | + * @access private |
2228 | + * @package OpenID |
2229 | + * @author JanRain, Inc. <openid@janrain.com> |
2230 | + * @copyright 2005-2008 Janrain, Inc. |
2231 | + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache |
2232 | + */ |
2233 | + |
2234 | +/** |
2235 | + * Needed for random number generation |
2236 | + */ |
2237 | +require_once 'Auth/OpenID/CryptUtil.php'; |
2238 | + |
2239 | +/** |
2240 | + * Need Auth_OpenID::bytes(). |
2241 | + */ |
2242 | +require_once 'Auth/OpenID.php'; |
2243 | + |
2244 | +/** |
2245 | + * The superclass of all big-integer math implementations |
2246 | + * @access private |
2247 | + * @package OpenID |
2248 | + */ |
2249 | +class Auth_OpenID_MathLibrary { |
2250 | + /** |
2251 | + * Given a long integer, returns the number converted to a binary |
2252 | + * string. This function accepts long integer values of arbitrary |
2253 | + * magnitude and uses the local large-number math library when |
2254 | + * available. |
2255 | + * |
2256 | + * @param integer $long The long number (can be a normal PHP |
2257 | + * integer or a number created by one of the available long number |
2258 | + * libraries) |
2259 | + * @return string $binary The binary version of $long |
2260 | + */ |
2261 | + function longToBinary($long) |
2262 | + { |
2263 | + $cmp = $this->cmp($long, 0); |
2264 | + if ($cmp < 0) { |
2265 | + $msg = __FUNCTION__ . " takes only positive integers."; |
2266 | + trigger_error($msg, E_USER_ERROR); |
2267 | + return null; |
2268 | + } |
2269 | + |
2270 | + if ($cmp == 0) { |
2271 | + return "\x00"; |
2272 | + } |
2273 | + |
2274 | + $bytes = array(); |
2275 | + |
2276 | + while ($this->cmp($long, 0) > 0) { |
2277 | + array_unshift($bytes, $this->mod($long, 256)); |
2278 | + $long = $this->div($long, pow(2, 8)); |
2279 | + } |
2280 | + |
2281 | + if ($bytes && ($bytes[0] > 127)) { |
2282 | + array_unshift($bytes, 0); |
2283 | + } |
2284 | + |
2285 | + $string = ''; |
2286 | + foreach ($bytes as $byte) { |
2287 | + $string .= pack('C', $byte); |
2288 | + } |
2289 | + |
2290 | + return $string; |
2291 | + } |
2292 | + |
2293 | + /** |
2294 | + * Given a binary string, returns the binary string converted to a |
2295 | + * long number. |
2296 | + * |
2297 | + * @param string $binary The binary version of a long number, |
2298 | + * probably as a result of calling longToBinary |
2299 | + * @return integer $long The long number equivalent of the binary |
2300 | + * string $str |
2301 | + */ |
2302 | + function binaryToLong($str) |
2303 | + { |
2304 | + if ($str === null) { |
2305 | + return null; |
2306 | + } |
2307 | + |
2308 | + // Use array_merge to return a zero-indexed array instead of a |
2309 | + // one-indexed array. |
2310 | + $bytes = array_merge(unpack('C*', $str)); |
2311 | + |
2312 | + $n = $this->init(0); |
2313 | + |
2314 | + if ($bytes && ($bytes[0] > 127)) { |
2315 | + trigger_error("bytesToNum works only for positive integers.", |
2316 | + E_USER_WARNING); |
2317 | + return null; |
2318 | + } |
2319 | + |
2320 | + foreach ($bytes as $byte) { |
2321 | + $n = $this->mul($n, pow(2, 8)); |
2322 | + $n = $this->add($n, $byte); |
2323 | + } |
2324 | + |
2325 | + return $n; |
2326 | + } |
2327 | + |
2328 | + function base64ToLong($str) |
2329 | + { |
2330 | + $b64 = base64_decode($str); |
2331 | + |
2332 | + if ($b64 === false) { |
2333 | + return false; |
2334 | + } |
2335 | + |
2336 | + return $this->binaryToLong($b64); |
2337 | + } |
2338 | + |
2339 | + function longToBase64($str) |
2340 | + { |
2341 | + return base64_encode($this->longToBinary($str)); |
2342 | + } |
2343 | + |
2344 | + /** |
2345 | + * Returns a random number in the specified range. This function |
2346 | + * accepts $start, $stop, and $step values of arbitrary magnitude |
2347 | + * and will utilize the local large-number math library when |
2348 | + * available. |
2349 | + * |
2350 | + * @param integer $start The start of the range, or the minimum |
2351 | + * random number to return |
2352 | + * @param integer $stop The end of the range, or the maximum |
2353 | + * random number to return |
2354 | + * @param integer $step The step size, such that $result - ($step |
2355 | + * * N) = $start for some N |
2356 | + * @return integer $result The resulting randomly-generated number |
2357 | + */ |
2358 | + function rand($stop) |
2359 | + { |
2360 | + static $duplicate_cache = array(); |
2361 | + |
2362 | + // Used as the key for the duplicate cache |
2363 | + $rbytes = $this->longToBinary($stop); |
2364 | + |
2365 | + if (array_key_exists($rbytes, $duplicate_cache)) { |
2366 | + list($duplicate, $nbytes) = $duplicate_cache[$rbytes]; |
2367 | + } else { |
2368 | + if ($rbytes[0] == "\x00") { |
2369 | + $nbytes = Auth_OpenID::bytes($rbytes) - 1; |
2370 | + } else { |
2371 | + $nbytes = Auth_OpenID::bytes($rbytes); |
2372 | + } |
2373 | + |
2374 | + $mxrand = $this->pow(256, $nbytes); |
2375 | + |
2376 | + // If we get a number less than this, then it is in the |
2377 | + // duplicated range. |
2378 | + $duplicate = $this->mod($mxrand, $stop); |
2379 | + |
2380 | + if (count($duplicate_cache) > 10) { |
2381 | + $duplicate_cache = array(); |
2382 | + } |
2383 | + |
2384 | + $duplicate_cache[$rbytes] = array($duplicate, $nbytes); |
2385 | + } |
2386 | + |
2387 | + do { |
2388 | + $bytes = "\x00" . Auth_OpenID_CryptUtil::getBytes($nbytes); |
2389 | + $n = $this->binaryToLong($bytes); |
2390 | + // Keep looping if this value is in the low duplicated range |
2391 | + } while ($this->cmp($n, $duplicate) < 0); |
2392 | + |
2393 | + return $this->mod($n, $stop); |
2394 | + } |
2395 | +} |
2396 | + |
2397 | +/** |
2398 | + * Exposes BCmath math library functionality. |
2399 | + * |
2400 | + * {@link Auth_OpenID_BcMathWrapper} wraps the functionality provided |
2401 | + * by the BCMath extension. |
2402 | + * |
2403 | + * @access private |
2404 | + * @package OpenID |
2405 | + */ |
2406 | +class Auth_OpenID_BcMathWrapper extends Auth_OpenID_MathLibrary{ |
2407 | + var $type = 'bcmath'; |
2408 | + |
2409 | + function add($x, $y) |
2410 | + { |
2411 | + return bcadd($x, $y); |
2412 | + } |
2413 | + |
2414 | + function sub($x, $y) |
2415 | + { |
2416 | + return bcsub($x, $y); |
2417 | + } |
2418 | + |
2419 | + function pow($base, $exponent) |
2420 | + { |
2421 | + return bcpow($base, $exponent); |
2422 | + } |
2423 | + |
2424 | + function cmp($x, $y) |
2425 | + { |
2426 | + return bccomp($x, $y); |
2427 | + } |
2428 | + |
2429 | + function init($number, $base = 10) |
2430 | + { |
2431 | + return $number; |
2432 | + } |
2433 | + |
2434 | + function mod($base, $modulus) |
2435 | + { |
2436 | + return bcmod($base, $modulus); |
2437 | + } |
2438 | + |
2439 | + function mul($x, $y) |
2440 | + { |
2441 | + return bcmul($x, $y); |
2442 | + } |
2443 | + |
2444 | + function div($x, $y) |
2445 | + { |
2446 | + return bcdiv($x, $y); |
2447 | + } |
2448 | + |
2449 | + /** |
2450 | + * Same as bcpowmod when bcpowmod is missing |
2451 | + * |
2452 | + * @access private |
2453 | + */ |
2454 | + function _powmod($base, $exponent, $modulus) |
2455 | + { |
2456 | + $square = $this->mod($base, $modulus); |
2457 | + $result = 1; |
2458 | + while($this->cmp($exponent, 0) > 0) { |
2459 | + if ($this->mod($exponent, 2)) { |
2460 | + $result = $this->mod($this->mul($result, $square), $modulus); |
2461 | + } |
2462 | + $square = $this->mod($this->mul($square, $square), $modulus); |
2463 | + $exponent = $this->div($exponent, 2); |
2464 | + } |
2465 | + return $result; |
2466 | + } |
2467 | + |
2468 | + function powmod($base, $exponent, $modulus) |
2469 | + { |
2470 | + if (function_exists('bcpowmod')) { |
2471 | + return bcpowmod($base, $exponent, $modulus); |
2472 | + } else { |
2473 | + return $this->_powmod($base, $exponent, $modulus); |
2474 | + } |
2475 | + } |
2476 | + |
2477 | + function toString($num) |
2478 | + { |
2479 | + return $num; |
2480 | + } |
2481 | +} |
2482 | + |
2483 | +/** |
2484 | + * Exposes GMP math library functionality. |
2485 | + * |
2486 | + * {@link Auth_OpenID_GmpMathWrapper} wraps the functionality provided |
2487 | + * by the GMP extension. |
2488 | + * |
2489 | + * @access private |
2490 | + * @package OpenID |
2491 | + */ |
2492 | +class Auth_OpenID_GmpMathWrapper extends Auth_OpenID_MathLibrary{ |
2493 | + var $type = 'gmp'; |
2494 | + |
2495 | + function add($x, $y) |
2496 | + { |
2497 | + return gmp_add($x, $y); |
2498 | + } |
2499 | + |
2500 | + function sub($x, $y) |
2501 | + { |
2502 | + return gmp_sub($x, $y); |
2503 | + } |
2504 | + |
2505 | + function pow($base, $exponent) |
2506 | + { |
2507 | + return gmp_pow($base, $exponent); |
2508 | + } |
2509 | + |
2510 | + function cmp($x, $y) |
2511 | + { |
2512 | + return gmp_cmp($x, $y); |
2513 | + } |
2514 | + |
2515 | + function init($number, $base = 10) |
2516 | + { |
2517 | + return gmp_init($number, $base); |
2518 | + } |
2519 | + |
2520 | + function mod($base, $modulus) |
2521 | + { |
2522 | + return gmp_mod($base, $modulus); |
2523 | + } |
2524 | + |
2525 | + function mul($x, $y) |
2526 | + { |
2527 | + return gmp_mul($x, $y); |
2528 | + } |
2529 | + |
2530 | + function div($x, $y) |
2531 | + { |
2532 | + return gmp_div_q($x, $y); |
2533 | + } |
2534 | + |
2535 | + function powmod($base, $exponent, $modulus) |
2536 | + { |
2537 | + return gmp_powm($base, $exponent, $modulus); |
2538 | + } |
2539 | + |
2540 | + function toString($num) |
2541 | + { |
2542 | + return gmp_strval($num); |
2543 | + } |
2544 | +} |
2545 | + |
2546 | +/** |
2547 | + * Define the supported extensions. An extension array has keys |
2548 | + * 'modules', 'extension', and 'class'. 'modules' is an array of PHP |
2549 | + * module names which the loading code will attempt to load. These |
2550 | + * values will be suffixed with a library file extension (e.g. ".so"). |
2551 | + * 'extension' is the name of a PHP extension which will be tested |
2552 | + * before 'modules' are loaded. 'class' is the string name of a |
2553 | + * {@link Auth_OpenID_MathWrapper} subclass which should be |
2554 | + * instantiated if a given extension is present. |
2555 | + * |
2556 | + * You can define new math library implementations and add them to |
2557 | + * this array. |
2558 | + */ |
2559 | +function Auth_OpenID_math_extensions() |
2560 | +{ |
2561 | + $result = array(); |
2562 | + |
2563 | + if (!defined('Auth_OpenID_BUGGY_GMP')) { |
2564 | + $result[] = |
2565 | + array('modules' => array('gmp', 'php_gmp'), |
2566 | + 'extension' => 'gmp', |
2567 | + 'class' => 'Auth_OpenID_GmpMathWrapper'); |
2568 | + } |
2569 | + |
2570 | + $result[] = array('modules' => array('bcmath', 'php_bcmath'), |
2571 | + 'extension' => 'bcmath', |
2572 | + 'class' => 'Auth_OpenID_BcMathWrapper'); |
2573 | + |
2574 | + return $result; |
2575 | +} |
2576 | + |
2577 | +/** |
2578 | + * Detect which (if any) math library is available |
2579 | + */ |
2580 | +function Auth_OpenID_detectMathLibrary($exts) |
2581 | +{ |
2582 | + $loaded = false; |
2583 | + |
2584 | + $hasDl = function_exists('dl'); |
2585 | + foreach ($exts as $extension) { |
2586 | + if (extension_loaded($extension['extension'])) { |
2587 | + return $extension; |
2588 | + } |
2589 | + } |
2590 | + |
2591 | + return false; |
2592 | +} |
2593 | + |
2594 | +/** |
2595 | + * {@link Auth_OpenID_getMathLib} checks for the presence of long |
2596 | + * number extension modules and returns an instance of |
2597 | + * {@link Auth_OpenID_MathWrapper} which exposes the module's |
2598 | + * functionality. |
2599 | + * |
2600 | + * Checks for the existence of an extension module described by the |
2601 | + * result of {@link Auth_OpenID_math_extensions()} and returns an |
2602 | + * instance of a wrapper for that extension module. If no extension |
2603 | + * module is found, an instance of {@link Auth_OpenID_MathWrapper} is |
2604 | + * returned, which wraps the native PHP integer implementation. The |
2605 | + * proper calling convention for this method is $lib = |
2606 | + * Auth_OpenID_getMathLib(). |
2607 | + * |
2608 | + * This function checks for the existence of specific long number |
2609 | + * implementations in the following order: GMP followed by BCmath. |
2610 | + * |
2611 | + * @return Auth_OpenID_MathWrapper $instance An instance of |
2612 | + * {@link Auth_OpenID_MathWrapper} or one of its subclasses |
2613 | + * |
2614 | + * @package OpenID |
2615 | + */ |
2616 | +function Auth_OpenID_getMathLib() |
2617 | +{ |
2618 | + // The instance of Auth_OpenID_MathWrapper that we choose to |
2619 | + // supply will be stored here, so that subseqent calls to this |
2620 | + // method will return a reference to the same object. |
2621 | + static $lib = null; |
2622 | + |
2623 | + if (isset($lib)) { |
2624 | + return $lib; |
2625 | + } |
2626 | + |
2627 | + if (Auth_OpenID_noMathSupport()) { |
2628 | + $null = null; |
2629 | + return $null; |
2630 | + } |
2631 | + |
2632 | + // If this method has not been called before, look at |
2633 | + // Auth_OpenID_math_extensions and try to find an extension that |
2634 | + // works. |
2635 | + $ext = Auth_OpenID_detectMathLibrary(Auth_OpenID_math_extensions()); |
2636 | + if ($ext === false) { |
2637 | + $tried = array(); |
2638 | + foreach (Auth_OpenID_math_extensions() as $extinfo) { |
2639 | + $tried[] = $extinfo['extension']; |
2640 | + } |
2641 | + $triedstr = implode(", ", $tried); |
2642 | + |
2643 | + Auth_OpenID_setNoMathSupport(); |
2644 | + |
2645 | + $result = null; |
2646 | + return $result; |
2647 | + } |
2648 | + |
2649 | + // Instantiate a new wrapper |
2650 | + $class = $ext['class']; |
2651 | + $lib = new $class(); |
2652 | + |
2653 | + return $lib; |
2654 | +} |
2655 | + |
2656 | +function Auth_OpenID_setNoMathSupport() |
2657 | +{ |
2658 | + if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) { |
2659 | + define('Auth_OpenID_NO_MATH_SUPPORT', true); |
2660 | + } |
2661 | +} |
2662 | + |
2663 | +function Auth_OpenID_noMathSupport() |
2664 | +{ |
2665 | + return defined('Auth_OpenID_NO_MATH_SUPPORT'); |
2666 | +} |
2667 | + |
2668 | + |
2669 | |
2670 | === added file 'Auth/OpenID/Consumer.php' |
2671 | --- Auth/OpenID/Consumer.php 1970-01-01 00:00:00 +0000 |
2672 | +++ Auth/OpenID/Consumer.php 2011-06-19 04:48:34 +0000 |
2673 | @@ -0,0 +1,2230 @@ |
2674 | +<?php |
2675 | + |
2676 | +/** |
2677 | + * This module documents the main interface with the OpenID consumer |
2678 | + * library. The only part of the library which has to be used and |
2679 | + * isn't documented in full here is the store required to create an |
2680 | + * Auth_OpenID_Consumer instance. More on the abstract store type and |
2681 | + * concrete implementations of it that are provided in the |
2682 | + * documentation for the Auth_OpenID_Consumer constructor. |
2683 | + * |
2684 | + * OVERVIEW |
2685 | + * |
2686 | + * The OpenID identity verification process most commonly uses the |
2687 | + * following steps, as visible to the user of this library: |
2688 | + * |
2689 | + * 1. The user enters their OpenID into a field on the consumer's |
2690 | + * site, and hits a login button. |
2691 | + * 2. The consumer site discovers the user's OpenID server using the |
2692 | + * YADIS protocol. |
2693 | + * 3. The consumer site sends the browser a redirect to the identity |
2694 | + * server. This is the authentication request as described in |
2695 | + * the OpenID specification. |
2696 | + * 4. The identity server's site sends the browser a redirect back |
2697 | + * to the consumer site. This redirect contains the server's |
2698 | + * response to the authentication request. |
2699 | + * |
2700 | + * The most important part of the flow to note is the consumer's site |
2701 | + * must handle two separate HTTP requests in order to perform the full |
2702 | + * identity check. |
2703 | + * |
2704 | + * LIBRARY DESIGN |
2705 | + * |
2706 | + * This consumer library is designed with that flow in mind. The goal |
2707 | + * is to make it as easy as possible to perform the above steps |
2708 | + * securely. |
2709 | + * |
2710 | + * At a high level, there are two important parts in the consumer |
2711 | + * library. The first important part is this module, which contains |
2712 | + * the interface to actually use this library. The second is the |
2713 | + * Auth_OpenID_Interface class, which describes the interface to use |
2714 | + * if you need to create a custom method for storing the state this |
2715 | + * library needs to maintain between requests. |
2716 | + * |
2717 | + * In general, the second part is less important for users of the |
2718 | + * library to know about, as several implementations are provided |
2719 | + * which cover a wide variety of situations in which consumers may use |
2720 | + * the library. |
2721 | + * |
2722 | + * This module contains a class, Auth_OpenID_Consumer, with methods |
2723 | + * corresponding to the actions necessary in each of steps 2, 3, and 4 |
2724 | + * described in the overview. Use of this library should be as easy |
2725 | + * as creating an Auth_OpenID_Consumer instance and calling the |
2726 | + * methods appropriate for the action the site wants to take. |
2727 | + * |
2728 | + * STORES AND DUMB MODE |
2729 | + * |
2730 | + * OpenID is a protocol that works best when the consumer site is able |
2731 | + * to store some state. This is the normal mode of operation for the |
2732 | + * protocol, and is sometimes referred to as smart mode. There is |
2733 | + * also a fallback mode, known as dumb mode, which is available when |
2734 | + * the consumer site is not able to store state. This mode should be |
2735 | + * avoided when possible, as it leaves the implementation more |
2736 | + * vulnerable to replay attacks. |
2737 | + * |
2738 | + * The mode the library works in for normal operation is determined by |
2739 | + * the store that it is given. The store is an abstraction that |
2740 | + * handles the data that the consumer needs to manage between http |
2741 | + * requests in order to operate efficiently and securely. |
2742 | + * |
2743 | + * Several store implementation are provided, and the interface is |
2744 | + * fully documented so that custom stores can be used as well. See |
2745 | + * the documentation for the Auth_OpenID_Consumer class for more |
2746 | + * information on the interface for stores. The implementations that |
2747 | + * are provided allow the consumer site to store the necessary data in |
2748 | + * several different ways, including several SQL databases and normal |
2749 | + * files on disk. |
2750 | + * |
2751 | + * There is an additional concrete store provided that puts the system |
2752 | + * in dumb mode. This is not recommended, as it removes the library's |
2753 | + * ability to stop replay attacks reliably. It still uses time-based |
2754 | + * checking to make replay attacks only possible within a small |
2755 | + * window, but they remain possible within that window. This store |
2756 | + * should only be used if the consumer site has no way to retain data |
2757 | + * between requests at all. |
2758 | + * |
2759 | + * IMMEDIATE MODE |
2760 | + * |
2761 | + * In the flow described above, the user may need to confirm to the |
2762 | + * lidentity server that it's ok to authorize his or her identity. |
2763 | + * The server may draw pages asking for information from the user |
2764 | + * before it redirects the browser back to the consumer's site. This |
2765 | + * is generally transparent to the consumer site, so it is typically |
2766 | + * ignored as an implementation detail. |
2767 | + * |
2768 | + * There can be times, however, where the consumer site wants to get a |
2769 | + * response immediately. When this is the case, the consumer can put |
2770 | + * the library in immediate mode. In immediate mode, there is an |
2771 | + * extra response possible from the server, which is essentially the |
2772 | + * server reporting that it doesn't have enough information to answer |
2773 | + * the question yet. |
2774 | + * |
2775 | + * USING THIS LIBRARY |
2776 | + * |
2777 | + * Integrating this library into an application is usually a |
2778 | + * relatively straightforward process. The process should basically |
2779 | + * follow this plan: |
2780 | + * |
2781 | + * Add an OpenID login field somewhere on your site. When an OpenID |
2782 | + * is entered in that field and the form is submitted, it should make |
2783 | + * a request to the your site which includes that OpenID URL. |
2784 | + * |
2785 | + * First, the application should instantiate the Auth_OpenID_Consumer |
2786 | + * class using the store of choice (Auth_OpenID_FileStore or one of |
2787 | + * the SQL-based stores). If the application has a custom |
2788 | + * session-management implementation, an object implementing the |
2789 | + * {@link Auth_Yadis_PHPSession} interface should be passed as the |
2790 | + * second parameter. Otherwise, the default uses $_SESSION. |
2791 | + * |
2792 | + * Next, the application should call the Auth_OpenID_Consumer object's |
2793 | + * 'begin' method. This method takes the OpenID URL. The 'begin' |
2794 | + * method returns an Auth_OpenID_AuthRequest object. |
2795 | + * |
2796 | + * Next, the application should call the 'redirectURL' method of the |
2797 | + * Auth_OpenID_AuthRequest object. The 'return_to' URL parameter is |
2798 | + * the URL that the OpenID server will send the user back to after |
2799 | + * attempting to verify his or her identity. The 'trust_root' is the |
2800 | + * URL (or URL pattern) that identifies your web site to the user when |
2801 | + * he or she is authorizing it. Send a redirect to the resulting URL |
2802 | + * to the user's browser. |
2803 | + * |
2804 | + * That's the first half of the authentication process. The second |
2805 | + * half of the process is done after the user's ID server sends the |
2806 | + * user's browser a redirect back to your site to complete their |
2807 | + * login. |
2808 | + * |
2809 | + * When that happens, the user will contact your site at the URL given |
2810 | + * as the 'return_to' URL to the Auth_OpenID_AuthRequest::redirectURL |
2811 | + * call made above. The request will have several query parameters |
2812 | + * added to the URL by the identity server as the information |
2813 | + * necessary to finish the request. |
2814 | + * |
2815 | + * Lastly, instantiate an Auth_OpenID_Consumer instance as above and |
2816 | + * call its 'complete' method, passing in all the received query |
2817 | + * arguments. |
2818 | + * |
2819 | + * There are multiple possible return types possible from that |
2820 | + * method. These indicate the whether or not the login was successful, |
2821 | + * and include any additional information appropriate for their type. |
2822 | + * |
2823 | + * PHP versions 4 and 5 |
2824 | + * |
2825 | + * LICENSE: See the COPYING file included in this distribution. |
2826 | + * |
2827 | + * @package OpenID |
2828 | + * @author JanRain, Inc. <openid@janrain.com> |
2829 | + * @copyright 2005-2008 Janrain, Inc. |
2830 | + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache |
2831 | + */ |
2832 | + |
2833 | +/** |
2834 | + * Require utility classes and functions for the consumer. |
2835 | + */ |
2836 | +require_once "Auth/OpenID.php"; |
2837 | +require_once "Auth/OpenID/Message.php"; |
2838 | +require_once "Auth/OpenID/HMAC.php"; |
2839 | +require_once "Auth/OpenID/Association.php"; |
2840 | +require_once "Auth/OpenID/CryptUtil.php"; |
2841 | +require_once "Auth/OpenID/DiffieHellman.php"; |
2842 | +require_once "Auth/OpenID/KVForm.php"; |
2843 | +require_once "Auth/OpenID/Nonce.php"; |
2844 | +require_once "Auth/OpenID/Discover.php"; |
2845 | +require_once "Auth/OpenID/URINorm.php"; |
2846 | +require_once "Auth/Yadis/Manager.php"; |
2847 | +require_once "Auth/Yadis/XRI.php"; |
2848 | + |
2849 | +/** |
2850 | + * This is the status code returned when the complete method returns |
2851 | + * successfully. |
2852 | + */ |
2853 | +define('Auth_OpenID_SUCCESS', 'success'); |
2854 | + |
2855 | +/** |
2856 | + * Status to indicate cancellation of OpenID authentication. |
2857 | + */ |
2858 | +define('Auth_OpenID_CANCEL', 'cancel'); |
2859 | + |
2860 | +/** |
2861 | + * This is the status code completeAuth returns when the value it |
2862 | + * received indicated an invalid login. |
2863 | + */ |
2864 | +define('Auth_OpenID_FAILURE', 'failure'); |
2865 | + |
2866 | +/** |
2867 | + * This is the status code completeAuth returns when the |
2868 | + * {@link Auth_OpenID_Consumer} instance is in immediate mode, and the |
2869 | + * identity server sends back a URL to send the user to to complete his |
2870 | + * or her login. |
2871 | + */ |
2872 | +define('Auth_OpenID_SETUP_NEEDED', 'setup needed'); |
2873 | + |
2874 | +/** |
2875 | + * This is the status code beginAuth returns when the page fetched |
2876 | + * from the entered OpenID URL doesn't contain the necessary link tags |
2877 | + * to function as an identity page. |
2878 | + */ |
2879 | +define('Auth_OpenID_PARSE_ERROR', 'parse error'); |
2880 | + |
2881 | +/** |
2882 | + * An OpenID consumer implementation that performs discovery and does |
2883 | + * session management. See the Consumer.php file documentation for |
2884 | + * more information. |
2885 | + * |
2886 | + * @package OpenID |
2887 | + */ |
2888 | +class Auth_OpenID_Consumer { |
2889 | + |
2890 | + /** |
2891 | + * @access private |
2892 | + */ |
2893 | + var $discoverMethod = 'Auth_OpenID_discover'; |
2894 | + |
2895 | + /** |
2896 | + * @access private |
2897 | + */ |
2898 | + var $session_key_prefix = "_openid_consumer_"; |
2899 | + |
2900 | + /** |
2901 | + * @access private |
2902 | + */ |
2903 | + var $_token_suffix = "last_token"; |
2904 | + |
2905 | + /** |
2906 | + * Initialize a Consumer instance. |
2907 | + * |
2908 | + * You should create a new instance of the Consumer object with |
2909 | + * every HTTP request that handles OpenID transactions. |
2910 | + * |
2911 | + * @param Auth_OpenID_OpenIDStore $store This must be an object |
2912 | + * that implements the interface in {@link |
2913 | + * Auth_OpenID_OpenIDStore}. Several concrete implementations are |
2914 | + * provided, to cover most common use cases. For stores backed by |
2915 | + * MySQL, PostgreSQL, or SQLite, see the {@link |
2916 | + * Auth_OpenID_SQLStore} class and its sublcasses. For a |
2917 | + * filesystem-backed store, see the {@link Auth_OpenID_FileStore} |
2918 | + * module. As a last resort, if it isn't possible for the server |
2919 | + * to store state at all, an instance of {@link |
2920 | + * Auth_OpenID_DumbStore} can be used. |
2921 | + * |
2922 | + * @param mixed $session An object which implements the interface |
2923 | + * of the {@link Auth_Yadis_PHPSession} class. Particularly, this |
2924 | + * object is expected to have these methods: get($key), set($key), |
2925 | + * $value), and del($key). This defaults to a session object |
2926 | + * which wraps PHP's native session machinery. You should only |
2927 | + * need to pass something here if you have your own sessioning |
2928 | + * implementation. |
2929 | + * |
2930 | + * @param str $consumer_cls The name of the class to instantiate |
2931 | + * when creating the internal consumer object. This is used for |
2932 | + * testing. |
2933 | + */ |
2934 | + function Auth_OpenID_Consumer($store, $session = null, |
2935 | + $consumer_cls = null) |
2936 | + { |
2937 | + if ($session === null) { |
2938 | + $session = new Auth_Yadis_PHPSession(); |
2939 | + } |
2940 | + |
2941 | + $this->session = $session; |
2942 | + |
2943 | + if ($consumer_cls !== null) { |
2944 | + $this->consumer = new $consumer_cls($store); |
2945 | + } else { |
2946 | + $this->consumer = new Auth_OpenID_GenericConsumer($store); |
2947 | + } |
2948 | + |
2949 | + $this->_token_key = $this->session_key_prefix . $this->_token_suffix; |
2950 | + } |
2951 | + |
2952 | + /** |
2953 | + * Used in testing to define the discovery mechanism. |
2954 | + * |
2955 | + * @access private |
2956 | + */ |
2957 | + function getDiscoveryObject($session, $openid_url, |
2958 | + $session_key_prefix) |
2959 | + { |
2960 | + return new Auth_Yadis_Discovery($session, $openid_url, |
2961 | + $session_key_prefix); |
2962 | + } |
2963 | + |
2964 | + /** |
2965 | + * Start the OpenID authentication process. See steps 1-2 in the |
2966 | + * overview at the top of this file. |
2967 | + * |
2968 | + * @param string $user_url Identity URL given by the user. This |
2969 | + * method performs a textual transformation of the URL to try and |
2970 | + * make sure it is normalized. For example, a user_url of |
2971 | + * example.com will be normalized to http://example.com/ |
2972 | + * normalizing and resolving any redirects the server might issue. |
2973 | + * |
2974 | + * @param bool $anonymous True if the OpenID request is to be sent |
2975 | + * to the server without any identifier information. Use this |
2976 | + * when you want to transport data but don't want to do OpenID |
2977 | + * authentication with identifiers. |
2978 | + * |
2979 | + * @return Auth_OpenID_AuthRequest $auth_request An object |
2980 | + * containing the discovered information will be returned, with a |
2981 | + * method for building a redirect URL to the server, as described |
2982 | + * in step 3 of the overview. This object may also be used to add |
2983 | + * extension arguments to the request, using its 'addExtensionArg' |
2984 | + * method. |
2985 | + */ |
2986 | + function begin($user_url, $anonymous=false) |
2987 | + { |
2988 | + $openid_url = $user_url; |
2989 | + |
2990 | + $disco = $this->getDiscoveryObject($this->session, |
2991 | + $openid_url, |
2992 | + $this->session_key_prefix); |
2993 | + |
2994 | + // Set the 'stale' attribute of the manager. If discovery |
2995 | + // fails in a fatal way, the stale flag will cause the manager |
2996 | + // to be cleaned up next time discovery is attempted. |
2997 | + |
2998 | + $m = $disco->getManager(); |
2999 | + $loader = new Auth_Yadis_ManagerLoader(); |
3000 | + |
3001 | + if ($m) { |
3002 | + if ($m->stale) { |
3003 | + $disco->destroyManager(); |
3004 | + } else { |
3005 | + $m->stale = true; |
3006 | + $disco->session->set($disco->session_key, |
3007 | + serialize($loader->toSession($m))); |
3008 | + } |
3009 | + } |
3010 | + |
3011 | + $endpoint = $disco->getNextService($this->discoverMethod, |
3012 | + $this->consumer->fetcher); |
3013 | + |
3014 | + // Reset the 'stale' attribute of the manager. |
3015 | + $m = $disco->getManager(); |
3016 | + if ($m) { |
3017 | + $m->stale = false; |
3018 | + $disco->session->set($disco->session_key, |
3019 | + serialize($loader->toSession($m))); |
3020 | + } |
3021 | + |
3022 | + if ($endpoint === null) { |
3023 | + return null; |
3024 | + } else { |
3025 | + return $this->beginWithoutDiscovery($endpoint, |
3026 | + $anonymous); |
3027 | + } |
3028 | + } |
3029 | + |
3030 | + /** |
3031 | + * Start OpenID verification without doing OpenID server |
3032 | + * discovery. This method is used internally by Consumer.begin |
3033 | + * after discovery is performed, and exists to provide an |
3034 | + * interface for library users needing to perform their own |
3035 | + * discovery. |
3036 | + * |
3037 | + * @param Auth_OpenID_ServiceEndpoint $endpoint an OpenID service |
3038 | + * endpoint descriptor. |
3039 | + * |
3040 | + * @param bool anonymous Set to true if you want to perform OpenID |
3041 | + * without identifiers. |
3042 | + * |
3043 | + * @return Auth_OpenID_AuthRequest $auth_request An OpenID |
3044 | + * authentication request object. |
3045 | + */ |
3046 | + function beginWithoutDiscovery($endpoint, $anonymous=false) |
3047 | + { |
3048 | + $loader = new Auth_OpenID_ServiceEndpointLoader(); |
3049 | + $auth_req = $this->consumer->begin($endpoint); |
3050 | + $this->session->set($this->_token_key, |
3051 | + $loader->toSession($auth_req->endpoint)); |
3052 | + if (!$auth_req->setAnonymous($anonymous)) { |
3053 | + return new Auth_OpenID_FailureResponse(null, |
3054 | + "OpenID 1 requests MUST include the identifier " . |
3055 | + "in the request."); |
3056 | + } |
3057 | + return $auth_req; |
3058 | + } |
3059 | + |
3060 | + /** |
3061 | + * Called to interpret the server's response to an OpenID |
3062 | + * request. It is called in step 4 of the flow described in the |
3063 | + * consumer overview. |
3064 | + * |
3065 | + * @param string $current_url The URL used to invoke the application. |
3066 | + * Extract the URL from your application's web |
3067 | + * request framework and specify it here to have it checked |
3068 | + * against the openid.current_url value in the response. If |
3069 | + * the current_url URL check fails, the status of the |
3070 | + * completion will be FAILURE. |
3071 | + * |
3072 | + * @param array $query An array of the query parameters (key => |
3073 | + * value pairs) for this HTTP request. Defaults to null. If |
3074 | + * null, the GET or POST data are automatically gotten from the |
3075 | + * PHP environment. It is only useful to override $query for |
3076 | + * testing. |
3077 | + * |
3078 | + * @return Auth_OpenID_ConsumerResponse $response A instance of an |
3079 | + * Auth_OpenID_ConsumerResponse subclass. The type of response is |
3080 | + * indicated by the status attribute, which will be one of |
3081 | + * SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED. |
3082 | + */ |
3083 | + function complete($current_url, $query=null) |
3084 | + { |
3085 | + if ($current_url && !is_string($current_url)) { |
3086 | + // This is ugly, but we need to complain loudly when |
3087 | + // someone uses the API incorrectly. |
3088 | + trigger_error("current_url must be a string; see NEWS file " . |
3089 | + "for upgrading notes.", |
3090 | + E_USER_ERROR); |
3091 | + } |
3092 | + |
3093 | + if ($query === null) { |
3094 | + $query = Auth_OpenID::getQuery(); |
3095 | + } |
3096 | + |
3097 | + $loader = new Auth_OpenID_ServiceEndpointLoader(); |
3098 | + $endpoint_data = $this->session->get($this->_token_key); |
3099 | + $endpoint = |
3100 | + $loader->fromSession($endpoint_data); |
3101 | + |
3102 | + $message = Auth_OpenID_Message::fromPostArgs($query); |
3103 | + $response = $this->consumer->complete($message, $endpoint, |
3104 | + $current_url); |
3105 | + $this->session->del($this->_token_key); |
3106 | + |
3107 | + if (in_array($response->status, array(Auth_OpenID_SUCCESS, |
3108 | + Auth_OpenID_CANCEL))) { |
3109 | + if ($response->identity_url !== null) { |
3110 | + $disco = $this->getDiscoveryObject($this->session, |
3111 | + $response->identity_url, |
3112 | + $this->session_key_prefix); |
3113 | + $disco->cleanup(true); |
3114 | + } |
3115 | + } |
3116 | + |
3117 | + return $response; |
3118 | + } |
3119 | +} |
3120 | + |
3121 | +/** |
3122 | + * A class implementing HMAC/DH-SHA1 consumer sessions. |
3123 | + * |
3124 | + * @package OpenID |
3125 | + */ |
3126 | +class Auth_OpenID_DiffieHellmanSHA1ConsumerSession { |
3127 | + var $session_type = 'DH-SHA1'; |
3128 | + var $hash_func = 'Auth_OpenID_SHA1'; |
3129 | + var $secret_size = 20; |
3130 | + var $allowed_assoc_types = array('HMAC-SHA1'); |
3131 | + |
3132 | + function Auth_OpenID_DiffieHellmanSHA1ConsumerSession($dh = null) |
3133 | + { |
3134 | + if ($dh === null) { |
3135 | + $dh = new Auth_OpenID_DiffieHellman(); |
3136 | + } |
3137 | + |
3138 | + $this->dh = $dh; |
3139 | + } |
3140 | + |
3141 | + function getRequest() |
3142 | + { |
3143 | + $math = Auth_OpenID_getMathLib(); |
3144 | + |
3145 | + $cpub = $math->longToBase64($this->dh->public); |
3146 | + |
3147 | + $args = array('dh_consumer_public' => $cpub); |
3148 | + |
3149 | + if (!$this->dh->usingDefaultValues()) { |
3150 | + $args = array_merge($args, array( |
3151 | + 'dh_modulus' => |
3152 | + $math->longToBase64($this->dh->mod), |
3153 | + 'dh_gen' => |
3154 | + $math->longToBase64($this->dh->gen))); |
3155 | + } |
3156 | + |
3157 | + return $args; |
3158 | + } |
3159 | + |
3160 | + function extractSecret($response) |
3161 | + { |
3162 | + if (!$response->hasKey(Auth_OpenID_OPENID_NS, |
3163 | + 'dh_server_public')) { |
3164 | + return null; |
3165 | + } |
3166 | + |
3167 | + if (!$response->hasKey(Auth_OpenID_OPENID_NS, |
3168 | + 'enc_mac_key')) { |
3169 | + return null; |
3170 | + } |
3171 | + |
3172 | + $math = Auth_OpenID_getMathLib(); |
3173 | + |
3174 | + $spub = $math->base64ToLong($response->getArg(Auth_OpenID_OPENID_NS, |
3175 | + 'dh_server_public')); |
3176 | + $enc_mac_key = base64_decode($response->getArg(Auth_OpenID_OPENID_NS, |
3177 | + 'enc_mac_key')); |
3178 | + |
3179 | + return $this->dh->xorSecret($spub, $enc_mac_key, $this->hash_func); |
3180 | + } |
3181 | +} |
3182 | + |
3183 | +/** |
3184 | + * A class implementing HMAC/DH-SHA256 consumer sessions. |
3185 | + * |
3186 | + * @package OpenID |
3187 | + */ |
3188 | +class Auth_OpenID_DiffieHellmanSHA256ConsumerSession extends |
3189 | + Auth_OpenID_DiffieHellmanSHA1ConsumerSession { |
3190 | + var $session_type = 'DH-SHA256'; |
3191 | + var $hash_func = 'Auth_OpenID_SHA256'; |
3192 | + var $secret_size = 32; |
3193 | + var $allowed_assoc_types = array('HMAC-SHA256'); |
3194 | +} |
3195 | + |
3196 | +/** |
3197 | + * A class implementing plaintext consumer sessions. |
3198 | + * |
3199 | + * @package OpenID |
3200 | + */ |
3201 | +class Auth_OpenID_PlainTextConsumerSession { |
3202 | + var $session_type = 'no-encryption'; |
3203 | + var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256'); |
3204 | + |
3205 | + function getRequest() |
3206 | + { |
3207 | + return array(); |
3208 | + } |
3209 | + |
3210 | + function extractSecret($response) |
3211 | + { |
3212 | + if (!$response->hasKey(Auth_OpenID_OPENID_NS, 'mac_key')) { |
3213 | + return null; |
3214 | + } |
3215 | + |
3216 | + return base64_decode($response->getArg(Auth_OpenID_OPENID_NS, |
3217 | + 'mac_key')); |
3218 | + } |
3219 | +} |
3220 | + |
3221 | +/** |
3222 | + * Returns available session types. |
3223 | + */ |
3224 | +function Auth_OpenID_getAvailableSessionTypes() |
3225 | +{ |
3226 | + $types = array( |
3227 | + 'no-encryption' => 'Auth_OpenID_PlainTextConsumerSession', |
3228 | + 'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ConsumerSession', |
3229 | + 'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ConsumerSession'); |
3230 | + |
3231 | + return $types; |
3232 | +} |
3233 | + |
3234 | +/** |
3235 | + * This class is the interface to the OpenID consumer logic. |
3236 | + * Instances of it maintain no per-request state, so they can be |
3237 | + * reused (or even used by multiple threads concurrently) as needed. |
3238 | + * |
3239 | + * @package OpenID |
3240 | + */ |
3241 | +class Auth_OpenID_GenericConsumer { |
3242 | + /** |
3243 | + * @access private |
3244 | + */ |
3245 | + var $discoverMethod = 'Auth_OpenID_discover'; |
3246 | + |
3247 | + /** |
3248 | + * This consumer's store object. |
3249 | + */ |
3250 | + var $store; |
3251 | + |
3252 | + /** |
3253 | + * @access private |
3254 | + */ |
3255 | + var $_use_assocs; |
3256 | + |
3257 | + /** |
3258 | + * @access private |
3259 | + */ |
3260 | + var $openid1_nonce_query_arg_name = 'janrain_nonce'; |
3261 | + |
3262 | + /** |
3263 | + * Another query parameter that gets added to the return_to for |
3264 | + * OpenID 1; if the user's session state is lost, use this claimed |
3265 | + * identifier to do discovery when verifying the response. |
3266 | + */ |
3267 | + var $openid1_return_to_identifier_name = 'openid1_claimed_id'; |
3268 | + |
3269 | + /** |
3270 | + * This method initializes a new {@link Auth_OpenID_Consumer} |
3271 | + * instance to access the library. |
3272 | + * |
3273 | + * @param Auth_OpenID_OpenIDStore $store This must be an object |
3274 | + * that implements the interface in {@link Auth_OpenID_OpenIDStore}. |
3275 | + * Several concrete implementations are provided, to cover most common use |
3276 | + * cases. For stores backed by MySQL, PostgreSQL, or SQLite, see |
3277 | + * the {@link Auth_OpenID_SQLStore} class and its sublcasses. For a |
3278 | + * filesystem-backed store, see the {@link Auth_OpenID_FileStore} module. |
3279 | + * As a last resort, if it isn't possible for the server to store |
3280 | + * state at all, an instance of {@link Auth_OpenID_DumbStore} can be used. |
3281 | + * |
3282 | + * @param bool $immediate This is an optional boolean value. It |
3283 | + * controls whether the library uses immediate mode, as explained |
3284 | + * in the module description. The default value is False, which |
3285 | + * disables immediate mode. |
3286 | + */ |
3287 | + function Auth_OpenID_GenericConsumer($store) |
3288 | + { |
3289 | + $this->store = $store; |
3290 | + $this->negotiator = Auth_OpenID_getDefaultNegotiator(); |
3291 | + $this->_use_assocs = (is_null($this->store) ? false : true); |
3292 | + |
3293 | + $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); |
3294 | + |
3295 | + $this->session_types = Auth_OpenID_getAvailableSessionTypes(); |
3296 | + } |
3297 | + |
3298 | + /** |
3299 | + * Called to begin OpenID authentication using the specified |
3300 | + * {@link Auth_OpenID_ServiceEndpoint}. |
3301 | + * |
3302 | + * @access private |
3303 | + */ |
3304 | + function begin($service_endpoint) |
3305 | + { |
3306 | + $assoc = $this->_getAssociation($service_endpoint); |
3307 | + $r = new Auth_OpenID_AuthRequest($service_endpoint, $assoc); |
3308 | + $r->return_to_args[$this->openid1_nonce_query_arg_name] = |
3309 | + Auth_OpenID_mkNonce(); |
3310 | + |
3311 | + if ($r->message->isOpenID1()) { |
3312 | + $r->return_to_args[$this->openid1_return_to_identifier_name] = |
3313 | + $r->endpoint->claimed_id; |
3314 | + } |
3315 | + |
3316 | + return $r; |
3317 | + } |
3318 | + |
3319 | + /** |
3320 | + * Given an {@link Auth_OpenID_Message}, {@link |
3321 | + * Auth_OpenID_ServiceEndpoint} and optional return_to URL, |
3322 | + * complete OpenID authentication. |
3323 | + * |
3324 | + * @access private |
3325 | + */ |
3326 | + function complete($message, $endpoint, $return_to) |
3327 | + { |
3328 | + $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode', |
3329 | + '<no mode set>'); |
3330 | + |
3331 | + $mode_methods = array( |
3332 | + 'cancel' => '_complete_cancel', |
3333 | + 'error' => '_complete_error', |
3334 | + 'setup_needed' => '_complete_setup_needed', |
3335 | + 'id_res' => '_complete_id_res', |
3336 | + ); |
3337 | + |
3338 | + $method = Auth_OpenID::arrayGet($mode_methods, $mode, |
3339 | + '_completeInvalid'); |
3340 | + |
3341 | + return call_user_func_array(array($this, $method), |
3342 | + array($message, &$endpoint, $return_to)); |
3343 | + } |
3344 | + |
3345 | + /** |
3346 | + * @access private |
3347 | + */ |
3348 | + function _completeInvalid($message, $endpoint, $unused) |
3349 | + { |
3350 | + $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode', |
3351 | + '<No mode set>'); |
3352 | + |
3353 | + return new Auth_OpenID_FailureResponse($endpoint, |
3354 | + sprintf("Invalid openid.mode '%s'", $mode)); |
3355 | + } |
3356 | + |
3357 | + /** |
3358 | + * @access private |
3359 | + */ |
3360 | + function _complete_cancel($message, $endpoint, $unused) |
3361 | + { |
3362 | + return new Auth_OpenID_CancelResponse($endpoint); |
3363 | + } |
3364 | + |
3365 | + /** |
3366 | + * @access private |
3367 | + */ |
3368 | + function _complete_error($message, $endpoint, $unused) |
3369 | + { |
3370 | + $error = $message->getArg(Auth_OpenID_OPENID_NS, 'error'); |
3371 | + $contact = $message->getArg(Auth_OpenID_OPENID_NS, 'contact'); |
3372 | + $reference = $message->getArg(Auth_OpenID_OPENID_NS, 'reference'); |
3373 | + |
3374 | + return new Auth_OpenID_FailureResponse($endpoint, $error, |
3375 | + $contact, $reference); |
3376 | + } |
3377 | + |
3378 | + /** |
3379 | + * @access private |
3380 | + */ |
3381 | + function _complete_setup_needed($message, $endpoint, $unused) |
3382 | + { |
3383 | + if (!$message->isOpenID2()) { |
3384 | + return $this->_completeInvalid($message, $endpoint); |
3385 | + } |
3386 | + |
3387 | + $user_setup_url = $message->getArg(Auth_OpenID_OPENID2_NS, |
3388 | + 'user_setup_url'); |
3389 | + return new Auth_OpenID_SetupNeededResponse($endpoint, $user_setup_url); |
3390 | + } |
3391 | + |
3392 | + /** |
3393 | + * @access private |
3394 | + */ |
3395 | + function _complete_id_res($message, $endpoint, $return_to) |
3396 | + { |
3397 | + $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS, |
3398 | + 'user_setup_url'); |
3399 | + |
3400 | + if ($this->_checkSetupNeeded($message)) { |
3401 | + return new Auth_OpenID_SetupNeededResponse( |
3402 | + $endpoint, $user_setup_url); |
3403 | + } else { |
3404 | + return $this->_doIdRes($message, $endpoint, $return_to); |
3405 | + } |
3406 | + } |
3407 | + |
3408 | + /** |
3409 | + * @access private |
3410 | + */ |
3411 | + function _checkSetupNeeded($message) |
3412 | + { |
3413 | + // In OpenID 1, we check to see if this is a cancel from |
3414 | + // immediate mode by the presence of the user_setup_url |
3415 | + // parameter. |
3416 | + if ($message->isOpenID1()) { |
3417 | + $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS, |
3418 | + 'user_setup_url'); |
3419 | + if ($user_setup_url !== null) { |
3420 | + return true; |
3421 | + } |
3422 | + } |
3423 | + |
3424 | + return false; |
3425 | + } |
3426 | + |
3427 | + /** |
3428 | + * @access private |
3429 | + */ |
3430 | + function _doIdRes($message, $endpoint, $return_to) |
3431 | + { |
3432 | + // Checks for presence of appropriate fields (and checks |
3433 | + // signed list fields) |
3434 | + $result = $this->_idResCheckForFields($message); |
3435 | + |
3436 | + if (Auth_OpenID::isFailure($result)) { |
3437 | + return $result; |
3438 | + } |
3439 | + |
3440 | + if (!$this->_checkReturnTo($message, $return_to)) { |
3441 | + return new Auth_OpenID_FailureResponse(null, |
3442 | + sprintf("return_to does not match return URL. Expected %s, got %s", |
3443 | + $return_to, |
3444 | + $message->getArg(Auth_OpenID_OPENID_NS, 'return_to'))); |
3445 | + } |
3446 | + |
3447 | + // Verify discovery information: |
3448 | + $result = $this->_verifyDiscoveryResults($message, $endpoint); |
3449 | + |
3450 | + if (Auth_OpenID::isFailure($result)) { |
3451 | + return $result; |
3452 | + } |
3453 | + |
3454 | + $endpoint = $result; |
3455 | + |
3456 | + $result = $this->_idResCheckSignature($message, |
3457 | + $endpoint->server_url); |
3458 | + |
3459 | + if (Auth_OpenID::isFailure($result)) { |
3460 | + return $result; |
3461 | + } |
3462 | + |
3463 | + $result = $this->_idResCheckNonce($message, $endpoint); |
3464 | + |
3465 | + if (Auth_OpenID::isFailure($result)) { |
3466 | + return $result; |
3467 | + } |
3468 | + |
3469 | + $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, 'signed', |
3470 | + Auth_OpenID_NO_DEFAULT); |
3471 | + if (Auth_OpenID::isFailure($signed_list_str)) { |
3472 | + return $signed_list_str; |
3473 | + } |
3474 | + $signed_list = explode(',', $signed_list_str); |
3475 | + |
3476 | + $signed_fields = Auth_OpenID::addPrefix($signed_list, "openid."); |
3477 | + |
3478 | + return new Auth_OpenID_SuccessResponse($endpoint, $message, |
3479 | + $signed_fields); |
3480 | + |
3481 | + } |
3482 | + |
3483 | + /** |
3484 | + * @access private |
3485 | + */ |
3486 | + function _checkReturnTo($message, $return_to) |
3487 | + { |
3488 | + // Check an OpenID message and its openid.return_to value |
3489 | + // against a return_to URL from an application. Return True |
3490 | + // on success, False on failure. |
3491 | + |
3492 | + // Check the openid.return_to args against args in the |
3493 | + // original message. |
3494 | + $result = Auth_OpenID_GenericConsumer::_verifyReturnToArgs( |
3495 | + $message->toPostArgs()); |
3496 | + if (Auth_OpenID::isFailure($result)) { |
3497 | + return false; |
3498 | + } |
3499 | + |
3500 | + // Check the return_to base URL against the one in the |
3501 | + // message. |
3502 | + $msg_return_to = $message->getArg(Auth_OpenID_OPENID_NS, |
3503 | + 'return_to'); |
3504 | + if (Auth_OpenID::isFailure($return_to)) { |
3505 | + // XXX log me |
3506 | + return false; |
3507 | + } |
3508 | + |
3509 | + $return_to_parts = parse_url(Auth_OpenID_urinorm($return_to)); |
3510 | + $msg_return_to_parts = parse_url(Auth_OpenID_urinorm($msg_return_to)); |
3511 | + |
3512 | + // If port is absent from both, add it so it's equal in the |
3513 | + // check below. |
3514 | + if ((!array_key_exists('port', $return_to_parts)) && |
3515 | + (!array_key_exists('port', $msg_return_to_parts))) { |
3516 | + $return_to_parts['port'] = null; |
3517 | + $msg_return_to_parts['port'] = null; |
3518 | + } |
3519 | + |
3520 | + // If path is absent from both, add it so it's equal in the |
3521 | + // check below. |
3522 | + if ((!array_key_exists('path', $return_to_parts)) && |
3523 | + (!array_key_exists('path', $msg_return_to_parts))) { |
3524 | + $return_to_parts['path'] = null; |
3525 | + $msg_return_to_parts['path'] = null; |
3526 | + } |
3527 | + |
3528 | + // The URL scheme, authority, and path MUST be the same |
3529 | + // between the two URLs. |
3530 | + foreach (array('scheme', 'host', 'port', 'path') as $component) { |
3531 | + // If the url component is absent in either URL, fail. |
3532 | + // There should always be a scheme, host, port, and path. |
3533 | + if (!array_key_exists($component, $return_to_parts)) { |
3534 | + return false; |
3535 | + } |
3536 | + |
3537 | + if (!array_key_exists($component, $msg_return_to_parts)) { |
3538 | + return false; |
3539 | + } |
3540 | + |
3541 | + if (Auth_OpenID::arrayGet($return_to_parts, $component) !== |
3542 | + Auth_OpenID::arrayGet($msg_return_to_parts, $component)) { |
3543 | + return false; |
3544 | + } |
3545 | + } |
3546 | + |
3547 | + return true; |
3548 | + } |
3549 | + |
3550 | + /** |
3551 | + * @access private |
3552 | + */ |
3553 | + function _verifyReturnToArgs($query) |
3554 | + { |
3555 | + // Verify that the arguments in the return_to URL are present in this |
3556 | + // response. |
3557 | + |
3558 | + $message = Auth_OpenID_Message::fromPostArgs($query); |
3559 | + $return_to = $message->getArg(Auth_OpenID_OPENID_NS, 'return_to'); |
3560 | + |
3561 | + if (Auth_OpenID::isFailure($return_to)) { |
3562 | + return $return_to; |
3563 | + } |
3564 | + // XXX: this should be checked by _idResCheckForFields |
3565 | + if (!$return_to) { |
3566 | + return new Auth_OpenID_FailureResponse(null, |
3567 | + "Response has no return_to"); |
3568 | + } |
3569 | + |
3570 | + $parsed_url = parse_url($return_to); |
3571 | + |
3572 | + $q = array(); |
3573 | + if (array_key_exists('query', $parsed_url)) { |
3574 | + $rt_query = $parsed_url['query']; |
3575 | + $q = Auth_OpenID::parse_str($rt_query); |
3576 | + } |
3577 | + |
3578 | + foreach ($q as $rt_key => $rt_value) { |
3579 | + if (!array_key_exists($rt_key, $query)) { |
3580 | + return new Auth_OpenID_FailureResponse(null, |
3581 | + sprintf("return_to parameter %s absent from query", $rt_key)); |
3582 | + } else { |
3583 | + $value = $query[$rt_key]; |
3584 | + if ($rt_value != $value) { |
3585 | + return new Auth_OpenID_FailureResponse(null, |
3586 | + sprintf("parameter %s value %s does not match " . |
3587 | + "return_to value %s", $rt_key, |
3588 | + $value, $rt_value)); |
3589 | + } |
3590 | + } |
3591 | + } |
3592 | + |
3593 | + // Make sure all non-OpenID arguments in the response are also |
3594 | + // in the signed return_to. |
3595 | + $bare_args = $message->getArgs(Auth_OpenID_BARE_NS); |
3596 | + foreach ($bare_args as $key => $value) { |
3597 | + if (Auth_OpenID::arrayGet($q, $key) != $value) { |
3598 | + return new Auth_OpenID_FailureResponse(null, |
3599 | + sprintf("Parameter %s = %s not in return_to URL", |
3600 | + $key, $value)); |
3601 | + } |
3602 | + } |
3603 | + |
3604 | + return true; |
3605 | + } |
3606 | + |
3607 | + /** |
3608 | + * @access private |
3609 | + */ |
3610 | + function _idResCheckSignature($message, $server_url) |
3611 | + { |
3612 | + $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS, |
3613 | + 'assoc_handle'); |
3614 | + if (Auth_OpenID::isFailure($assoc_handle)) { |
3615 | + return $assoc_handle; |
3616 | + } |
3617 | + |
3618 | + $assoc = $this->store->getAssociation($server_url, $assoc_handle); |
3619 | + |
3620 | + if ($assoc) { |
3621 | + if ($assoc->getExpiresIn() <= 0) { |
3622 | + // XXX: It might be a good idea sometimes to re-start |
3623 | + // the authentication with a new association. Doing it |
3624 | + // automatically opens the possibility for |
3625 | + // denial-of-service by a server that just returns |
3626 | + // expired associations (or really short-lived |
3627 | + // associations) |
3628 | + return new Auth_OpenID_FailureResponse(null, |
3629 | + 'Association with ' . $server_url . ' expired'); |
3630 | + } |
3631 | + |
3632 | + if (!$assoc->checkMessageSignature($message)) { |
3633 | + return new Auth_OpenID_FailureResponse(null, |
3634 | + "Bad signature"); |
3635 | + } |
3636 | + } else { |
3637 | + // It's not an association we know about. Stateless mode |
3638 | + // is our only possible path for recovery. XXX - async |
3639 | + // framework will not want to block on this call to |
3640 | + // _checkAuth. |
3641 | + if (!$this->_checkAuth($message, $server_url)) { |
3642 | + return new Auth_OpenID_FailureResponse(null, |
3643 | + "Server denied check_authentication"); |
3644 | + } |
3645 | + } |
3646 | + |
3647 | + return null; |
3648 | + } |
3649 | + |
3650 | + /** |
3651 | + * @access private |
3652 | + */ |
3653 | + function _verifyDiscoveryResults($message, $endpoint=null) |
3654 | + { |
3655 | + if ($message->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS) { |
3656 | + return $this->_verifyDiscoveryResultsOpenID2($message, |
3657 | + $endpoint); |
3658 | + } else { |
3659 | + return $this->_verifyDiscoveryResultsOpenID1($message, |
3660 | + $endpoint); |
3661 | + } |
3662 | + } |
3663 | + |
3664 | + /** |
3665 | + * @access private |
3666 | + */ |
3667 | + function _verifyDiscoveryResultsOpenID1($message, $endpoint) |
3668 | + { |
3669 | + $claimed_id = $message->getArg(Auth_OpenID_BARE_NS, |
3670 | + $this->openid1_return_to_identifier_name); |
3671 | + |
3672 | + if (($endpoint === null) && ($claimed_id === null)) { |
3673 | + return new Auth_OpenID_FailureResponse($endpoint, |
3674 | + 'When using OpenID 1, the claimed ID must be supplied, ' . |
3675 | + 'either by passing it through as a return_to parameter ' . |
3676 | + 'or by using a session, and supplied to the GenericConsumer ' . |
3677 | + 'as the argument to complete()'); |
3678 | + } else if (($endpoint !== null) && ($claimed_id === null)) { |
3679 | + $claimed_id = $endpoint->claimed_id; |
3680 | + } |
3681 | + |
3682 | + $to_match = new Auth_OpenID_ServiceEndpoint(); |
3683 | + $to_match->type_uris = array(Auth_OpenID_TYPE_1_1); |
3684 | + $to_match->local_id = $message->getArg(Auth_OpenID_OPENID1_NS, |
3685 | + 'identity'); |
3686 | + |
3687 | + // Restore delegate information from the initiation phase |
3688 | + $to_match->claimed_id = $claimed_id; |
3689 | + |
3690 | + if ($to_match->local_id === null) { |
3691 | + return new Auth_OpenID_FailureResponse($endpoint, |
3692 | + "Missing required field openid.identity"); |
3693 | + } |
3694 | + |
3695 | + $to_match_1_0 = $to_match->copy(); |
3696 | + $to_match_1_0->type_uris = array(Auth_OpenID_TYPE_1_0); |
3697 | + |
3698 | + if ($endpoint !== null) { |
3699 | + $result = $this->_verifyDiscoverySingle($endpoint, $to_match); |
3700 | + |
3701 | + if (is_a($result, 'Auth_OpenID_TypeURIMismatch')) { |
3702 | + $result = $this->_verifyDiscoverySingle($endpoint, |
3703 | + $to_match_1_0); |
3704 | + } |
3705 | + |
3706 | + if (Auth_OpenID::isFailure($result)) { |
3707 | + // oidutil.log("Error attempting to use stored |
3708 | + // discovery information: " + str(e)) |
3709 | + // oidutil.log("Attempting discovery to |
3710 | + // verify endpoint") |
3711 | + } else { |
3712 | + return $endpoint; |
3713 | + } |
3714 | + } |
3715 | + |
3716 | + // Endpoint is either bad (failed verification) or None |
3717 | + return $this->_discoverAndVerify($to_match->claimed_id, |
3718 | + array($to_match, $to_match_1_0)); |
3719 | + } |
3720 | + |
3721 | + /** |
3722 | + * @access private |
3723 | + */ |
3724 | + function _verifyDiscoverySingle($endpoint, $to_match) |
3725 | + { |
3726 | + // Every type URI that's in the to_match endpoint has to be |
3727 | + // present in the discovered endpoint. |
3728 | + foreach ($to_match->type_uris as $type_uri) { |
3729 | + if (!$endpoint->usesExtension($type_uri)) { |
3730 | + return new Auth_OpenID_TypeURIMismatch($endpoint, |
3731 | + "Required type ".$type_uri." not present"); |
3732 | + } |
3733 | + } |
3734 | + |
3735 | + // Fragments do not influence discovery, so we can't compare a |
3736 | + // claimed identifier with a fragment to discovered |
3737 | + // information. |
3738 | + list($defragged_claimed_id, $_) = |
3739 | + Auth_OpenID::urldefrag($to_match->claimed_id); |
3740 | + |
3741 | + if ($defragged_claimed_id != $endpoint->claimed_id) { |
3742 | + return new Auth_OpenID_FailureResponse($endpoint, |
3743 | + sprintf('Claimed ID does not match (different subjects!), ' . |
3744 | + 'Expected %s, got %s', $defragged_claimed_id, |
3745 | + $endpoint->claimed_id)); |
3746 | + } |
3747 | + |
3748 | + if ($to_match->getLocalID() != $endpoint->getLocalID()) { |
3749 | + return new Auth_OpenID_FailureResponse($endpoint, |
3750 | + sprintf('local_id mismatch. Expected %s, got %s', |
3751 | + $to_match->getLocalID(), $endpoint->getLocalID())); |
3752 | + } |
3753 | + |
3754 | + // If the server URL is None, this must be an OpenID 1 |
3755 | + // response, because op_endpoint is a required parameter in |
3756 | + // OpenID 2. In that case, we don't actually care what the |
3757 | + // discovered server_url is, because signature checking or |
3758 | + // check_auth should take care of that check for us. |
3759 | + if ($to_match->server_url === null) { |
3760 | + if ($to_match->preferredNamespace() != Auth_OpenID_OPENID1_NS) { |
3761 | + return new Auth_OpenID_FailureResponse($endpoint, |
3762 | + "Preferred namespace mismatch (bug)"); |
3763 | + } |
3764 | + } else if ($to_match->server_url != $endpoint->server_url) { |
3765 | + return new Auth_OpenID_FailureResponse($endpoint, |
3766 | + sprintf('OP Endpoint mismatch. Expected %s, got %s', |
3767 | + $to_match->server_url, $endpoint->server_url)); |
3768 | + } |
3769 | + |
3770 | + return null; |
3771 | + } |
3772 | + |
3773 | + /** |
3774 | + * @access private |
3775 | + */ |
3776 | + function _verifyDiscoveryResultsOpenID2($message, $endpoint) |
3777 | + { |
3778 | + $to_match = new Auth_OpenID_ServiceEndpoint(); |
3779 | + $to_match->type_uris = array(Auth_OpenID_TYPE_2_0); |
3780 | + $to_match->claimed_id = $message->getArg(Auth_OpenID_OPENID2_NS, |
3781 | + 'claimed_id'); |
3782 | + |
3783 | + $to_match->local_id = $message->getArg(Auth_OpenID_OPENID2_NS, |
3784 | + 'identity'); |
3785 | + |
3786 | + $to_match->server_url = $message->getArg(Auth_OpenID_OPENID2_NS, |
3787 | + 'op_endpoint'); |
3788 | + |
3789 | + if ($to_match->server_url === null) { |
3790 | + return new Auth_OpenID_FailureResponse($endpoint, |
3791 | + "OP Endpoint URL missing"); |
3792 | + } |
3793 | + |
3794 | + // claimed_id and identifier must both be present or both be |
3795 | + // absent |
3796 | + if (($to_match->claimed_id === null) && |
3797 | + ($to_match->local_id !== null)) { |
3798 | + return new Auth_OpenID_FailureResponse($endpoint, |
3799 | + 'openid.identity is present without openid.claimed_id'); |
3800 | + } |
3801 | + |
3802 | + if (($to_match->claimed_id !== null) && |
3803 | + ($to_match->local_id === null)) { |
3804 | + return new Auth_OpenID_FailureResponse($endpoint, |
3805 | + 'openid.claimed_id is present without openid.identity'); |
3806 | + } |
3807 | + |
3808 | + if ($to_match->claimed_id === null) { |
3809 | + // This is a response without identifiers, so there's |
3810 | + // really no checking that we can do, so return an |
3811 | + // endpoint that's for the specified `openid.op_endpoint' |
3812 | + return Auth_OpenID_ServiceEndpoint::fromOPEndpointURL( |
3813 | + $to_match->server_url); |
3814 | + } |
3815 | + |
3816 | + if (!$endpoint) { |
3817 | + // The claimed ID doesn't match, so we have to do |
3818 | + // discovery again. This covers not using sessions, OP |
3819 | + // identifier endpoints and responses that didn't match |
3820 | + // the original request. |
3821 | + // oidutil.log('No pre-discovered information supplied.') |
3822 | + return $this->_discoverAndVerify($to_match->claimed_id, |
3823 | + array($to_match)); |
3824 | + } else { |
3825 | + |
3826 | + // The claimed ID matches, so we use the endpoint that we |
3827 | + // discovered in initiation. This should be the most |
3828 | + // common case. |
3829 | + $result = $this->_verifyDiscoverySingle($endpoint, $to_match); |
3830 | + |
3831 | + if (Auth_OpenID::isFailure($result)) { |
3832 | + $endpoint = $this->_discoverAndVerify($to_match->claimed_id, |
3833 | + array($to_match)); |
3834 | + if (Auth_OpenID::isFailure($endpoint)) { |
3835 | + return $endpoint; |
3836 | + } |
3837 | + } |
3838 | + } |
3839 | + |
3840 | + // The endpoint we return should have the claimed ID from the |
3841 | + // message we just verified, fragment and all. |
3842 | + if ($endpoint->claimed_id != $to_match->claimed_id) { |
3843 | + $endpoint->claimed_id = $to_match->claimed_id; |
3844 | + } |
3845 | + |
3846 | + return $endpoint; |
3847 | + } |
3848 | + |
3849 | + /** |
3850 | + * @access private |
3851 | + */ |
3852 | + function _discoverAndVerify($claimed_id, $to_match_endpoints) |
3853 | + { |
3854 | + // oidutil.log('Performing discovery on %s' % (claimed_id,)) |
3855 | + list($unused, $services) = call_user_func($this->discoverMethod, |
3856 | + $claimed_id, |
3857 | + &$this->fetcher); |
3858 | + |
3859 | + if (!$services) { |
3860 | + return new Auth_OpenID_FailureResponse(null, |
3861 | + sprintf("No OpenID information found at %s", |
3862 | + $claimed_id)); |
3863 | + } |
3864 | + |
3865 | + return $this->_verifyDiscoveryServices($claimed_id, $services, |
3866 | + $to_match_endpoints); |
3867 | + } |
3868 | + |
3869 | + /** |
3870 | + * @access private |
3871 | + */ |
3872 | + function _verifyDiscoveryServices($claimed_id, |
3873 | + $services, $to_match_endpoints) |
3874 | + { |
3875 | + // Search the services resulting from discovery to find one |
3876 | + // that matches the information from the assertion |
3877 | + |
3878 | + foreach ($services as $endpoint) { |
3879 | + foreach ($to_match_endpoints as $to_match_endpoint) { |
3880 | + $result = $this->_verifyDiscoverySingle($endpoint, |
3881 | + $to_match_endpoint); |
3882 | + |
3883 | + if (!Auth_OpenID::isFailure($result)) { |
3884 | + // It matches, so discover verification has |
3885 | + // succeeded. Return this endpoint. |
3886 | + return $endpoint; |
3887 | + } |
3888 | + } |
3889 | + } |
3890 | + |
3891 | + return new Auth_OpenID_FailureResponse(null, |
3892 | + sprintf('No matching endpoint found after discovering %s: %s', |
3893 | + $claimed_id, $result->message)); |
3894 | + } |
3895 | + |
3896 | + /** |
3897 | + * Extract the nonce from an OpenID 1 response. Return the nonce |
3898 | + * from the BARE_NS since we independently check the return_to |
3899 | + * arguments are the same as those in the response message. |
3900 | + * |
3901 | + * See the openid1_nonce_query_arg_name class variable |
3902 | + * |
3903 | + * @returns $nonce The nonce as a string or null |
3904 | + * |
3905 | + * @access private |
3906 | + */ |
3907 | + function _idResGetNonceOpenID1($message, $endpoint) |
3908 | + { |
3909 | + return $message->getArg(Auth_OpenID_BARE_NS, |
3910 | + $this->openid1_nonce_query_arg_name); |
3911 | + } |
3912 | + |
3913 | + /** |
3914 | + * @access private |
3915 | + */ |
3916 | + function _idResCheckNonce($message, $endpoint) |
3917 | + { |
3918 | + if ($message->isOpenID1()) { |
3919 | + // This indicates that the nonce was generated by the consumer |
3920 | + $nonce = $this->_idResGetNonceOpenID1($message, $endpoint); |
3921 | + $server_url = ''; |
3922 | + } else { |
3923 | + $nonce = $message->getArg(Auth_OpenID_OPENID2_NS, |
3924 | + 'response_nonce'); |
3925 | + |
3926 | + $server_url = $endpoint->server_url; |
3927 | + } |
3928 | + |
3929 | + if ($nonce === null) { |
3930 | + return new Auth_OpenID_FailureResponse($endpoint, |
3931 | + "Nonce missing from response"); |
3932 | + } |
3933 | + |
3934 | + $parts = Auth_OpenID_splitNonce($nonce); |
3935 | + |
3936 | + if ($parts === null) { |
3937 | + return new Auth_OpenID_FailureResponse($endpoint, |
3938 | + "Malformed nonce in response"); |
3939 | + } |
3940 | + |
3941 | + list($timestamp, $salt) = $parts; |
3942 | + |
3943 | + if (!$this->store->useNonce($server_url, $timestamp, $salt)) { |
3944 | + return new Auth_OpenID_FailureResponse($endpoint, |
3945 | + "Nonce already used or out of range"); |
3946 | + } |
3947 | + |
3948 | + return null; |
3949 | + } |
3950 | + |
3951 | + /** |
3952 | + * @access private |
3953 | + */ |
3954 | + function _idResCheckForFields($message) |
3955 | + { |
3956 | + $basic_fields = array('return_to', 'assoc_handle', 'sig', 'signed'); |
3957 | + $basic_sig_fields = array('return_to', 'identity'); |
3958 | + |
3959 | + $require_fields = array( |
3960 | + Auth_OpenID_OPENID2_NS => array_merge($basic_fields, |
3961 | + array('op_endpoint')), |
3962 | + |
3963 | + Auth_OpenID_OPENID1_NS => array_merge($basic_fields, |
3964 | + array('identity')) |
3965 | + ); |
3966 | + |
3967 | + $require_sigs = array( |
3968 | + Auth_OpenID_OPENID2_NS => array_merge($basic_sig_fields, |
3969 | + array('response_nonce', |
3970 | + 'claimed_id', |
3971 | + 'assoc_handle', |
3972 | + 'op_endpoint')), |
3973 | + Auth_OpenID_OPENID1_NS => array_merge($basic_sig_fields, |
3974 | + array('nonce')) |
3975 | + ); |
3976 | + |
3977 | + foreach ($require_fields[$message->getOpenIDNamespace()] as $field) { |
3978 | + if (!$message->hasKey(Auth_OpenID_OPENID_NS, $field)) { |
3979 | + return new Auth_OpenID_FailureResponse(null, |
3980 | + "Missing required field '".$field."'"); |
3981 | + } |
3982 | + } |
3983 | + |
3984 | + $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, |
3985 | + 'signed', |
3986 | + Auth_OpenID_NO_DEFAULT); |
3987 | + if (Auth_OpenID::isFailure($signed_list_str)) { |
3988 | + return $signed_list_str; |
3989 | + } |
3990 | + $signed_list = explode(',', $signed_list_str); |
3991 | + |
3992 | + foreach ($require_sigs[$message->getOpenIDNamespace()] as $field) { |
3993 | + // Field is present and not in signed list |
3994 | + if ($message->hasKey(Auth_OpenID_OPENID_NS, $field) && |
3995 | + (!in_array($field, $signed_list))) { |
3996 | + return new Auth_OpenID_FailureResponse(null, |
3997 | + "'".$field."' not signed"); |
3998 | + } |
3999 | + } |
4000 | + |
4001 | + return null; |
4002 | + } |
4003 | + |
4004 | + /** |
4005 | + * @access private |
4006 | + */ |
4007 | + function _checkAuth($message, $server_url) |
4008 | + { |
4009 | + $request = $this->_createCheckAuthRequest($message); |
4010 | + if ($request === null) { |
4011 | + return false; |
4012 | + } |
4013 | + |
4014 | + $resp_message = $this->_makeKVPost($request, $server_url); |
4015 | + if (($resp_message === null) || |
4016 | + (is_a($resp_message, 'Auth_OpenID_ServerErrorContainer'))) { |
4017 | + return false; |
4018 | + } |
4019 | + |
4020 | + return $this->_processCheckAuthResponse($resp_message, $server_url); |
4021 | + } |
4022 | + |
4023 | + /** |
4024 | + * @access private |
4025 | + */ |
4026 | + function _createCheckAuthRequest($message) |
4027 | + { |
4028 | + $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed'); |
4029 | + if ($signed) { |
4030 | + foreach (explode(',', $signed) as $k) { |
4031 | + $value = $message->getAliasedArg($k); |
4032 | + if ($value === null) { |
4033 | + return null; |
4034 | + } |
4035 | + } |
4036 | + } |
4037 | + $ca_message = $message->copy(); |
4038 | + $ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode', |
4039 | + 'check_authentication'); |
4040 | + return $ca_message; |
4041 | + } |
4042 | + |
4043 | + /** |
4044 | + * @access private |
4045 | + */ |
4046 | + function _processCheckAuthResponse($response, $server_url) |
4047 | + { |
4048 | + $is_valid = $response->getArg(Auth_OpenID_OPENID_NS, 'is_valid', |
4049 | + 'false'); |
4050 | + |
4051 | + $invalidate_handle = $response->getArg(Auth_OpenID_OPENID_NS, |
4052 | + 'invalidate_handle'); |
4053 | + |
4054 | + if ($invalidate_handle !== null) { |
4055 | + $this->store->removeAssociation($server_url, |
4056 | + $invalidate_handle); |
4057 | + } |
4058 | + |
4059 | + if ($is_valid == 'true') { |
4060 | + return true; |
4061 | + } |
4062 | + |
4063 | + return false; |
4064 | + } |
4065 | + |
4066 | + /** |
4067 | + * Adapt a POST response to a Message. |
4068 | + * |
4069 | + * @param $response Result of a POST to an OpenID endpoint. |
4070 | + * |
4071 | + * @access private |
4072 | + */ |
4073 | + static function _httpResponseToMessage($response, $server_url) |
4074 | + { |
4075 | + // Should this function be named Message.fromHTTPResponse instead? |
4076 | + $response_message = Auth_OpenID_Message::fromKVForm($response->body); |
4077 | + |
4078 | + if ($response->status == 400) { |
4079 | + return Auth_OpenID_ServerErrorContainer::fromMessage( |
4080 | + $response_message); |
4081 | + } else if ($response->status != 200 and $response->status != 206) { |
4082 | + return null; |
4083 | + } |
4084 | + |
4085 | + return $response_message; |
4086 | + } |
4087 | + |
4088 | + /** |
4089 | + * @access private |
4090 | + */ |
4091 | + function _makeKVPost($message, $server_url) |
4092 | + { |
4093 | + $body = $message->toURLEncoded(); |
4094 | + $resp = $this->fetcher->post($server_url, $body); |
4095 | + |
4096 | + if ($resp === null) { |
4097 | + return null; |
4098 | + } |
4099 | + |
4100 | + return $this->_httpResponseToMessage($resp, $server_url); |
4101 | + } |
4102 | + |
4103 | + /** |
4104 | + * @access private |
4105 | + */ |
4106 | + function _getAssociation($endpoint) |
4107 | + { |
4108 | + if (!$this->_use_assocs) { |
4109 | + return null; |
4110 | + } |
4111 | + |
4112 | + $assoc = $this->store->getAssociation($endpoint->server_url); |
4113 | + |
4114 | + if (($assoc === null) || |
4115 | + ($assoc->getExpiresIn() <= 0)) { |
4116 | + |
4117 | + $assoc = $this->_negotiateAssociation($endpoint); |
4118 | + |
4119 | + if ($assoc !== null) { |
4120 | + $this->store->storeAssociation($endpoint->server_url, |
4121 | + $assoc); |
4122 | + } |
4123 | + } |
4124 | + |
4125 | + return $assoc; |
4126 | + } |
4127 | + |
4128 | + /** |
4129 | + * Handle ServerErrors resulting from association requests. |
4130 | + * |
4131 | + * @return $result If server replied with an C{unsupported-type} |
4132 | + * error, return a tuple of supported C{association_type}, |
4133 | + * C{session_type}. Otherwise logs the error and returns null. |
4134 | + * |
4135 | + * @access private |
4136 | + */ |
4137 | + function _extractSupportedAssociationType($server_error, $endpoint, |
4138 | + $assoc_type) |
4139 | + { |
4140 | + // Any error message whose code is not 'unsupported-type' |
4141 | + // should be considered a total failure. |
4142 | + if (($server_error->error_code != 'unsupported-type') || |
4143 | + ($server_error->message->isOpenID1())) { |
4144 | + return null; |
4145 | + } |
4146 | + |
4147 | + // The server didn't like the association/session type that we |
4148 | + // sent, and it sent us back a message that might tell us how |
4149 | + // to handle it. |
4150 | + |
4151 | + // Extract the session_type and assoc_type from the error |
4152 | + // message |
4153 | + $assoc_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS, |
4154 | + 'assoc_type'); |
4155 | + |
4156 | + $session_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS, |
4157 | + 'session_type'); |
4158 | + |
4159 | + if (($assoc_type === null) || ($session_type === null)) { |
4160 | + return null; |
4161 | + } else if (!$this->negotiator->isAllowed($assoc_type, |
4162 | + $session_type)) { |
4163 | + return null; |
4164 | + } else { |
4165 | + return array($assoc_type, $session_type); |
4166 | + } |
4167 | + } |
4168 | + |
4169 | + /** |
4170 | + * @access private |
4171 | + */ |
4172 | + function _negotiateAssociation($endpoint) |
4173 | + { |
4174 | + // Get our preferred session/association type from the negotiatior. |
4175 | + list($assoc_type, $session_type) = $this->negotiator->getAllowedType(); |
4176 | + |
4177 | + $assoc = $this->_requestAssociation( |
4178 | + $endpoint, $assoc_type, $session_type); |
4179 | + |
4180 | + if (Auth_OpenID::isFailure($assoc)) { |
4181 | + return null; |
4182 | + } |
4183 | + |
4184 | + if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) { |
4185 | + $why = $assoc; |
4186 | + |
4187 | + $supportedTypes = $this->_extractSupportedAssociationType( |
4188 | + $why, $endpoint, $assoc_type); |
4189 | + |
4190 | + if ($supportedTypes !== null) { |
4191 | + list($assoc_type, $session_type) = $supportedTypes; |
4192 | + |
4193 | + // Attempt to create an association from the assoc_type |
4194 | + // and session_type that the server told us it |
4195 | + // supported. |
4196 | + $assoc = $this->_requestAssociation( |
4197 | + $endpoint, $assoc_type, $session_type); |
4198 | + |
4199 | + if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) { |
4200 | + // Do not keep trying, since it rejected the |
4201 | + // association type that it told us to use. |
4202 | + // oidutil.log('Server %s refused its suggested association |
4203 | + // 'type: session_type=%s, assoc_type=%s' |
4204 | + // % (endpoint.server_url, session_type, |
4205 | + // assoc_type)) |
4206 | + return null; |
4207 | + } else { |
4208 | + return $assoc; |
4209 | + } |
4210 | + } else { |
4211 | + return null; |
4212 | + } |
4213 | + } else { |
4214 | + return $assoc; |
4215 | + } |
4216 | + } |
4217 | + |
4218 | + /** |
4219 | + * @access private |
4220 | + */ |
4221 | + function _requestAssociation($endpoint, $assoc_type, $session_type) |
4222 | + { |
4223 | + list($assoc_session, $args) = $this->_createAssociateRequest( |
4224 | + $endpoint, $assoc_type, $session_type); |
4225 | + |
4226 | + $response_message = $this->_makeKVPost($args, $endpoint->server_url); |
4227 | + |
4228 | + if ($response_message === null) { |
4229 | + // oidutil.log('openid.associate request failed: %s' % (why[0],)) |
4230 | + return null; |
4231 | + } else if (is_a($response_message, |
4232 | + 'Auth_OpenID_ServerErrorContainer')) { |
4233 | + return $response_message; |
4234 | + } |
4235 | + |
4236 | + return $this->_extractAssociation($response_message, $assoc_session); |
4237 | + } |
4238 | + |
4239 | + /** |
4240 | + * @access private |
4241 | + */ |
4242 | + function _extractAssociation($assoc_response, $assoc_session) |
4243 | + { |
4244 | + // Extract the common fields from the response, raising an |
4245 | + // exception if they are not found |
4246 | + $assoc_type = $assoc_response->getArg( |
4247 | + Auth_OpenID_OPENID_NS, 'assoc_type', |
4248 | + Auth_OpenID_NO_DEFAULT); |
4249 | + |
4250 | + if (Auth_OpenID::isFailure($assoc_type)) { |
4251 | + return $assoc_type; |
4252 | + } |
4253 | + |
4254 | + $assoc_handle = $assoc_response->getArg( |
4255 | + Auth_OpenID_OPENID_NS, 'assoc_handle', |
4256 | + Auth_OpenID_NO_DEFAULT); |
4257 | + |
4258 | + if (Auth_OpenID::isFailure($assoc_handle)) { |
4259 | + return $assoc_handle; |
4260 | + } |
4261 | + |
4262 | + // expires_in is a base-10 string. The Python parsing will |
4263 | + // accept literals that have whitespace around them and will |
4264 | + // accept negative values. Neither of these are really in-spec, |
4265 | + // but we think it's OK to accept them. |
4266 | + $expires_in_str = $assoc_response->getArg( |
4267 | + Auth_OpenID_OPENID_NS, 'expires_in', |
4268 | + Auth_OpenID_NO_DEFAULT); |
4269 | + |
4270 | + if (Auth_OpenID::isFailure($expires_in_str)) { |
4271 | + return $expires_in_str; |
4272 | + } |
4273 | + |
4274 | + $expires_in = Auth_OpenID::intval($expires_in_str); |
4275 | + if ($expires_in === false) { |
4276 | + |
4277 | + $err = sprintf("Could not parse expires_in from association ". |
4278 | + "response %s", print_r($assoc_response, true)); |
4279 | + return new Auth_OpenID_FailureResponse(null, $err); |
4280 | + } |
4281 | + |
4282 | + // OpenID 1 has funny association session behaviour. |
4283 | + if ($assoc_response->isOpenID1()) { |
4284 | + $session_type = $this->_getOpenID1SessionType($assoc_response); |
4285 | + } else { |
4286 | + $session_type = $assoc_response->getArg( |
4287 | + Auth_OpenID_OPENID2_NS, 'session_type', |
4288 | + Auth_OpenID_NO_DEFAULT); |
4289 | + |
4290 | + if (Auth_OpenID::isFailure($session_type)) { |
4291 | + return $session_type; |
4292 | + } |
4293 | + } |
4294 | + |
4295 | + // Session type mismatch |
4296 | + if ($assoc_session->session_type != $session_type) { |
4297 | + if ($assoc_response->isOpenID1() && |
4298 | + ($session_type == 'no-encryption')) { |
4299 | + // In OpenID 1, any association request can result in |
4300 | + // a 'no-encryption' association response. Setting |
4301 | + // assoc_session to a new no-encryption session should |
4302 | + // make the rest of this function work properly for |
4303 | + // that case. |
4304 | + $assoc_session = new Auth_OpenID_PlainTextConsumerSession(); |
4305 | + } else { |
4306 | + // Any other mismatch, regardless of protocol version |
4307 | + // results in the failure of the association session |
4308 | + // altogether. |
4309 | + return null; |
4310 | + } |
4311 | + } |
4312 | + |
4313 | + // Make sure assoc_type is valid for session_type |
4314 | + if (!in_array($assoc_type, $assoc_session->allowed_assoc_types)) { |
4315 | + return null; |
4316 | + } |
4317 | + |
4318 | + // Delegate to the association session to extract the secret |
4319 | + // from the response, however is appropriate for that session |
4320 | + // type. |
4321 | + $secret = $assoc_session->extractSecret($assoc_response); |
4322 | + |
4323 | + if ($secret === null) { |
4324 | + return null; |
4325 | + } |
4326 | + |
4327 | + return Auth_OpenID_Association::fromExpiresIn( |
4328 | + $expires_in, $assoc_handle, $secret, $assoc_type); |
4329 | + } |
4330 | + |
4331 | + /** |
4332 | + * @access private |
4333 | + */ |
4334 | + function _createAssociateRequest($endpoint, $assoc_type, $session_type) |
4335 | + { |
4336 | + if (array_key_exists($session_type, $this->session_types)) { |
4337 | + $session_type_class = $this->session_types[$session_type]; |
4338 | + |
4339 | + if (is_callable($session_type_class)) { |
4340 | + $assoc_session = $session_type_class(); |
4341 | + } else { |
4342 | + $assoc_session = new $session_type_class(); |
4343 | + } |
4344 | + } else { |
4345 | + return null; |
4346 | + } |
4347 | + |
4348 | + $args = array( |
4349 | + 'mode' => 'associate', |
4350 | + 'assoc_type' => $assoc_type); |
4351 | + |
4352 | + if (!$endpoint->compatibilityMode()) { |
4353 | + $args['ns'] = Auth_OpenID_OPENID2_NS; |
4354 | + } |
4355 | + |
4356 | + // Leave out the session type if we're in compatibility mode |
4357 | + // *and* it's no-encryption. |
4358 | + if ((!$endpoint->compatibilityMode()) || |
4359 | + ($assoc_session->session_type != 'no-encryption')) { |
4360 | + $args['session_type'] = $assoc_session->session_type; |
4361 | + } |
4362 | + |
4363 | + $args = array_merge($args, $assoc_session->getRequest()); |
4364 | + $message = Auth_OpenID_Message::fromOpenIDArgs($args); |
4365 | + return array($assoc_session, $message); |
4366 | + } |
4367 | + |
4368 | + /** |
4369 | + * Given an association response message, extract the OpenID 1.X |
4370 | + * session type. |
4371 | + * |
4372 | + * This function mostly takes care of the 'no-encryption' default |
4373 | + * behavior in OpenID 1. |
4374 | + * |
4375 | + * If the association type is plain-text, this function will |
4376 | + * return 'no-encryption' |
4377 | + * |
4378 | + * @access private |
4379 | + * @return $typ The association type for this message |
4380 | + */ |
4381 | + function _getOpenID1SessionType($assoc_response) |
4382 | + { |
4383 | + // If it's an OpenID 1 message, allow session_type to default |
4384 | + // to None (which signifies "no-encryption") |
4385 | + $session_type = $assoc_response->getArg(Auth_OpenID_OPENID1_NS, |
4386 | + 'session_type'); |
4387 | + |
4388 | + // Handle the differences between no-encryption association |
4389 | + // respones in OpenID 1 and 2: |
4390 | + |
4391 | + // no-encryption is not really a valid session type for OpenID |
4392 | + // 1, but we'll accept it anyway, while issuing a warning. |
4393 | + if ($session_type == 'no-encryption') { |
4394 | + // oidutil.log('WARNING: OpenID server sent "no-encryption"' |
4395 | + // 'for OpenID 1.X') |
4396 | + } else if (($session_type == '') || ($session_type === null)) { |
4397 | + // Missing or empty session type is the way to flag a |
4398 | + // 'no-encryption' response. Change the session type to |
4399 | + // 'no-encryption' so that it can be handled in the same |
4400 | + // way as OpenID 2 'no-encryption' respones. |
4401 | + $session_type = 'no-encryption'; |
4402 | + } |
4403 | + |
4404 | + return $session_type; |
4405 | + } |
4406 | +} |
4407 | + |
4408 | +/** |
4409 | + * This class represents an authentication request from a consumer to |
4410 | + * an OpenID server. |
4411 | + * |
4412 | + * @package OpenID |
4413 | + */ |
4414 | +class Auth_OpenID_AuthRequest { |
4415 | + |
4416 | + /** |
4417 | + * Initialize an authentication request with the specified token, |
4418 | + * association, and endpoint. |
4419 | + * |
4420 | + * Users of this library should not create instances of this |
4421 | + * class. Instances of this class are created by the library when |
4422 | + * needed. |
4423 | + */ |
4424 | + function Auth_OpenID_AuthRequest($endpoint, $assoc) |
4425 | + { |
4426 | + $this->assoc = $assoc; |
4427 | + $this->endpoint = $endpoint; |
4428 | + $this->return_to_args = array(); |
4429 | + $this->message = new Auth_OpenID_Message( |
4430 | + $endpoint->preferredNamespace()); |
4431 | + $this->_anonymous = false; |
4432 | + } |
4433 | + |
4434 | + /** |
4435 | + * Add an extension to this checkid request. |
4436 | + * |
4437 | + * $extension_request: An object that implements the extension |
4438 | + * request interface for adding arguments to an OpenID message. |
4439 | + */ |
4440 | + function addExtension($extension_request) |
4441 | + { |
4442 | + $extension_request->toMessage($this->message); |
4443 | + } |
4444 | + |
4445 | + /** |
4446 | + * Add an extension argument to this OpenID authentication |
4447 | + * request. |
4448 | + * |
4449 | + * Use caution when adding arguments, because they will be |
4450 | + * URL-escaped and appended to the redirect URL, which can easily |
4451 | + * get quite long. |
4452 | + * |
4453 | + * @param string $namespace The namespace for the extension. For |
4454 | + * example, the simple registration extension uses the namespace |
4455 | + * 'sreg'. |
4456 | + * |
4457 | + * @param string $key The key within the extension namespace. For |
4458 | + * example, the nickname field in the simple registration |
4459 | + * extension's key is 'nickname'. |
4460 | + * |
4461 | + * @param string $value The value to provide to the server for |
4462 | + * this argument. |
4463 | + */ |
4464 | + function addExtensionArg($namespace, $key, $value) |
4465 | + { |
4466 | + return $this->message->setArg($namespace, $key, $value); |
4467 | + } |
4468 | + |
4469 | + /** |
4470 | + * Set whether this request should be made anonymously. If a |
4471 | + * request is anonymous, the identifier will not be sent in the |
4472 | + * request. This is only useful if you are making another kind of |
4473 | + * request with an extension in this request. |
4474 | + * |
4475 | + * Anonymous requests are not allowed when the request is made |
4476 | + * with OpenID 1. |
4477 | + */ |
4478 | + function setAnonymous($is_anonymous) |
4479 | + { |
4480 | + if ($is_anonymous && $this->message->isOpenID1()) { |
4481 | + return false; |
4482 | + } else { |
4483 | + $this->_anonymous = $is_anonymous; |
4484 | + return true; |
4485 | + } |
4486 | + } |
4487 | + |
4488 | + /** |
4489 | + * Produce a {@link Auth_OpenID_Message} representing this |
4490 | + * request. |
4491 | + * |
4492 | + * @param string $realm The URL (or URL pattern) that identifies |
4493 | + * your web site to the user when she is authorizing it. |
4494 | + * |
4495 | + * @param string $return_to The URL that the OpenID provider will |
4496 | + * send the user back to after attempting to verify her identity. |
4497 | + * |
4498 | + * Not specifying a return_to URL means that the user will not be |
4499 | + * returned to the site issuing the request upon its completion. |
4500 | + * |
4501 | + * @param bool $immediate If true, the OpenID provider is to send |
4502 | + * back a response immediately, useful for behind-the-scenes |
4503 | + * authentication attempts. Otherwise the OpenID provider may |
4504 | + * engage the user before providing a response. This is the |
4505 | + * default case, as the user may need to provide credentials or |
4506 | + * approve the request before a positive response can be sent. |
4507 | + */ |
4508 | + function getMessage($realm, $return_to=null, $immediate=false) |
4509 | + { |
4510 | + if ($return_to) { |
4511 | + $return_to = Auth_OpenID::appendArgs($return_to, |
4512 | + $this->return_to_args); |
4513 | + } else if ($immediate) { |
4514 | + // raise ValueError( |
4515 | + // '"return_to" is mandatory when |
4516 | + //using "checkid_immediate"') |
4517 | + return new Auth_OpenID_FailureResponse(null, |
4518 | + "'return_to' is mandatory when using checkid_immediate"); |
4519 | + } else if ($this->message->isOpenID1()) { |
4520 | + // raise ValueError('"return_to" is |
4521 | + // mandatory for OpenID 1 requests') |
4522 | + return new Auth_OpenID_FailureResponse(null, |
4523 | + "'return_to' is mandatory for OpenID 1 requests"); |
4524 | + } else if ($this->return_to_args) { |
4525 | + // raise ValueError('extra "return_to" arguments |
4526 | + // were specified, but no return_to was specified') |
4527 | + return new Auth_OpenID_FailureResponse(null, |
4528 | + "extra 'return_to' arguments where specified, " . |
4529 | + "but no return_to was specified"); |
4530 | + } |
4531 | + |
4532 | + if ($immediate) { |
4533 | + $mode = 'checkid_immediate'; |
4534 | + } else { |
4535 | + $mode = 'checkid_setup'; |
4536 | + } |
4537 | + |
4538 | + $message = $this->message->copy(); |
4539 | + if ($message->isOpenID1()) { |
4540 | + $realm_key = 'trust_root'; |
4541 | + } else { |
4542 | + $realm_key = 'realm'; |
4543 | + } |
4544 | + |
4545 | + $message->updateArgs(Auth_OpenID_OPENID_NS, |
4546 | + array( |
4547 | + $realm_key => $realm, |
4548 | + 'mode' => $mode, |
4549 | + 'return_to' => $return_to)); |
4550 | + |
4551 | + if (!$this->_anonymous) { |
4552 | + if ($this->endpoint->isOPIdentifier()) { |
4553 | + // This will never happen when we're in compatibility |
4554 | + // mode, as long as isOPIdentifier() returns False |
4555 | + // whenever preferredNamespace() returns OPENID1_NS. |
4556 | + $claimed_id = $request_identity = |
4557 | + Auth_OpenID_IDENTIFIER_SELECT; |
4558 | + } else { |
4559 | + $request_identity = $this->endpoint->getLocalID(); |
4560 | + $claimed_id = $this->endpoint->claimed_id; |
4561 | + } |
4562 | + |
4563 | + // This is true for both OpenID 1 and 2 |
4564 | + $message->setArg(Auth_OpenID_OPENID_NS, 'identity', |
4565 | + $request_identity); |
4566 | + |
4567 | + if ($message->isOpenID2()) { |
4568 | + $message->setArg(Auth_OpenID_OPENID2_NS, 'claimed_id', |
4569 | + $claimed_id); |
4570 | + } |
4571 | + } |
4572 | + |
4573 | + if ($this->assoc) { |
4574 | + $message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle', |
4575 | + $this->assoc->handle); |
4576 | + } |
4577 | + |
4578 | + return $message; |
4579 | + } |
4580 | + |
4581 | + function redirectURL($realm, $return_to = null, |
4582 | + $immediate = false) |
4583 | + { |
4584 | + $message = $this->getMessage($realm, $return_to, $immediate); |
4585 | + |
4586 | + if (Auth_OpenID::isFailure($message)) { |
4587 | + return $message; |
4588 | + } |
4589 | + |
4590 | + return $message->toURL($this->endpoint->server_url); |
4591 | + } |
4592 | + |
4593 | + /** |
4594 | + * Get html for a form to submit this request to the IDP. |
4595 | + * |
4596 | + * form_tag_attrs: An array of attributes to be added to the form |
4597 | + * tag. 'accept-charset' and 'enctype' have defaults that can be |
4598 | + * overridden. If a value is supplied for 'action' or 'method', it |
4599 | + * will be replaced. |
4600 | + */ |
4601 | + function formMarkup($realm, $return_to=null, $immediate=false, |
4602 | + $form_tag_attrs=null) |
4603 | + { |
4604 | + $message = $this->getMessage($realm, $return_to, $immediate); |
4605 | + |
4606 | + if (Auth_OpenID::isFailure($message)) { |
4607 | + return $message; |
4608 | + } |
4609 | + |
4610 | + return $message->toFormMarkup($this->endpoint->server_url, |
4611 | + $form_tag_attrs); |
4612 | + } |
4613 | + |
4614 | + /** |
4615 | + * Get a complete html document that will autosubmit the request |
4616 | + * to the IDP. |
4617 | + * |
4618 | + * Wraps formMarkup. See the documentation for that function. |
4619 | + */ |
4620 | + function htmlMarkup($realm, $return_to=null, $immediate=false, |
4621 | + $form_tag_attrs=null) |
4622 | + { |
4623 | + $form = $this->formMarkup($realm, $return_to, $immediate, |
4624 | + $form_tag_attrs); |
4625 | + |
4626 | + if (Auth_OpenID::isFailure($form)) { |
4627 | + return $form; |
4628 | + } |
4629 | + return Auth_OpenID::autoSubmitHTML($form); |
4630 | + } |
4631 | + |
4632 | + function shouldSendRedirect() |
4633 | + { |
4634 | + return $this->endpoint->compatibilityMode(); |
4635 | + } |
4636 | +} |
4637 | + |
4638 | +/** |
4639 | + * The base class for responses from the Auth_OpenID_Consumer. |
4640 | + * |
4641 | + * @package OpenID |
4642 | + */ |
4643 | +class Auth_OpenID_ConsumerResponse { |
4644 | + var $status = null; |
4645 | + |
4646 | + function setEndpoint($endpoint) |
4647 | + { |
4648 | + $this->endpoint = $endpoint; |
4649 | + if ($endpoint === null) { |
4650 | + $this->identity_url = null; |
4651 | + } else { |
4652 | + $this->identity_url = $endpoint->claimed_id; |
4653 | + } |
4654 | + } |
4655 | + |
4656 | + /** |
4657 | + * Return the display identifier for this response. |
4658 | + * |
4659 | + * The display identifier is related to the Claimed Identifier, but the |
4660 | + * two are not always identical. The display identifier is something the |
4661 | + * user should recognize as what they entered, whereas the response's |
4662 | + * claimed identifier (in the identity_url attribute) may have extra |
4663 | + * information for better persistence. |
4664 | + * |
4665 | + * URLs will be stripped of their fragments for display. XRIs will |
4666 | + * display the human-readable identifier (i-name) instead of the |
4667 | + * persistent identifier (i-number). |
4668 | + * |
4669 | + * Use the display identifier in your user interface. Use |
4670 | + * identity_url for querying your database or authorization server. |
4671 | + * |
4672 | + */ |
4673 | + function getDisplayIdentifier() |
4674 | + { |
4675 | + if ($this->endpoint !== null) { |
4676 | + return $this->endpoint->getDisplayIdentifier(); |
4677 | + } |
4678 | + return null; |
4679 | + } |
4680 | +} |
4681 | + |
4682 | +/** |
4683 | + * A response with a status of Auth_OpenID_SUCCESS. Indicates that |
4684 | + * this request is a successful acknowledgement from the OpenID server |
4685 | + * that the supplied URL is, indeed controlled by the requesting |
4686 | + * agent. This has three relevant attributes: |
4687 | + * |
4688 | + * claimed_id - The identity URL that has been authenticated |
4689 | + * |
4690 | + * signed_args - The arguments in the server's response that were |
4691 | + * signed and verified. |
4692 | + * |
4693 | + * status - Auth_OpenID_SUCCESS. |
4694 | + * |
4695 | + * @package OpenID |
4696 | + */ |
4697 | +class Auth_OpenID_SuccessResponse extends Auth_OpenID_ConsumerResponse { |
4698 | + var $status = Auth_OpenID_SUCCESS; |
4699 | + |
4700 | + /** |
4701 | + * @access private |
4702 | + */ |
4703 | + function Auth_OpenID_SuccessResponse($endpoint, $message, $signed_args=null) |
4704 | + { |
4705 | + $this->endpoint = $endpoint; |
4706 | + $this->identity_url = $endpoint->claimed_id; |
4707 | + $this->signed_args = $signed_args; |
4708 | + $this->message = $message; |
4709 | + |
4710 | + if ($this->signed_args === null) { |
4711 | + $this->signed_args = array(); |
4712 | + } |
4713 | + } |
4714 | + |
4715 | + /** |
4716 | + * Extract signed extension data from the server's response. |
4717 | + * |
4718 | + * @param string $prefix The extension namespace from which to |
4719 | + * extract the extension data. |
4720 | + */ |
4721 | + function extensionResponse($namespace_uri, $require_signed) |
4722 | + { |
4723 | + if ($require_signed) { |
4724 | + return $this->getSignedNS($namespace_uri); |
4725 | + } else { |
4726 | + return $this->message->getArgs($namespace_uri); |
4727 | + } |
4728 | + } |
4729 | + |
4730 | + function isOpenID1() |
4731 | + { |
4732 | + return $this->message->isOpenID1(); |
4733 | + } |
4734 | + |
4735 | + function isSigned($ns_uri, $ns_key) |
4736 | + { |
4737 | + // Return whether a particular key is signed, regardless of |
4738 | + // its namespace alias |
4739 | + return in_array($this->message->getKey($ns_uri, $ns_key), |
4740 | + $this->signed_args); |
4741 | + } |
4742 | + |
4743 | + function getSigned($ns_uri, $ns_key, $default = null) |
4744 | + { |
4745 | + // Return the specified signed field if available, otherwise |
4746 | + // return default |
4747 | + if ($this->isSigned($ns_uri, $ns_key)) { |
4748 | + return $this->message->getArg($ns_uri, $ns_key, $default); |
4749 | + } else { |
4750 | + return $default; |
4751 | + } |
4752 | + } |
4753 | + |
4754 | + function getSignedNS($ns_uri) |
4755 | + { |
4756 | + $args = array(); |
4757 | + |
4758 | + $msg_args = $this->message->getArgs($ns_uri); |
4759 | + if (Auth_OpenID::isFailure($msg_args)) { |
4760 | + return null; |
4761 | + } |
4762 | + |
4763 | + foreach ($msg_args as $key => $value) { |
4764 | + if (!$this->isSigned($ns_uri, $key)) { |
4765 | + unset($msg_args[$key]); |
4766 | + } |
4767 | + } |
4768 | + |
4769 | + return $msg_args; |
4770 | + } |
4771 | + |
4772 | + /** |
4773 | + * Get the openid.return_to argument from this response. |
4774 | + * |
4775 | + * This is useful for verifying that this request was initiated by |
4776 | + * this consumer. |
4777 | + * |
4778 | + * @return string $return_to The return_to URL supplied to the |
4779 | + * server on the initial request, or null if the response did not |
4780 | + * contain an 'openid.return_to' argument. |
4781 | + */ |
4782 | + function getReturnTo() |
4783 | + { |
4784 | + return $this->getSigned(Auth_OpenID_OPENID_NS, 'return_to'); |
4785 | + } |
4786 | +} |
4787 | + |
4788 | +/** |
4789 | + * A response with a status of Auth_OpenID_FAILURE. Indicates that the |
4790 | + * OpenID protocol has failed. This could be locally or remotely |
4791 | + * triggered. This has three relevant attributes: |
4792 | + * |
4793 | + * claimed_id - The identity URL for which authentication was |
4794 | + * attempted, if it can be determined. Otherwise, null. |
4795 | + * |
4796 | + * message - A message indicating why the request failed, if one is |
4797 | + * supplied. Otherwise, null. |
4798 | + * |
4799 | + * status - Auth_OpenID_FAILURE. |
4800 | + * |
4801 | + * @package OpenID |
4802 | + */ |
4803 | +class Auth_OpenID_FailureResponse extends Auth_OpenID_ConsumerResponse { |
4804 | + var $status = Auth_OpenID_FAILURE; |
4805 | + |
4806 | + function Auth_OpenID_FailureResponse($endpoint, $message = null, |
4807 | + $contact = null, $reference = null) |
4808 | + { |
4809 | + $this->setEndpoint($endpoint); |
4810 | + $this->message = $message; |
4811 | + $this->contact = $contact; |
4812 | + $this->reference = $reference; |
4813 | + } |
4814 | +} |
4815 | + |
4816 | +/** |
4817 | + * A specific, internal failure used to detect type URI mismatch. |
4818 | + * |
4819 | + * @package OpenID |
4820 | + */ |
4821 | +class Auth_OpenID_TypeURIMismatch extends Auth_OpenID_FailureResponse { |
4822 | +} |
4823 | + |
4824 | +/** |
4825 | + * Exception that is raised when the server returns a 400 response |
4826 | + * code to a direct request. |
4827 | + * |
4828 | + * @package OpenID |
4829 | + */ |
4830 | +class Auth_OpenID_ServerErrorContainer { |
4831 | + function Auth_OpenID_ServerErrorContainer($error_text, |
4832 | + $error_code, |
4833 | + $message) |
4834 | + { |
4835 | + $this->error_text = $error_text; |
4836 | + $this->error_code = $error_code; |
4837 | + $this->message = $message; |
4838 | + } |
4839 | + |
4840 | + /** |
4841 | + * @access private |
4842 | + */ |
4843 | + static function fromMessage($message) |
4844 | + { |
4845 | + $error_text = $message->getArg( |
4846 | + Auth_OpenID_OPENID_NS, 'error', '<no error message supplied>'); |
4847 | + $error_code = $message->getArg(Auth_OpenID_OPENID_NS, 'error_code'); |
4848 | + return new Auth_OpenID_ServerErrorContainer($error_text, |
4849 | + $error_code, |
4850 | + $message); |
4851 | + } |
4852 | +} |
4853 | + |
4854 | +/** |
4855 | + * A response with a status of Auth_OpenID_CANCEL. Indicates that the |
4856 | + * user cancelled the OpenID authentication request. This has two |
4857 | + * relevant attributes: |
4858 | + * |
4859 | + * claimed_id - The identity URL for which authentication was |
4860 | + * attempted, if it can be determined. Otherwise, null. |
4861 | + * |
4862 | + * status - Auth_OpenID_SUCCESS. |
4863 | + * |
4864 | + * @package OpenID |
4865 | + */ |
4866 | +class Auth_OpenID_CancelResponse extends Auth_OpenID_ConsumerResponse { |
4867 | + var $status = Auth_OpenID_CANCEL; |
4868 | + |
4869 | + function Auth_OpenID_CancelResponse($endpoint) |
4870 | + { |
4871 | + $this->setEndpoint($endpoint); |
4872 | + } |
4873 | +} |
4874 | + |
4875 | +/** |
4876 | + * A response with a status of Auth_OpenID_SETUP_NEEDED. Indicates |
4877 | + * that the request was in immediate mode, and the server is unable to |
4878 | + * authenticate the user without further interaction. |
4879 | + * |
4880 | + * claimed_id - The identity URL for which authentication was |
4881 | + * attempted. |
4882 | + * |
4883 | + * setup_url - A URL that can be used to send the user to the server |
4884 | + * to set up for authentication. The user should be redirected in to |
4885 | + * the setup_url, either in the current window or in a new browser |
4886 | + * window. Null in OpenID 2. |
4887 | + * |
4888 | + * status - Auth_OpenID_SETUP_NEEDED. |
4889 | + * |
4890 | + * @package OpenID |
4891 | + */ |
4892 | +class Auth_OpenID_SetupNeededResponse extends Auth_OpenID_ConsumerResponse { |
4893 | + var $status = Auth_OpenID_SETUP_NEEDED; |
4894 | + |
4895 | + function Auth_OpenID_SetupNeededResponse($endpoint, |
4896 | + $setup_url = null) |
4897 | + { |
4898 | + $this->setEndpoint($endpoint); |
4899 | + $this->setup_url = $setup_url; |
4900 | + } |
4901 | +} |
4902 | + |
4903 | + |
4904 | |
4905 | === added file 'Auth/OpenID/CryptUtil.php' |
4906 | --- Auth/OpenID/CryptUtil.php 1970-01-01 00:00:00 +0000 |
4907 | +++ Auth/OpenID/CryptUtil.php 2011-06-19 04:48:34 +0000 |
4908 | @@ -0,0 +1,108 @@ |
4909 | +<?php |
4910 | + |
4911 | +/** |
4912 | + * CryptUtil: A suite of wrapper utility functions for the OpenID |
4913 | + * library. |
4914 | + * |
4915 | + * PHP versions 4 and 5 |
4916 | + * |
4917 | + * LICENSE: See the COPYING file included in this distribution. |
4918 | + * |
4919 | + * @access private |
4920 | + * @package OpenID |
4921 | + * @author JanRain, Inc. <openid@janrain.com> |
4922 | + * @copyright 2005-2008 Janrain, Inc. |
4923 | + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache |
4924 | + */ |
4925 | + |
4926 | +if (!defined('Auth_OpenID_RAND_SOURCE')) { |
4927 | + /** |
4928 | + * The filename for a source of random bytes. Define this yourself |
4929 | + * if you have a different source of randomness. |
4930 | + */ |
4931 | + define('Auth_OpenID_RAND_SOURCE', '/dev/urandom'); |
4932 | +} |
4933 | + |
4934 | +class Auth_OpenID_CryptUtil { |
4935 | + /** |
4936 | + * Get the specified number of random bytes. |
4937 | + * |
4938 | + * Attempts to use a cryptographically secure (not predictable) |
4939 | + * source of randomness if available. If there is no high-entropy |
4940 | + * randomness source available, it will fail. As a last resort, |
4941 | + * for non-critical systems, define |
4942 | + * <code>Auth_OpenID_RAND_SOURCE</code> as <code>null</code>, and |
4943 | + * the code will fall back on a pseudo-random number generator. |
4944 | + * |
4945 | + * @param int $num_bytes The length of the return value |
4946 | + * @return string $bytes random bytes |
4947 | + */ |
4948 | + static function getBytes($num_bytes) |
4949 | + { |
4950 | + static $f = null; |
4951 | + $bytes = ''; |
4952 | + if ($f === null) { |
4953 | + if (Auth_OpenID_RAND_SOURCE === null) { |
4954 | + $f = false; |
4955 | + } else { |
4956 | + $f = @fopen(Auth_OpenID_RAND_SOURCE, "r"); |
4957 | + if ($f === false) { |
4958 | + $msg = 'Define Auth_OpenID_RAND_SOURCE as null to ' . |
4959 | + ' continue with an insecure random number generator.'; |
4960 | + trigger_error($msg, E_USER_ERROR); |
4961 | + } |
4962 | + } |
4963 | + } |
4964 | + if ($f === false) { |
4965 | + // pseudorandom used |
4966 | + $bytes = ''; |
4967 | + for ($i = 0; $i < $num_bytes; $i += 4) { |
4968 | + $bytes .= pack('L', mt_rand()); |
4969 | + } |
4970 | + $bytes = substr($bytes, 0, $num_bytes); |
4971 | + } else { |
4972 | + $bytes = fread($f, $num_bytes); |
4973 | + } |
4974 | + return $bytes; |
4975 | + } |
4976 | + |
4977 | + /** |
4978 | + * Produce a string of length random bytes, chosen from chrs. If |
4979 | + * $chrs is null, the resulting string may contain any characters. |
4980 | + * |
4981 | + * @param integer $length The length of the resulting |
4982 | + * randomly-generated string |
4983 | + * @param string $chrs A string of characters from which to choose |
4984 | + * to build the new string |
4985 | + * @return string $result A string of randomly-chosen characters |
4986 | + * from $chrs |
4987 | + */ |
4988 | + static function randomString($length, $population = null) |
4989 | + { |
4990 | + if ($population === null) { |
4991 | + return Auth_OpenID_CryptUtil::getBytes($length); |
4992 | + } |
4993 | + |
4994 | + $popsize = strlen($population); |
4995 | + |
4996 | + if ($popsize > 256) { |
4997 | + $msg = 'More than 256 characters supplied to ' . __FUNCTION__; |
4998 | + trigger_error($msg, E_USER_ERROR); |
4999 | + } |
5000 | + |
The diff has been truncated for viewing.
Se aprueba