| 1 | <?php |
|---|
| 2 | class OpenID extends Plugin |
|---|
| 3 | { |
|---|
| 4 | public function info() |
|---|
| 5 | { |
|---|
| 6 | return array( |
|---|
| 7 | 'name' => 'OpenID', |
|---|
| 8 | 'version' => '1.1.2', |
|---|
| 9 | 'url' => 'http://phpquebec.org/', |
|---|
| 10 | 'author' => 'PHP Quebec Community', |
|---|
| 11 | 'authorurl' => 'http://phpquebec.org/', |
|---|
| 12 | 'license' => 'Apache License 2.0', |
|---|
| 13 | 'description' => 'Adds OpenID 2.0 authentification support.', |
|---|
| 14 | 'copyright' => '2007' |
|---|
| 15 | ); |
|---|
| 16 | } |
|---|
| 17 | |
|---|
| 18 | public function filter_rewrite_rules( $db_rules ) |
|---|
| 19 | { |
|---|
| 20 | $db_rules[] = new RewriteRule( array( |
|---|
| 21 | 'name' => 'openid', |
|---|
| 22 | 'parse_regex' => '%^openid/?(?P<user>[^/]*)/?$%i', |
|---|
| 23 | 'build_str' => 'openid/({$user})', |
|---|
| 24 | 'handler' => 'OpenID', |
|---|
| 25 | 'action' => 'dispatch', |
|---|
| 26 | 'priority' => 1, |
|---|
| 27 | 'is_active' => 1, |
|---|
| 28 | 'rule_class' => RewriteRule::RULE_CUSTOM, |
|---|
| 29 | 'description' => 'OpenID Authentification' |
|---|
| 30 | ) ); |
|---|
| 31 | |
|---|
| 32 | return $db_rules; |
|---|
| 33 | } |
|---|
| 34 | |
|---|
| 35 | public function act( $action ) |
|---|
| 36 | { |
|---|
| 37 | if ( isset( $_GET['openid_mode'] ) ) { |
|---|
| 38 | switch ( $_GET['openid_mode'] ) { |
|---|
| 39 | case 'id_res': |
|---|
| 40 | self::openid_end(); |
|---|
| 41 | break; |
|---|
| 42 | case 'cancel': |
|---|
| 43 | EventLog::log( 'Authorization failed: User cancelled authorization.', 'info', 'authentication', 'OpenID' ); |
|---|
| 44 | throw new Exception( 'Authorization failed: User cancelled authorization.' ); |
|---|
| 45 | break; |
|---|
| 46 | } |
|---|
| 47 | } |
|---|
| 48 | else if ( isset( $_POST['openid_url'] ) ) { |
|---|
| 49 | self::openid_start(); |
|---|
| 50 | } |
|---|
| 51 | else { |
|---|
| 52 | EventLog::log( 'Authorization failed: unknown error.', 'err', 'authentication', 'OpenID' ); |
|---|
| 53 | throw new Exception( 'Authorization failed: unknown error.' ); |
|---|
| 54 | } |
|---|
| 55 | } |
|---|
| 56 | |
|---|
| 57 | public function action_plugin_activation( $file ) |
|---|
| 58 | { |
|---|
| 59 | if ( realpath( $file ) == __FILE__ ) { |
|---|
| 60 | if ( !extension_loaded('curl') && !@dl('curl') ) { |
|---|
| 61 | EventLog::log( 'Could not load CURL, which is needed for OpenID to work.', 'err', 'authentication', 'OpenID' ); |
|---|
| 62 | throw new Exception( 'Could not load CURL, which is needed for OpenID to work.' ); |
|---|
| 63 | } |
|---|
| 64 | EventLog::register_type( 'authentification', 'OpenID' ); |
|---|
| 65 | } |
|---|
| 66 | } |
|---|
| 67 | |
|---|
| 68 | public function action_plugin_deactivation( $file ) |
|---|
| 69 | { |
|---|
| 70 | if ( realpath( $file ) == __FILE__ ) { |
|---|
| 71 | EventLog::unregister_type( 'OpenID' ); |
|---|
| 72 | } |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | public function action_init() |
|---|
| 76 | { |
|---|
| 77 | if ( session_id() == '' ) { |
|---|
| 78 | session_start(); |
|---|
| 79 | } |
|---|
| 80 | ini_set( 'include_path', dirname( __FILE__ ) ); |
|---|
| 81 | Stack::add( 'template_stylesheet', array( $this->get_url() . '/openid.css', 'screen' ), 'openid_style' ); |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | public function action_theme_loginform_before() |
|---|
| 85 | { |
|---|
| 86 | if ( isset( $_GET['openid_url'] ) ) { |
|---|
| 87 | echo '<hr><div class="alert"><strong>If you have an existing account</strong>, sign in so we can assign your OpenID identifer to it.</div>'; |
|---|
| 88 | } |
|---|
| 89 | } |
|---|
| 90 | |
|---|
| 91 | public function action_theme_loginform_after() |
|---|
| 92 | { |
|---|
| 93 | |
|---|
| 94 | if ( ( Controller::get_action() != 'register' ) && !isset( $_GET['openid_url'] ) ) { |
|---|
| 95 | if ( Controller::get_action() == 'login' ) { |
|---|
| 96 | echo ' |
|---|
| 97 | <form method="post" action="'. URL::get( 'openid' ) .'" id="admin_openidform"> |
|---|
| 98 | <p> |
|---|
| 99 | <label for="openid_url" class="incontent abovecontent">' . _t('OpenID Identifier') . '</label><input type="text" name="openid_url" id="openid_url"' . ( isset($openid_url) ? 'value="'. $openid_url . '"' : '' ) . ' placeholder="' . _t('openid identifier') . '" class="styledformelement"> |
|---|
| 100 | </p> |
|---|
| 101 | <p> |
|---|
| 102 | <input id="openid_submit" class="submit" type="submit" value="Sign in using OpenID"> |
|---|
| 103 | </p> |
|---|
| 104 | </form> |
|---|
| 105 | '; |
|---|
| 106 | } |
|---|
| 107 | else { |
|---|
| 108 | echo ' |
|---|
| 109 | <form method="post" action="'. URL::get( 'openid' ) .'" id="openidform"> |
|---|
| 110 | <p> |
|---|
| 111 | <label for="openid_url">OpenID Identifier:</label> |
|---|
| 112 | <input type="text" size="25" name="openid_url" id="openid_url"> |
|---|
| 113 | </p> |
|---|
| 114 | <p> |
|---|
| 115 | <input type="submit" value="Sign in using OpenID"> |
|---|
| 116 | </p> |
|---|
| 117 | </form> |
|---|
| 118 | '; |
|---|
| 119 | } |
|---|
| 120 | } |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | public function action_theme_loginform_controls() |
|---|
| 124 | { |
|---|
| 125 | if ( isset( $_GET['openid_url'] ) ) { |
|---|
| 126 | echo '<input type="hidden" value="'.$_GET['openid_url'].'" name="habari_openid_url">'; |
|---|
| 127 | } |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | |
|---|
| 131 | |
|---|
| 132 | |
|---|
| 133 | |
|---|
| 134 | |
|---|
| 135 | |
|---|
| 136 | |
|---|
| 137 | |
|---|
| 138 | public function action_theme_admin_user( $user ) |
|---|
| 139 | { |
|---|
| 140 | $openid_url = isset( $user->info->openid_url ) ? $user->info->openid_url : ''; |
|---|
| 141 | echo ' |
|---|
| 142 | <div class="container settings user openid" id="openid"> |
|---|
| 143 | <h2>' . _t('OpenID') . '</h2> |
|---|
| 144 | <div class="item clear" id="openid_url"> |
|---|
| 145 | <span class="pct20"> |
|---|
| 146 | <label for="habari_openid_url">' . _t('OpenID Identifier') . '</label> |
|---|
| 147 | </span> |
|---|
| 148 | <span class="pct80"> |
|---|
| 149 | <input type="text" name="habari_openid_url" id="habari_openid_url" class="border" value="' . $openid_url . '" disabled> |
|---|
| 150 | </span> |
|---|
| 151 | </div> |
|---|
| 152 | </div>'; |
|---|
| 153 | } |
|---|
| 154 | |
|---|
| 155 | public function action_user_identify() |
|---|
| 156 | { |
|---|
| 157 | if ( ( Controller::get_action() == 'login' ) && !empty( $_POST['openid_url'] ) ) { |
|---|
| 158 | self::openid_start(); |
|---|
| 159 | } |
|---|
| 160 | } |
|---|
| 161 | |
|---|
| 162 | |
|---|
| 163 | public function action_user_authenticate_successful( $user ) |
|---|
| 164 | { |
|---|
| 165 | if ( !empty( $_POST['habari_openid_url'] ) ) { |
|---|
| 166 | $user->info->openid_url = $_POST['habari_openid_url']; |
|---|
| 167 | } |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | function action_admin_header( $theme ) |
|---|
| 171 | { |
|---|
| 172 | |
|---|
| 173 | if ( $theme->admin_page == 'login' ) { |
|---|
| 174 | Stack::add( 'admin_stylesheet', array( $this->get_url() . '/openid.css', 'screen' ), 'openid_style' ); |
|---|
| 175 | } |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | function getOpenIDURL() |
|---|
| 179 | { |
|---|
| 180 | if ( empty( $_POST['openid_url'] ) ) { |
|---|
| 181 | EventLog::log( 'Expected an OpenID URL.', 'err', 'authentication', 'OpenID' ); |
|---|
| 182 | throw new Exception( 'Expected an OpenID URL.' ); |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | return $_POST['openid_url']; |
|---|
| 186 | } |
|---|
| 187 | |
|---|
| 188 | function getReturnTo() |
|---|
| 189 | { |
|---|
| 190 | return URL::get('openid'); |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | function getTrustRoot() |
|---|
| 194 | { |
|---|
| 195 | return Site::get_url('habari'); |
|---|
| 196 | } |
|---|
| 197 | |
|---|
| 198 | function getStore() |
|---|
| 199 | { |
|---|
| 200 | $store_path = "/tmp/_php_consumer_test"; |
|---|
| 201 | |
|---|
| 202 | if ( !file_exists( $store_path ) && !mkdir( $store_path ) ) { |
|---|
| 203 | EventLog::log( 'Could not create the FileStore directory: ' . $store_path, 'err', 'authentication', 'OpenID' ); |
|---|
| 204 | throw new Exception( 'Could not create the FileStore directory: ' . $store_path . '. Please check the effective permissions.' ); |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | return new Auth_OpenID_FileStore( $store_path ); |
|---|
| 208 | } |
|---|
| 209 | |
|---|
| 210 | function getConsumer() |
|---|
| 211 | { |
|---|
| 212 | require_once "Auth/OpenID/Consumer.php"; |
|---|
| 213 | require_once "Auth/OpenID/FileStore.php"; |
|---|
| 214 | require_once "Auth/OpenID/SReg.php"; |
|---|
| 215 | $store = self::getStore(); |
|---|
| 216 | return new Auth_OpenID_Consumer( $store ); |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | function openid_start() |
|---|
| 220 | { |
|---|
| 221 | $openid = self::getOpenIDURL(); |
|---|
| 222 | $consumer = self::getConsumer(); |
|---|
| 223 | |
|---|
| 224 | $auth_request = $consumer->begin( $openid ); |
|---|
| 225 | |
|---|
| 226 | if ( !$auth_request ) { |
|---|
| 227 | EventLog::log( 'Authentication error: Not a valid OpenID.', 'err', 'authentication', 'OpenID' ); |
|---|
| 228 | throw new Exception( 'Authentication error: Not a valid OpenID.' ); |
|---|
| 229 | } |
|---|
| 230 | |
|---|
| 231 | $sreg_request = Auth_OpenID_SRegRequest::build( array( 'nickname' ), array( 'fullname', 'email' ) ); |
|---|
| 232 | |
|---|
| 233 | if ( $sreg_request ) { |
|---|
| 234 | $auth_request->addExtension( $sreg_request ); |
|---|
| 235 | } |
|---|
| 236 | |
|---|
| 237 | if ( $auth_request->shouldSendRedirect() ) { |
|---|
| 238 | $redirect_url = $auth_request->redirectURL( self::getTrustRoot(), self::getReturnTo() ); |
|---|
| 239 | |
|---|
| 240 | if ( Auth_OpenID::isFailure( $redirect_url ) ) { |
|---|
| 241 | EventLog::log( 'Could not redirect to server: ' . $redirect_url->message, 'err', 'authentication', 'OpenID' ); |
|---|
| 242 | throw new Exception( 'Could not redirect to server: ' . $redirect_url->message ); |
|---|
| 243 | } |
|---|
| 244 | else { |
|---|
| 245 | header( "Location: ".$redirect_url ); |
|---|
| 246 | } |
|---|
| 247 | } |
|---|
| 248 | else { |
|---|
| 249 | $form_id = 'openid_message'; |
|---|
| 250 | $form_html = $auth_request->formMarkup( self::getTrustRoot(), self::getReturnTo(), false, array( 'id' => $form_id ) ); |
|---|
| 251 | |
|---|
| 252 | if ( Auth_OpenID::isFailure( $form_html ) ) { |
|---|
| 253 | EventLog::log( 'Could not prepare redirection form: ' . $form_html->message, 'err', 'authentication', 'OpenID' ); |
|---|
| 254 | throw new Exception( 'Could not prepare redirection form: ' . $form_html->message ); |
|---|
| 255 | } |
|---|
| 256 | else { |
|---|
| 257 | echo ' |
|---|
| 258 | <html> |
|---|
| 259 | <head> |
|---|
| 260 | <title>OpenID transaction in progress</title> |
|---|
| 261 | </head> |
|---|
| 262 | <body onload="document.getElementById(\''.$form_id.'\').submit()"> |
|---|
| 263 | '.$form_html.' |
|---|
| 264 | </body> |
|---|
| 265 | </html> |
|---|
| 266 | '; |
|---|
| 267 | } |
|---|
| 268 | } |
|---|
| 269 | } |
|---|
| 270 | |
|---|
| 271 | function openid_end() |
|---|
| 272 | { |
|---|
| 273 | $consumer = self::getConsumer(); |
|---|
| 274 | $return_to = self::getReturnTo(); |
|---|
| 275 | $response = $consumer->complete( $return_to ); |
|---|
| 276 | |
|---|
| 277 | switch( $response->status ) { |
|---|
| 278 | case Auth_OpenID_CANCEL: |
|---|
| 279 | EventLog::log( 'Verification cancelled.', 'err', 'authentication', 'OpenID' ); |
|---|
| 280 | throw new Exception( 'Verification cancelled.' ); |
|---|
| 281 | break; |
|---|
| 282 | case Auth_OpenID_FAILURE: |
|---|
| 283 | EventLog::log( 'OpenID authentication failed: ' . $response->message, 'err', 'authentication', 'OpenID' ); |
|---|
| 284 | throw new Exception( 'OpenID authentication failed: ' . $response->message ); |
|---|
| 285 | break; |
|---|
| 286 | case Auth_OpenID_SUCCESS: |
|---|
| 287 | $openid = $response->getDisplayIdentifier(); |
|---|
| 288 | $esc_identity = htmlspecialchars( $openid, ENT_QUOTES ); |
|---|
| 289 | |
|---|
| 290 | $user = Users::get_by_info( 'openid_url', $openid ); |
|---|
| 291 | if ( count( $user ) != 0 ) { |
|---|
| 292 | if ( count( $user ) > 1 ) { |
|---|
| 293 | EventLog::log( 'Authentication error: More than one user has this OpenID.', 'err', 'authentication', 'OpenID' ); |
|---|
| 294 | throw new Exception( 'Authentication error: More than one user has this OpenID.' ); |
|---|
| 295 | } |
|---|
| 296 | $user[0]->remember(); |
|---|
| 297 | EventLog::log( 'Successful login for ' . $user[0]->username, 'info', 'authentication', 'OpenID' ); |
|---|
| 298 | |
|---|
| 299 | header( "HTTP/1.1 100 Continue" ); |
|---|
| 300 | header( "Location: " . Site::get_url( 'admin' ) ); |
|---|
| 301 | header( "Connection: close" ); |
|---|
| 302 | } |
|---|
| 303 | else { |
|---|
| 304 | Utils::redirect( URL::get( 'user', array( 'page'=>'login', 'openid_url' => $openid ), true ) ); |
|---|
| 305 | } |
|---|
| 306 | } |
|---|
| 307 | } |
|---|
| 308 | |
|---|
| 309 | } |
|---|
| 310 | ?> |
|---|