-
Notifications
You must be signed in to change notification settings - Fork 368
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
684 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
<?php | ||
/** | ||
* File containing the class Guest_Session. | ||
* | ||
* @package wp-job-manager | ||
*/ | ||
|
||
namespace WP_Job_Manager; | ||
|
||
/** | ||
* Manage the guest user session. | ||
* | ||
* @since $$next-version$$ | ||
*/ | ||
class Guest_Session { | ||
|
||
use Singleton; | ||
|
||
const COOKIE_NAME = 'jm-guest-user'; | ||
const COOKIE_EXPIRY = 1 * DAY_IN_SECONDS; | ||
const QUERY_VAR = 'jmtoken'; | ||
|
||
/** | ||
* The current guest user. | ||
* | ||
* @var Guest_User|false | ||
*/ | ||
private $user = false; | ||
|
||
/** | ||
* Whether the session has been initialized. | ||
* | ||
* @var bool | ||
*/ | ||
private bool $initialized = false; | ||
|
||
/** | ||
* Get the guest user for the current session. | ||
* | ||
* @return Guest_User|false | ||
*/ | ||
public static function get_current_guest() { | ||
$session = self::instance(); | ||
$session->init(); | ||
|
||
return $session->user; | ||
} | ||
|
||
/** | ||
* Initialize the guest session based on URL token or session cookie. | ||
* If only a URL token is present, it will be set as a session cookie. | ||
* | ||
* @return void | ||
*/ | ||
private function init() { | ||
|
||
if ( $this->initialized ) { | ||
return; | ||
} | ||
|
||
$this->initialized = true; | ||
|
||
$token = $this->get_url_token(); | ||
$cookie = $this->get_cookie(); | ||
$token = $token ?? $cookie; | ||
|
||
if ( ! $token ) { | ||
return; | ||
} | ||
|
||
$guest = Guest_User::get_by_token( $token ); | ||
|
||
$this->user = $guest; | ||
|
||
if ( $guest && $cookie !== $token ) { | ||
$this->set_cookie( $token ); | ||
} | ||
} | ||
|
||
/** | ||
* Get the token from a session cookie. | ||
* | ||
* @return string|null | ||
*/ | ||
private function get_cookie() { | ||
return isset( $_COOKIE[ self::COOKIE_NAME ] ) ? sanitize_text_field( wp_unslash( $_COOKIE[ self::COOKIE_NAME ] ) ) : null; | ||
} | ||
|
||
/** | ||
* Set the token as a session cookie. | ||
* | ||
* @param string $token | ||
*/ | ||
private function set_cookie( $token ) { | ||
$secure = 'https' === wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME ); | ||
$expire = time() + self::COOKIE_EXPIRY; | ||
setcookie( self::COOKIE_NAME, $token, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure, true ); | ||
} | ||
|
||
/** | ||
* Get the token from the URL query string. | ||
* | ||
* @return string|null | ||
*/ | ||
private static function get_url_token() { | ||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- URL token-based login action. | ||
return isset( $_GET[ self::QUERY_VAR ] ) ? sanitize_text_field( wp_unslash( $_GET[ self::QUERY_VAR ] ) ) : null; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
<?php | ||
/** | ||
* File containing the class Guest_User. | ||
* | ||
* @package wp-job-manager | ||
*/ | ||
|
||
namespace WP_Job_Manager; | ||
|
||
if ( ! defined( 'ABSPATH' ) ) { | ||
exit; // Exit if accessed directly. | ||
} | ||
|
||
/** | ||
* A guest user is a site visitors without a full account, identified by their email address. | ||
* | ||
* @since $$next-version$$ | ||
*/ | ||
class Guest_User { | ||
const TOKEN_EXPIRY = 35 * DAY_IN_SECONDS; | ||
const TOKEN_PREFIX = 'jm'; | ||
const TOKEN_SEPARATOR = '-'; | ||
|
||
/** | ||
* The post object for the guest user. | ||
* | ||
* @var \WP_Post | ||
*/ | ||
private $post; | ||
|
||
/** | ||
* The post ID. | ||
* | ||
* @readonly | ||
* @var int | ||
*/ | ||
public int $ID; | ||
|
||
/** | ||
* The user email address. | ||
* | ||
* @readonly | ||
* @var string | ||
*/ | ||
public string $user_email; | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param \WP_Post $guest The guest user post object. | ||
*/ | ||
public function __construct( \WP_Post $guest ) { | ||
$this->post = $guest; | ||
$this->ID = $guest->ID; | ||
$this->user_email = $guest->post_title; | ||
} | ||
|
||
/** | ||
* The post object for the guest user. | ||
* | ||
* @param int|string $guest_id Post ID of guest user, or their email. | ||
* | ||
* @return self|false | ||
*/ | ||
public static function load( $guest_id ) { | ||
|
||
if ( is_numeric( $guest_id ) ) { | ||
$guest = get_post( $guest_id ); | ||
} elseif ( is_email( $guest_id ) ) { | ||
$email = sanitize_email( $guest_id ); | ||
$guest = get_posts( | ||
[ | ||
'post_type' => \WP_Job_Manager_Post_Types::PT_GUEST_USER, | ||
'title' => $email, | ||
] | ||
); | ||
$guest = empty( $guest ) ? false : $guest[0]; | ||
} | ||
|
||
if ( empty( $guest ) ) { | ||
return false; | ||
} | ||
|
||
return new self( $guest ); | ||
} | ||
|
||
/** | ||
* Create a new guest user for the given email. Returns existing guest user if one exists for the email. | ||
* | ||
* @param string $email | ||
* | ||
* @return self|false | ||
*/ | ||
public static function create( string $email ) { | ||
|
||
$email = sanitize_email( $email ); | ||
|
||
if ( ! is_email( $email ) ) { | ||
return false; | ||
} | ||
|
||
$existing = self::load( $email ); | ||
|
||
if ( $existing ) { | ||
return $existing; | ||
} | ||
|
||
$guest_id = wp_insert_post( | ||
[ | ||
'post_title' => $email, | ||
'post_type' => \WP_Job_Manager_Post_Types::PT_GUEST_USER, | ||
'post_status' => 'publish', | ||
] | ||
); | ||
|
||
return self::load( $guest_id ); | ||
|
||
} | ||
|
||
/** | ||
* Get a URL token for the guest user. Includes the guest user ID and an access token. | ||
* | ||
* @return string | ||
*/ | ||
public function create_token() { | ||
|
||
$at = new Access_Token( [ 'guest_id' => $this->ID ] ); | ||
$access_token = $at->create( time() + self::TOKEN_EXPIRY ); | ||
|
||
return implode( self::TOKEN_SEPARATOR, [ self::TOKEN_PREFIX, $this->ID, $access_token ] ); | ||
|
||
} | ||
|
||
/** | ||
* Verify a URL token and return the guest user if valid. | ||
* | ||
* @param string $token URL token. | ||
* | ||
* @return Guest_User|false | ||
*/ | ||
public static function get_by_token( $token ) { | ||
|
||
if ( ! is_string( $token ) ) { | ||
return false; | ||
} | ||
|
||
$token_parts = explode( self::TOKEN_SEPARATOR, $token ); | ||
if ( count( $token_parts ) !== 3 ) { | ||
return false; | ||
} | ||
|
||
[ $prefix, $guest_id, $access_token ] = $token_parts; | ||
|
||
if ( self::TOKEN_PREFIX !== $prefix ) { | ||
return false; | ||
} | ||
|
||
$at = new Access_Token( [ 'guest_id' => (int) $guest_id ] ); | ||
|
||
if ( ! $at->verify( $access_token ) ) { | ||
return false; | ||
} | ||
|
||
return self::load( $guest_id ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.