<?php /** * A simple Service definition for services that just use HTTP Basic for * authentication. You will need to extend this and supply a verify endpoint * which is where the user/pass will be tested against (for a 401 response). * * @package Keyring */ class Keyring_Service_HTTP_Basic extends Keyring_Service { protected $username = null; protected $password = null; protected $verify_url = null; protected $verify_method = null; protected $token = null; function __construct() { parent::__construct(); if ( ! KEYRING__HEADLESS_MODE ) add_action( 'keyring_' . $this->get_name() . '_request_ui', array( $this, 'request_ui' ) ); } function get_display( Keyring_Access_Token $token ) { $meta = $token->get_meta(); return $meta['username']; } function request_ui() { // Common Header echo '<div class="wrap">'; screen_icon( 'ms-admin' ); echo '<h2>' . __( 'Account Details', 'keyring' ) . '</h2>'; // Handle errors if ( isset( $_GET['error'] ) ) { echo '<div id="keyring-admin-errors" class="updated"><ul>'; switch ( $_GET['error'] ) { case '401': echo '<li>' . __( 'Your account details could not be confirmed, please try again.', 'keyring' ) . '</li>'; break; case 'empty': echo '<li>' . __( 'Please make sure you enter a username and password.', 'keyring' ) . '</li>'; break; } echo '</ul></div>'; } // Even though it doesn't make too much sense, we support request tokens in HTTP Basic // to ensure consistency with other services $request_token = new Keyring_Request_Token( $this->get_name(), array(), apply_filters( 'keyring_request_token_meta', array( 'for' => isset( $_REQUEST['for'] ) ? (string) $_REQUEST['for'] : false ), $this->get_name(), array() // no token ) ); $request_token = apply_filters( 'keyring_request_token', $request_token, $this ); $request_token_id = $this->store_token( $request_token ); Keyring_Util::debug( 'HTTP Basic Stored Request token ' . $request_token_id ); echo apply_filters( 'keyring_' . $this->get_name() . '_request_ui_intro', '' ); // Output basic form for collecting user/pass echo '<p>' . sprintf( __( 'Enter your username and password for accessing <strong>%s</strong>:', 'keyring' ), $this->get_label() ) . '</p>'; echo '<form method="post" action="">'; echo '<input type="hidden" name="service" value="' . esc_attr( $this->get_name() ) . '" />'; echo '<input type="hidden" name="action" value="verify" />'; echo '<input type="hidden" name="state" value="' . esc_attr( $request_token_id ) . '" />'; wp_nonce_field( 'keyring-verify', 'kr_nonce', false ); wp_nonce_field( 'keyring-verify-' . $this->get_name(), 'nonce', false ); echo '<table class="form-table">'; echo '<tr><th scope="row">' . __( 'Username', 'keyring' ) . '</th>'; echo '<td><input type="text" name="username" value="" id="username" class="regular-text"></td></tr>'; echo '<tr><th scope="row">' . __( 'Password', 'keyring' ) . '</th>'; echo '<td><input type="password" name="password" value="" id="password" class="regular-text"></td></tr>'; echo '</table>'; echo '<p class="submitbox">'; echo '<input type="submit" name="submit" value="' . __( 'Verify Details', 'keyring' ) . '" id="submit" class="button-primary">'; echo '<a href="' . esc_url( $_SERVER['HTTP_REFERER'] ) . '" class="submitdelete" style="margin-left:2em;">' . __( 'Cancel', 'keyring' ) . '</a>'; echo '</p>'; echo '</form>'; echo '</div>'; ?><script type="text/javascript" charset="utf-8"> jQuery( document ).ready( function() { jQuery( '#username' ).focus(); } ); </script><?php } function request_token() { return; } function verify_token() { if ( !isset( $_REQUEST['nonce'] ) || !wp_verify_nonce( $_REQUEST['nonce'], 'keyring-verify-' . $this->get_name() ) ) { Keyring::error( __( 'Invalid/missing verification nonce.', 'keyring' ) ); exit; } // Load up the request token that got us here and globalize it if ( $_REQUEST['state'] ) { global $keyring_request_token; $state = (int) $_REQUEST['state']; $keyring_request_token = $this->store->get_token( array( 'id' => $state, 'type' => 'request' ) ); Keyring_Util::debug( 'HTTP Basic Loaded Request Token ' . $_REQUEST['state'] ); Keyring_Util::debug( $keyring_request_token ); // Remove request token, don't need it any more. $this->store->delete( array( 'id' => $state, 'type' => 'request' ) ); } if ( !strlen( $_POST['username'] ) ) { $url = Keyring_Util::admin_url( $this->get_name(), array( 'action' => 'request', 'error' => 'empty', 'kr_nonce' => wp_create_nonce( 'keyring-request' ) ) ); Keyring_Util::debug( $url ); wp_safe_redirect( $url ); exit; } // HTTP Basic does not use Keyring_Request_Tokens, since there's only one step $token = new Keyring_Access_Token( $this->get_name(), base64_encode( $_POST['username'] . ':' . $_POST['password'] ) ); $this->set_token( $token ); $res = $this->request( $this->verify_url, array( 'method' => $this->verify_method ) ); // We will get a 401 if they entered an incorrect user/pass combo. ::request // will then return a Keyring_Error if ( Keyring_Util::is_error( $res ) ) { $url = Keyring_Util::admin_url( $this->get_name(), array( 'action' => 'request', 'error' => '401', 'kr_nonce' => wp_create_nonce( 'keyring-request' ) ) ); Keyring_Util::debug( $url ); wp_safe_redirect( $url ); exit; } $meta = array_merge( array( 'username' => $_POST['username'] ), $this->build_token_meta( $token ) ); $access_token = new Keyring_Access_Token( $this->get_name(), $token, $meta ); $access_token = apply_filters( 'keyring_access_token', $access_token, array() ); // If we didn't get a 401, then we'll assume it's OK $id = $this->store_token( $access_token ); $this->verified( $id, $keyring_request_token ); } function request( $url, array $params = array() ) { if ( $this->requires_token() && empty( $this->token ) ) return new Keyring_Error( 'keyring-request-error', __( 'No token' ) ); if ( $this->requires_token() ) $params['headers'] = array( 'Authorization' => 'Basic ' . $this->token ); $method = 'GET'; if ( isset( $params['method'] ) ) { $method = strtoupper( $params['method'] ); unset( $params['method'] ); } $raw_response = false; if ( isset( $params['raw_response'] ) ) { $raw_response = (bool) $params['raw_response']; unset( $params['raw_response'] ); } Keyring_Util::debug( "HTTP Basic $method $url" ); Keyring_Util::debug( $params ); switch ( strtoupper( $method ) ) { case 'GET': $res = wp_remote_get( $url, $params ); break; case 'POST': $res = wp_remote_post( $url, $params ); break; default: Keyring::error( __( 'Unsupported method specified for verify_token.', 'keyring' ) ); exit; } Keyring_Util::debug( $res ); $this->set_request_response_code( wp_remote_retrieve_response_code( $res ) ); if ( 200 == wp_remote_retrieve_response_code( $res ) || 201 == wp_remote_retrieve_response_code( $res ) ) { if ( $raw_response ) return wp_remote_retrieve_body( $res ); else return $this->parse_response( wp_remote_retrieve_body( $res ) ); } else { return new Keyring_Error( 'keyring-request-error', $res ); } } }