http-basic.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <?php
  2. /**
  3. * A simple Service definition for services that just use HTTP Basic for
  4. * authentication. You will need to extend this and supply a verify endpoint
  5. * which is where the user/pass will be tested against (for a 401 response).
  6. *
  7. * @package Keyring
  8. */
  9. class Keyring_Service_HTTP_Basic extends Keyring_Service {
  10. protected $username = null;
  11. protected $password = null;
  12. protected $verify_url = null;
  13. protected $verify_method = null;
  14. protected $token = null;
  15. function __construct() {
  16. parent::__construct();
  17. if ( ! KEYRING__HEADLESS_MODE )
  18. add_action( 'keyring_' . $this->get_name() . '_request_ui', array( $this, 'request_ui' ) );
  19. }
  20. function get_display( Keyring_Access_Token $token ) {
  21. $meta = $token->get_meta();
  22. return $meta['username'];
  23. }
  24. function request_ui() {
  25. // Common Header
  26. echo '<div class="wrap">';
  27. screen_icon( 'ms-admin' );
  28. echo '<h2>' . __( 'Account Details', 'keyring' ) . '</h2>';
  29. // Handle errors
  30. if ( isset( $_GET['error'] ) ) {
  31. echo '<div id="keyring-admin-errors" class="updated"><ul>';
  32. switch ( $_GET['error'] ) {
  33. case '401':
  34. echo '<li>' . __( 'Your account details could not be confirmed, please try again.', 'keyring' ) . '</li>';
  35. break;
  36. case 'empty':
  37. echo '<li>' . __( 'Please make sure you enter a username and password.', 'keyring' ) . '</li>';
  38. break;
  39. }
  40. echo '</ul></div>';
  41. }
  42. // Even though it doesn't make too much sense, we support request tokens in HTTP Basic
  43. // to ensure consistency with other services
  44. $request_token = new Keyring_Request_Token(
  45. $this->get_name(),
  46. array(),
  47. apply_filters(
  48. 'keyring_request_token_meta',
  49. array(
  50. 'for' => isset( $_REQUEST['for'] ) ? (string) $_REQUEST['for'] : false
  51. ),
  52. $this->get_name(),
  53. array() // no token
  54. )
  55. );
  56. $request_token = apply_filters( 'keyring_request_token', $request_token, $this );
  57. $request_token_id = $this->store_token( $request_token );
  58. Keyring_Util::debug( 'HTTP Basic Stored Request token ' . $request_token_id );
  59. echo apply_filters( 'keyring_' . $this->get_name() . '_request_ui_intro', '' );
  60. // Output basic form for collecting user/pass
  61. echo '<p>' . sprintf( __( 'Enter your username and password for accessing <strong>%s</strong>:', 'keyring' ), $this->get_label() ) . '</p>';
  62. echo '<form method="post" action="">';
  63. echo '<input type="hidden" name="service" value="' . esc_attr( $this->get_name() ) . '" />';
  64. echo '<input type="hidden" name="action" value="verify" />';
  65. echo '<input type="hidden" name="state" value="' . esc_attr( $request_token_id ) . '" />';
  66. wp_nonce_field( 'keyring-verify', 'kr_nonce', false );
  67. wp_nonce_field( 'keyring-verify-' . $this->get_name(), 'nonce', false );
  68. echo '<table class="form-table">';
  69. echo '<tr><th scope="row">' . __( 'Username', 'keyring' ) . '</th>';
  70. echo '<td><input type="text" name="username" value="" id="username" class="regular-text"></td></tr>';
  71. echo '<tr><th scope="row">' . __( 'Password', 'keyring' ) . '</th>';
  72. echo '<td><input type="password" name="password" value="" id="password" class="regular-text"></td></tr>';
  73. echo '</table>';
  74. echo '<p class="submitbox">';
  75. echo '<input type="submit" name="submit" value="' . __( 'Verify Details', 'keyring' ) . '" id="submit" class="button-primary">';
  76. echo '<a href="' . esc_url( $_SERVER['HTTP_REFERER'] ) . '" class="submitdelete" style="margin-left:2em;">' . __( 'Cancel', 'keyring' ) . '</a>';
  77. echo '</p>';
  78. echo '</form>';
  79. echo '</div>';
  80. ?><script type="text/javascript" charset="utf-8">
  81. jQuery( document ).ready( function() {
  82. jQuery( '#username' ).focus();
  83. } );
  84. </script><?php
  85. }
  86. function request_token() {
  87. return;
  88. }
  89. function verify_token() {
  90. if ( !isset( $_REQUEST['nonce'] ) || !wp_verify_nonce( $_REQUEST['nonce'], 'keyring-verify-' . $this->get_name() ) ) {
  91. Keyring::error( __( 'Invalid/missing verification nonce.', 'keyring' ) );
  92. exit;
  93. }
  94. // Load up the request token that got us here and globalize it
  95. if ( isset( $_REQUEST['state'] ) ) {
  96. global $keyring_request_token;
  97. $state = preg_replace( '/[^\x20-\x7E]/', '', $_GET['state'] );
  98. $keyring_request_token = $this->store->get_token( array( 'id' => $state, 'type' => 'request' ) );
  99. Keyring_Util::debug( 'HTTP Basic Loaded Request Token ' . $state );
  100. Keyring_Util::debug( $keyring_request_token );
  101. // Remove request token, don't need it any more.
  102. $this->store->delete( array( 'id' => $state, 'type' => 'request' ) );
  103. }
  104. if ( !strlen( $_POST['username'] ) ) {
  105. $url = Keyring_Util::admin_url(
  106. $this->get_name(),
  107. array(
  108. 'action' => 'request',
  109. 'error' => 'empty',
  110. 'kr_nonce' => wp_create_nonce( 'keyring-request' )
  111. )
  112. );
  113. Keyring_Util::debug( $url );
  114. wp_safe_redirect( $url );
  115. exit;
  116. }
  117. // HTTP Basic does not use Keyring_Request_Tokens, since there's only one step
  118. $token = new Keyring_Access_Token(
  119. $this->get_name(),
  120. base64_encode( $_POST['username'] . ':' . $_POST['password'] )
  121. );
  122. $this->set_token( $token );
  123. $res = $this->request( $this->verify_url, array( 'method' => $this->verify_method ) );
  124. // We will get a 401 if they entered an incorrect user/pass combo. ::request
  125. // will then return a Keyring_Error
  126. if ( Keyring_Util::is_error( $res ) ) {
  127. $url = Keyring_Util::admin_url(
  128. $this->get_name(),
  129. array(
  130. 'action' => 'request',
  131. 'error' => '401',
  132. 'kr_nonce' => wp_create_nonce( 'keyring-request' )
  133. )
  134. );
  135. Keyring_Util::debug( $url );
  136. wp_safe_redirect( $url );
  137. exit;
  138. }
  139. $meta = array_merge( array( 'username' => $_POST['username'] ), $this->build_token_meta( $token ) );
  140. $access_token = new Keyring_Access_Token(
  141. $this->get_name(),
  142. $token,
  143. $meta
  144. );
  145. $access_token = apply_filters( 'keyring_access_token', $access_token, array() );
  146. // If we didn't get a 401, then we'll assume it's OK
  147. $id = $this->store_token( $access_token );
  148. $this->verified( $id, $keyring_request_token );
  149. }
  150. function request( $url, array $params = array() ) {
  151. if ( $this->requires_token() && empty( $this->token ) )
  152. return new Keyring_Error( 'keyring-request-error', __( 'No token' ) );
  153. if ( $this->requires_token() )
  154. $params['headers'] = array( 'Authorization' => 'Basic ' . $this->token );
  155. $method = 'GET';
  156. if ( isset( $params['method'] ) ) {
  157. $method = strtoupper( $params['method'] );
  158. unset( $params['method'] );
  159. }
  160. $raw_response = false;
  161. if ( isset( $params['raw_response'] ) ) {
  162. $raw_response = (bool) $params['raw_response'];
  163. unset( $params['raw_response'] );
  164. }
  165. Keyring_Util::debug( "HTTP Basic $method $url" );
  166. Keyring_Util::debug( $params );
  167. switch ( strtoupper( $method ) ) {
  168. case 'GET':
  169. $res = wp_remote_get( $url, $params );
  170. break;
  171. case 'POST':
  172. $res = wp_remote_post( $url, $params );
  173. break;
  174. default:
  175. Keyring::error( __( 'Unsupported method specified for verify_token.', 'keyring' ) );
  176. exit;
  177. }
  178. Keyring_Util::debug( $res );
  179. $this->set_request_response_code( wp_remote_retrieve_response_code( $res ) );
  180. if ( 200 == wp_remote_retrieve_response_code( $res ) || 201 == wp_remote_retrieve_response_code( $res ) ) {
  181. if ( $raw_response )
  182. return wp_remote_retrieve_body( $res );
  183. else
  184. return $this->parse_response( wp_remote_retrieve_body( $res ) );
  185. } else {
  186. return new Keyring_Error( 'keyring-request-error', $res );
  187. }
  188. }
  189. }