Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@
* @package IndieAuth
*/

namespace IndieAuth;

use IndieAuth\Token\User as Token_User;

/**
* IndieAuth Authorize class.
*
* Handles token verification and authentication for IndieAuth.
*
* @since 1.0.0
*/
class IndieAuth_Authorize {
class Authorize {

/**
* Error object.
*
* @var WP_Error|WP_OAuth_Response|null
* @var \WP_Error|OAuth_Response|null
*/
public $error = null;

Expand Down Expand Up @@ -58,30 +62,30 @@ public function load() {

// WordPress validates the auth cookie at priority 10 and this cannot be overridden by an earlier priority.
// It validates the logged in cookie at 20 and can be overridden by something with a higher priority.
add_filter( 'determine_current_user', array( $this, 'determine_current_user' ), 15 );
add_filter( 'rest_authentication_errors', array( $this, 'rest_authentication_errors' ) );
\add_filter( 'determine_current_user', array( $this, 'determine_current_user' ), 15 );
\add_filter( 'rest_authentication_errors', array( $this, 'rest_authentication_errors' ) );

add_filter( 'indieauth_scopes', array( $this, 'get_indieauth_scopes' ), 9 );
add_filter( 'indieauth_response', array( $this, 'get_indieauth_response' ), 9 );
add_filter( 'wp_rest_server_class', array( $this, 'wp_rest_server_class' ) );
add_filter( 'rest_request_after_callbacks', array( $this, 'return_oauth_error' ), 10, 3 );
\add_filter( 'indieauth_scopes', array( $this, 'get_indieauth_scopes' ), 9 );
\add_filter( 'indieauth_response', array( $this, 'get_indieauth_response' ), 9 );
\add_filter( 'wp_rest_server_class', array( $this, 'wp_rest_server_class' ) );
\add_filter( 'rest_request_after_callbacks', array( $this, 'return_oauth_error' ), 10, 3 );
}


/**
* Ensures responses to any IndieAuth endpoints are always OAuth Responses rather than WP_Error.
*
* @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
* @param array $handler Route handler used for the request.
* @param WP_REST_Request $request Request used to generate the response.
* @return WP_REST_Response|WP_OAuth_Response|mixed Modified response.
* @param \WP_REST_Response|\WP_HTTP_Response|\WP_Error|mixed $response Result to send to the client.
* @param array $handler Route handler used for the request.
* @param \WP_REST_Request $request Request used to generate the response.
* @return \WP_REST_Response|OAuth_Response|mixed Modified response.
*/
public static function return_oauth_error( $response, $handler, $request ) {
if ( 0 !== strpos( $request->get_route(), '/indieauth/1.0/' ) ) {
return $response;
}

if ( is_wp_error( $response ) ) {
if ( \is_wp_error( $response ) ) {
return wp_error_to_oauth_response( $response );
}
return $response;
Expand All @@ -98,7 +102,7 @@ public static function return_oauth_error( $response, $handler, $request ) {
*/
public static function wp_rest_server_class( $class ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.classFound
global $current_user;
if ( defined( 'REST_REQUEST' ) && REST_REQUEST && $current_user instanceof WP_User && 0 === $current_user->ID ) {
if ( defined( 'REST_REQUEST' ) && REST_REQUEST && $current_user instanceof \WP_User && 0 === $current_user->ID ) {
/*
* For our authentication to work, we need to remove the cached lack
* of a current user, so the next time it checks, we can detect that
Expand Down Expand Up @@ -137,16 +141,16 @@ public function get_indieauth_response( $response ) {
* Attached to the rest_authentication_errors filter. Passes through existing
* errors registered on the filter.
*
* @param WP_Error|null|true $error Current error, null or true.
* @return WP_Error|null|true Error if one is set, unchanged otherwise.
* @param \WP_Error|null|true $error Current error, null or true.
* @return \WP_Error|null|true Error if one is set, unchanged otherwise.
*/
public function rest_authentication_errors( $error = null ) {
if ( is_user_logged_in() ) {
if ( \is_user_logged_in() ) {
// Another OAuth2 plugin successfully authenticated.
return $error;
}

if ( is_wp_error( $this->error ) ) {
if ( \is_wp_error( $this->error ) ) {
return $this->error;
}

Expand Down Expand Up @@ -193,9 +197,9 @@ public function determine_current_user( $user_id ) {
}
}

$this->error = new WP_OAuth_Response(
$this->error = new OAuth_Response(
'unauthorized',
__( 'User Not Found on this Site', 'indieauth' ),
\__( 'User Not Found on this Site', 'indieauth' ),
401,
array(
'response' => $params,
Expand All @@ -217,16 +221,16 @@ public function determine_current_user( $user_id ) {
public function get_authorization_header() {
$auth = null;
if ( ! empty( $_SERVER['HTTP_AUTHORIZATION'] ) ) {
$auth = wp_unslash( $_SERVER['HTTP_AUTHORIZATION'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$auth = \wp_unslash( $_SERVER['HTTP_AUTHORIZATION'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
} elseif ( ! empty( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ) {
// When Apache speaks via FastCGI with PHP, then the authorization header is often available as REDIRECT_HTTP_AUTHORIZATION.
$auth = wp_unslash( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$auth = \wp_unslash( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
} else {
$headers = getallheaders();
// Check for the authorization header case-insensitively.
foreach ( $headers as $key => $value ) {
if ( strtolower( $key ) === 'authorization' ) {
$auth = wp_unslash( $value );
$auth = \wp_unslash( $value );
break;
}
}
Expand Down Expand Up @@ -278,7 +282,7 @@ public function get_token_from_request() {
if ( empty( $_POST['access_token'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
return null;
}
$token = sanitize_text_field( wp_unslash( $_POST['access_token'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
$token = \sanitize_text_field( \wp_unslash( $_POST['access_token'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing

if ( is_string( $token ) ) {
return $token;
Expand All @@ -290,23 +294,23 @@ public function get_token_from_request() {
* Verifies Access Token.
*
* @param string $token The token to verify.
* @return array|WP_OAuth_Response Return either the token information or an OAuth Error Object.
* @return array|OAuth_Response Return either the token information or an OAuth Error Object.
*/
public function verify_access_token( $token ) {
$tokens = new Token_User( '_indieauth_token_' );
$return = $tokens->get( $token );
if ( empty( $return ) ) {
return new WP_OAuth_Response(
return new OAuth_Response(
'invalid_token',
__( 'Invalid access token', 'indieauth' ),
\__( 'Invalid access token', 'indieauth' ),
401
);
}
if ( is_oauth_error( $return ) ) {
return $return;
}
$return['last_accessed'] = time();
$return['last_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
$return['last_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? \sanitize_text_field( \wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
$tokens->update( $token, $return );
if ( array_key_exists( 'exp', $return ) ) {
$return['expires_in'] = $return['exp'] - time();
Expand All @@ -318,15 +322,15 @@ public function verify_access_token( $token ) {
* Verifies authorization code.
*
* @param string $code Authorization Code.
* @return array|WP_OAuth_Response Return either the code information or an OAuth Error object.
* @return array|OAuth_Response Return either the code information or an OAuth Error object.
*/
public static function verify_authorization_code( $code ) {
$tokens = new Token_User( '_indieauth_code_' );
$return = $tokens->get( $code );
if ( empty( $return ) ) {
return new WP_OAuth_Response(
return new OAuth_Response(
'invalid_code',
__( 'Invalid authorization code', 'indieauth' ),
\__( 'Invalid authorization code', 'indieauth' ),
401
);
}
Expand Down
106 changes: 106 additions & 0 deletions includes/class-autoloader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php
/**
* Autoloader for IndieAuth.
*
* @package IndieAuth
*/

namespace IndieAuth;

/**
* An Autoloader that respects WordPress's filename standards.
*/
class Autoloader {

/**
* Namespace separator.
*/
const NS_SEPARATOR = '\\';

/**
* The prefix to compare classes against.
*
* @var string
* @access protected
*/
protected $prefix;

/**
* Length of the prefix string.
*
* @var int
* @access protected
*/
protected $prefix_length;

/**
* Path to the file to be loaded.
*
* @var string
* @access protected
*/
protected $path;

/**
* Constructor.
*
* @param string $prefix Namespace prefix all classes have in common.
* @param string $path Path to the files to be loaded.
*/
public function __construct( $prefix, $path ) {
$this->prefix = $prefix;
$this->prefix_length = \strlen( $prefix );
$this->path = \rtrim( $path . '/' );
}

/**
* Registers Autoloader's autoload function.
*
* @throws \Exception When autoload_function cannot be registered.
*
* @param string $prefix Namespace prefix all classes have in common.
* @param string $path Path to the files to be loaded.
*/
public static function register_path( $prefix, $path ) {
$loader = new self( $prefix, $path );
\spl_autoload_register( array( $loader, 'load' ) );
}

/**
* Loads a class if its namespace starts with `$this->prefix`.
*
* @param string $class_name The class to be loaded.
*/
public function load( $class_name ) {
if ( \strpos( $class_name, $this->prefix . self::NS_SEPARATOR ) !== 0 ) {
return;
}

// Strip prefix from the start (ala PSR-4).
$class_name = \substr( $class_name, $this->prefix_length + 1 );
$class_name = \strtolower( $class_name );
$dir = '';

$last_ns_pos = \strripos( $class_name, self::NS_SEPARATOR );
if ( false !== $last_ns_pos ) {
$namespace = \substr( $class_name, 0, $last_ns_pos );
$namespace = \str_replace( '_', '-', $namespace );
$class_name = \substr( $class_name, $last_ns_pos + 1 );
$dir = \str_replace( self::NS_SEPARATOR, DIRECTORY_SEPARATOR, $namespace ) . DIRECTORY_SEPARATOR;
}

$path = $this->path . $dir . 'class-' . \str_replace( '_', '-', $class_name ) . '.php';

if ( ! \file_exists( $path ) ) {
$path = $this->path . $dir . 'interface-' . \str_replace( '_', '-', $class_name ) . '.php';
}

if ( ! \file_exists( $path ) ) {
$path = $this->path . $dir . 'trait-' . \str_replace( '_', '-', $class_name ) . '.php';
}

if ( \file_exists( $path ) ) {
require_once $path;
}
}
}
Loading
Loading