1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
|
<?php
/*
* Disable strict error reporting, since the OpenID library
* used is PHP4-compatible, and not PHP5 strict-standards compatible.
*/
SimpleSAML_Utilities::maskErrors(E_STRICT);
if (defined('E_DEPRECATED')) {
/* PHP 5.3 also has E_DEPRECATED. */
SimpleSAML_Utilities::maskErrors(constant('E_DEPRECATED'));
}
/* Add the OpenID library search path. */
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))) . '/lib');
require_once('Auth/OpenID/AX.php');
require_once('Auth/OpenID/SReg.php');
require_once('Auth/OpenID/Server.php');
require_once('Auth/OpenID/ServerRequest.php');
/**
* Authentication module which acts as an OpenID Consumer
*
* @author Andreas Åkre Solberg, <andreas.solberg@uninett.no>, UNINETT AS.
* @package simpleSAMLphp
*/
class sspmod_openid_Auth_Source_OpenIDConsumer extends SimpleSAML_Auth_Source {
/**
* Static openid target to use.
*
* @var string|NULL
*/
private $target;
/**
* Custom realm to use.
*
* @var string|NULL
*/
private $realm;
/**
* List of optional attributes.
*/
private $optionalAttributes;
private $optionalAXAttributes;
/**
* List of required attributes.
*/
private $requiredAttributes;
private $requiredAXAttributes;
/**
* Validate SReg responses.
*/
private $validateSReg;
/**
* List of custom extension args
*/
private $extensionArgs;
/**
* Prefer HTTP Redirect over HTML Form Redirection (POST)
*/
private $preferHttpRedirect;
/**
* Constructor for this authentication source.
*
* @param array $info Information about this authentication source.
* @param array $config Configuration.
*/
public function __construct($info, $config) {
/* Call the parent constructor first, as required by the interface. */
parent::__construct($info, $config);
$cfgParse = SimpleSAML_Configuration::loadFromArray($config,
'Authentication source ' . var_export($this->authId, TRUE));
$this->target = $cfgParse->getString('target', NULL);
$this->realm = $cfgParse->getString('realm', NULL);
$this->optionalAttributes = $cfgParse->getArray('attributes.optional', array());
$this->requiredAttributes = $cfgParse->getArray('attributes.required', array());
$this->optionalAXAttributes = $cfgParse->getArray('attributes.ax_optional', array());
$this->requiredAXAttributes = $cfgParse->getArray('attributes.ax_required', array());
$this->validateSReg = $cfgParse->getBoolean('sreg.validate',TRUE);
$this->extensionArgs = $cfgParse->getArray('extension.args', array());
$this->preferHttpRedirect = $cfgParse->getBoolean('prefer_http_redirect', FALSE);
}
/**
* Initiate authentication. Redirecting the user to the consumer endpoint
* with a state Auth ID.
*
* @param array &$state Information about the current authentication.
*/
public function authenticate(&$state) {
assert('is_array($state)');
$state['openid:AuthId'] = $this->authId;
if ($this->target !== NULL) {
/* We know our OpenID target URL. Skip the page where we ask for it. */
$this->doAuth($state, $this->target);
/* doAuth() never returns. */
assert('FALSE');
}
$id = SimpleSAML_Auth_State::saveState($state, 'openid:init');
$url = SimpleSAML_Module::getModuleURL('openid/consumer.php');
SimpleSAML_Utilities::redirectTrustedURL($url, array('AuthState' => $id));
}
/**
* Retrieve the Auth_OpenID_Consumer instance.
*
* @param array &$state The state array we are currently working with.
* @return Auth_OpenID_Consumer The Auth_OpenID_Consumer instance.
*/
private function getConsumer(array &$state) {
$store = new sspmod_openid_StateStore($state);
$session = new sspmod_openid_SessionStore();
return new Auth_OpenID_Consumer($store, $session);
}
/**
* Retrieve the URL we should return to after successful authentication.
*
* @return string The URL we should return to after successful authentication.
*/
private function getReturnTo($stateId) {
assert('is_string($stateId)');
return SimpleSAML_Module::getModuleURL('openid/linkback.php', array(
'AuthState' => $stateId,
));
}
/**
* Retrieve the trust root for this openid site.
*
* @return string The trust root.
*/
private function getTrustRoot() {
if (!empty($this->realm)) {
return $this->realm;
} else {
return SimpleSAML_Utilities::selfURLhost();
}
}
/**
* Send an authentication request to the OpenID provider.
*
* @param array &$state The state array.
* @param string $openid The OpenID we should try to authenticate with.
*/
public function doAuth(array &$state, $openid) {
assert('is_string($openid)');
$stateId = SimpleSAML_Auth_State::saveState($state, 'openid:auth');
$consumer = $this->getConsumer($state);
// Begin the OpenID authentication process.
$auth_request = $consumer->begin($openid);
// No auth request means we can't begin OpenID.
if (!$auth_request) {
throw new SimpleSAML_Error_BadRequest('Not a valid OpenID: ' . var_export($openid, TRUE));
}
$sreg_request = Auth_OpenID_SRegRequest::build(
$this->requiredAttributes,
$this->optionalAttributes
);
if ($sreg_request) {
$auth_request->addExtension($sreg_request);
}
// Create attribute request object
$ax_attribute = array();
foreach($this->requiredAXAttributes as $attr) {
$ax_attribute[] = Auth_OpenID_AX_AttrInfo::make($attr,1,true);
}
foreach($this->optionalAXAttributes as $attr) {
$ax_attribute[] = Auth_OpenID_AX_AttrInfo::make($attr,1,false);
}
if (count($ax_attribute) > 0) {
// Create AX fetch request
$ax_request = new Auth_OpenID_AX_FetchRequest;
// Add attributes to AX fetch request
foreach($ax_attribute as $attr){
$ax_request->add($attr);
}
// Add AX fetch request to authentication request
$auth_request->addExtension($ax_request);
}
foreach($this->extensionArgs as $ext_ns => $ext_arg) {
if (is_array($ext_arg)) {
foreach($ext_arg as $ext_key => $ext_value) {
$auth_request->addExtensionArg($ext_ns, $ext_key, $ext_value);
}
}
}
// Redirect the user to the OpenID server for authentication.
// Store the token for this authentication so we can verify the
// response.
// For OpenID 1, send a redirect. For OpenID 2, use a Javascript form
// to send a POST request to the server or use redirect if
// prefer_http_redirect is enabled and redirect URL size
// is less than 2049
$should_send_redirect = $auth_request->shouldSendRedirect();
if ($this->preferHttpRedirect || $should_send_redirect) {
$redirect_url = $auth_request->redirectURL($this->getTrustRoot(), $this->getReturnTo($stateId));
// If the redirect URL can't be built, display an error message.
if (Auth_OpenID::isFailure($redirect_url)) {
throw new SimpleSAML_Error_AuthSource($this->authId, 'Could not redirect to server: ' . var_export($redirect_url->message, TRUE));
}
// For OpenID 2 failover to POST if redirect URL is longer than 2048
if ($should_send_redirect || strlen($redirect_url) <= 2048) {
SimpleSAML_Utilities::redirectTrustedURL($redirect_url);
assert('FALSE');
}
}
// Generate form markup and render it.
$form_id = 'openid_message';
$form_html = $auth_request->formMarkup($this->getTrustRoot(), $this->getReturnTo($stateId), FALSE, array('id' => $form_id));
// Display an error if the form markup couldn't be generated; otherwise, render the HTML.
if (Auth_OpenID::isFailure($form_html)) {
throw new SimpleSAML_Error_AuthSource($this->authId, 'Could not redirect to server: ' . var_export($form_html->message, TRUE));
} else {
echo '<html><head><title>OpenID transaction in progress</title></head>
<body onload=\'document.getElementById("' . $form_id . '").submit()\'>' .
$form_html . '</body></html>';
exit;
}
}
/**
* Process an authentication response.
*
* @param array &$state The state array.
*/
public function postAuth(array &$state) {
$consumer = $this->getConsumer($state);
$return_to = SimpleSAML_Utilities::selfURL();
// Complete the authentication process using the server's
// response.
$response = $consumer->complete($return_to);
// Check the response status.
if ($response->status == Auth_OpenID_CANCEL) {
// This means the authentication was cancelled.
throw new SimpleSAML_Error_UserAborted();
} else if ($response->status == Auth_OpenID_FAILURE) {
// Authentication failed; display the error message.
throw new SimpleSAML_Error_AuthSource($this->authId, 'Authentication failed: ' . var_export($response->message, TRUE));
} else if ($response->status != Auth_OpenID_SUCCESS) {
throw new SimpleSAML_Error_AuthSource($this->authId, 'General error. Try again.');
}
// This means the authentication succeeded; extract the
// identity URL and Simple Registration data (if it was
// returned).
$openid = $response->identity_url;
$attributes = array('openid' => array($openid));
$attributes['openid.server_url'] = array($response->endpoint->server_url);
if ($response->endpoint->canonicalID) {
$attributes['openid.canonicalID'] = array($response->endpoint->canonicalID);
}
if ($response->endpoint->local_id) {
$attributes['openid.local_id'] = array($response->endpoint->local_id);
}
$sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response, $this->validateSReg);
$sregresponse = $sreg_resp->contents();
if (is_array($sregresponse) && count($sregresponse) > 0) {
$attributes['openid.sregkeys'] = array_keys($sregresponse);
foreach ($sregresponse AS $sregkey => $sregvalue) {
$attributes['openid.sreg.' . $sregkey] = array($sregvalue);
}
}
// Get AX response information
$ax = new Auth_OpenID_AX_FetchResponse();
$ax_resp = $ax->fromSuccessResponse($response);
if (($ax_resp instanceof Auth_OpenID_AX_FetchResponse) && (!empty($ax_resp->data))) {
$axresponse = $ax_resp->data;
$attributes['openid.axkeys'] = array_keys($axresponse);
foreach ($axresponse AS $axkey => $axvalue) {
if (preg_match("/^\w+:/",$axkey)) {
$attributes[$axkey] = (is_array($axvalue)) ? $axvalue : array($axvalue);
} else {
SimpleSAML_Logger::warning('Invalid attribute name in AX response: ' . var_export($axkey, TRUE));
}
}
}
SimpleSAML_Logger::debug('OpenID Returned Attributes: '. implode(", ",array_keys($attributes)));
$state['Attributes'] = $attributes;
SimpleSAML_Auth_Source::completeAuth($state);
}
}
|