*/ require_once(DOKU_INC.'inc/HTTPClient.php'); class OpenIDConsumer { var $openid = ''; // the original, normalized OpenID identifier var $identifier = ''; // the identifier after following delegates var $provider = ''; // the Identify provider var $self = ''; // full URL to the consumer var $error = ''; // errors are stored here var $HTTP = null; // the HTTPClient library /** * Construtor * * @param string $self - The URL this Consumer runs on * @param string $identifier - The OpenID identifier to check */ function OpenIDConsumer($self,$identifier){ $this->self = $self; $this->identifier = trim($identifier); if(!$this->identifier){ $this->error = 'No Identifier given'; return null; } $this->identifier = $this->normalize_url($this->identifier); $this->openid = $this->identifier; $this->HTTP = new DokuHTTPClient(); } function addURLparams($url,$param){ // server may contain params already if(strpos($url,'?') === false){ $url .= '?'.$param; }else{ $url .= '&'.$param; } return $url; } /** * Delegate the ID check to an Indentity Provider with User Interaction * * Ends the script with a redirect to the provider */ function checkid_setup(){ // prepare request $data = array( 'openid.mode' => 'checkid_setup', 'openid.identity' => $this->identifier, 'openid.return_to' => $this->addURLparams($this->self,'openid='.rawurlencode($this->openid)), 'openid.trust_root' => DOKU_URL, 'openid.sreg.optional' => 'nickname,email,fullname', ); $param = buildURLparams($data,'&'); $url = $this->addURLparams($this->provider,$param); // redirect user to his Identity provider header('Location: '.$url); ob_end_clean(); exit; } /** * Checks the response to a checkid_setup request */ function check_authentication(){ $data = array(); foreach($_GET as $key => $val){ if(substr($key,0,12) == 'openid_sreg_'){ $data['openid.sreg.'.substr($key,12)] = $val; }elseif(substr($key,0,7) == 'openid_'){ $data['openid.'.substr($key,7)] = $val; } } $data['openid.mode'] = 'check_authentication'; // fetch via POST $resp = $this->HTTP->post($this->provider,$data); if(!$resp){ $this->error = 'HTTP error: '.$this->HTTP->error; return false; } if(strstr($resp,'is_valid:true')) return true; $this->error = 'Failed to verify the response signature: '.htmlspecialchars($this->HTTP->resp_body); return false; } /** * Find the provider for the identifier * * @param int delegations - count the number of followed delegations * @return boolean - true on success */ function findIdentity($delegations=0){ // avoid endless loops if($delegations > 5){ $this->error = 'Maximum of 5 delegations reached. Aborted'; return false; } // fetch the page configured at the identifier $page = $this->HTTP->get($this->identifier); if(!$page){ $this->error = 'HTTP error: '.$this->HTTP->error; return false; } // check for delegation if(preg_match('!<\s*link[^>]+rel=[\'"]openid\.delegate[\'"][^>]*>!is',$page,$match)){ if(preg_match('!href=[\'"]([^\'"]+)[\'"]!is',$match[0],$delmatch)){ // we found a delegation header -> use its oid $this->identifier = $delmatch[1]; $delegations++; }else{ $this->error = 'Failed to parse openid.delegate: '.htmlspecialchars($match); return false; } } // check for server if(preg_match('!<\s*link[^>]+rel=[\'"]openid\.server[\'"][^>]*>!is',$page,$match)){ if(preg_match('!href=[\'"]([^\'"]+)[\'"]!is',$match[0],$srvmatch)){ // success! $this->provider = $srvmatch[1]; return true; }else{ $this->error = 'Failed to parse openid.server: '.htmlspecialchars($match); return false; } }elseif($delegation){ // no server but we have a new id, follow it return $this->findIdentity($delegations); } $this->error = 'We found no Identity Provider for '.htmlspecialchars($this->identifier); return false; } /** * Normalize the given URL * * From openID spec: * The delegate identity URL must be canonical. It will not be further * processed by the consumer, so be sure it has the "http://" and trailing * slash, if there's no path component. * * @author Open Source Consulting, SA. and Dan Libby * @link http://videntity.org */ function normalize_url($url) { // Note: we use parse_url() here rather than checking for http(s) because // possibly the url contains eg ftp:// and it would be silly to prepend // http:// to such a url. $parts = parse_url( $url ); $scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : null; if( !$scheme ) { $url = 'http://' . $url; // If no scheme was found, then path will contain the whole url, which is not what we want, // so we parse it again. $parts = parse_url( $url ); } $path = isset( $parts['path'] ) ? $parts['path'] : null; if( !$path ) { $url .= '/'; } return $url; } }