File: //proc/self/cwd/wp-content/plugins/password-protect-page/includes/services/class-ppw-passwords.php
<?php
/**
* Created by PhpStorm.
* User: gaupoit
* Date: 5/6/19
* Time: 21:04
*/
if ( ! class_exists( 'PPW_Password_Services' ) ) {
class PPW_Password_Services implements PPW_Service_Interfaces {
/**
* @var PPW_Repository_Passwords
*/
private $passwords_repository;
/**
* PPW_Password_Services constructor.
*
* @param PPW_Repository_Passwords $repo The password repository class help to interact with DB.
*/
public function __construct( $repo = null ) {
if ( is_null( $repo ) ) {
$this->passwords_repository = new PPW_Repository_Passwords();
} else {
$this->passwords_repository = $repo;
}
}
/**
* Check content is protected
*
* @param $post_id
*
* @return array|bool
*/
public function is_protected_content( $post_id ) {
$result = $this->get_passwords( $post_id );
if ( ! $result['has_global_passwords'] && ! $result['has_role_passwords'] ) {
return false;
}
return $result;
}
/**
* Check password is valid
*
* @param $password
* @param $post_id
* @param $current_roles
*
* @return bool
*/
public function is_valid_password( $password, $post_id, $current_roles ) {
if ( $this->check_password_type_is_global( $post_id, $password ) ) {
$this->set_cookie_bypass_cache( $password . $post_id, PPW_Constants::COOKIE_NAME . $post_id );
return true;
}
if ( ! is_user_logged_in() ) {
return false;
}
$role_meta = get_post_meta( $post_id, PPW_Constants::POST_PROTECTION_ROLES, true );
$protected_roles = ppw_free_fix_serialize_data( $role_meta );
if ( empty( $protected_roles ) ) {
return false;
}
return $this->check_password_type_is_roles( $current_roles, $protected_roles, $password, $post_id );
}
/**
* Set password to cookie
*
* @param $password
* @param $cookie_name
*/
public function set_password_to_cookie( $password, $cookie_name ) {
$password_hashed = wp_hash_password( $password );
$expire = apply_filters( PPW_Constants::HOOK_COOKIE_EXPIRED, time() + 7 * DAY_IN_SECONDS );
$password_cookie_expired = ppw_core_get_setting_type_string( PPW_Constants::COOKIE_EXPIRED );
if ( ! empty( $password_cookie_expired ) ) {
$time = explode( ' ', $password_cookie_expired )[0];
$unit = ppw_core_get_unit_time( $password_cookie_expired );
if ( 0 !== $unit ) {
$expire = apply_filters( PPW_Constants::HOOK_COOKIE_EXPIRED, time() + (int) $time * $unit );
}
}
$referer = wp_get_referer();
if ( $referer ) {
$secure = ( 'https' === parse_url( $referer, PHP_URL_SCHEME ) );
} else {
$secure = false;
}
$expire = apply_filters( 'ppw_cookie_expire', $expire );
$expire = apply_filters( 'ppwp_cookie_expiry', $expire );
return setcookie( $cookie_name . COOKIEHASH, $password_hashed, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure );
}
public function generate_cookie_data( $cookie_name, $password ) {
$password_hashed = wp_hash_password( $password );
$expire = apply_filters( PPW_Constants::HOOK_COOKIE_EXPIRED, time() + 7 * DAY_IN_SECONDS );
$password_cookie_expired = ppw_core_get_setting_type_string( PPW_Constants::COOKIE_EXPIRED );
if ( ! empty( $password_cookie_expired ) ) {
$time = explode( ' ', $password_cookie_expired )[0];
$unit = ppw_core_get_unit_time( $password_cookie_expired );
if ( 0 !== $unit ) {
$expire = apply_filters( PPW_Constants::HOOK_COOKIE_EXPIRED, time() + (int) $time * $unit );
}
}
$referer = wp_get_referer();
if ( $referer ) {
$secure = ( 'https' === parse_url( $referer, PHP_URL_SCHEME ) );
} else {
$secure = false;
}
$expire = apply_filters( 'ppw_cookie_expire', $expire );
$expire = apply_filters( 'ppwp_cookie_expiry', $expire );
return array(
'name' => $cookie_name . COOKIEHASH,
'value' => $password_hashed,
'expire' => $expire,
);
}
/**
* Set password to cookie with case cookie name the same WP to bypass cache
*
* @param string $cookie_value The value of the cookie.
* @param string $cookie_name The name of the cookie.
*/
public function set_cookie_bypass_cache( $cookie_value, $cookie_name ) {
// Bypass Caching plugin with WordPress cookie.
$this->set_password_to_cookie( $cookie_value, PPW_Constants::WP_POST_PASS );
$this->set_password_to_cookie( $cookie_value, $cookie_name );
}
/**
* Check whether the current cookie is valid.
*
* @param $post_id
* @param $passwords
* @param $cookie_name
*
* @return bool
*/
public function is_valid_cookie( $post_id, $passwords, $cookie_name ) {
$_cookie = wp_unslash( $_COOKIE );
if ( ! isset( $_cookie[ $cookie_name . $post_id . COOKIEHASH ] ) ) {
return false;
}
$cookie = sanitize_text_field( $_cookie[ $cookie_name . $post_id . COOKIEHASH ] );
$hash = wp_unslash( $cookie );
$roles = ppw_core_get_current_role();
foreach ( $passwords as $password ) {
if ( wp_check_password( $password . $post_id, $hash ) ) {
return true;
}
foreach ( $roles as $role ) {
if ( wp_check_password( $password . $role . $post_id, $hash ) ) {
return true;
}
}
}
return false;
}
/**
* Redirect after enter password
*
* @param bool $is_valid Is entered password valid.
*/
public function handle_redirect_after_enter_password( $is_valid ) {
// Refactor since 1.4.2.
// 1. Clean code
// 2. Easier to write UT.
$redirect_url = $this->get_redirect_url( $is_valid );
wp_safe_redirect( $redirect_url );
exit();
}
/**
* Get redirect URL after user entered password.
*
* @param bool $is_valid Is password valid.
*
* @return string
*/
public function get_redirect_url( $is_valid ) {
$referrer_url = $this->get_referer_url();
$params_in_referer = ppw_core_get_param_in_url( $referrer_url );
if ( $is_valid ) {
$referrer_url = apply_filters(
'ppwp_ppf_referrer_url',
$referrer_url,
array(
'is_valid' => $is_valid,
'parameters' => $params_in_referer,
)
);
$url_redirect = preg_replace( '/[&?]' . PPW_Constants::WRONG_PASSWORD_PARAM . '=true/', '', $referrer_url );
$params = apply_filters(
PPW_Constants::HOOK_PARAM_PASSWORD_SUCCESS,
array(
'name' => PPW_Constants::PASSWORD_PARAM_NAME,
'value' => PPW_Constants::PASSWORD_PARAM_VALUE,
)
);
if ( array_key_exists( $params['name'], $params_in_referer ) && '1' === $params_in_referer[ $params['name'] ] ) {
return $url_redirect;
}
$params_in_redirect = ppw_core_get_param_in_url( $url_redirect );
$new_param = empty( $params_in_redirect ) ? '?' . $params['name'] . '=' . $params['value'] : '&' . $params['name'] . '=' . $params['value'];
$new_param = apply_filters(
'ppwp_ppf_redirect_url_param_before_return_content',
$new_param
);
return apply_filters( 'ppwp_ppf_redirect_url_before_return_content', $url_redirect . $new_param );
}
if ( array_key_exists( PPW_Constants::WRONG_PASSWORD_PARAM, $params_in_referer ) && 'true' === $params_in_referer[ PPW_Constants::WRONG_PASSWORD_PARAM ] ) {
return apply_filters(
'ppwp_ppf_redirect_url',
$referrer_url,
array(
'is_valid' => $is_valid,
'parameters' => $params_in_referer,
)
);
}
$new_param = empty( $params_in_referer ) ? '?' . PPW_Constants::WRONG_PASSWORD_PARAM . '=true' : '&' . PPW_Constants::WRONG_PASSWORD_PARAM . '=true';
return apply_filters(
'ppwp_ppf_redirect_url',
$referrer_url . $new_param,
array(
'is_valid' => $is_valid,
'parameters' => $params_in_referer,
)
);
}
/**
* Get referer URL from HTTP Referrer or callback URL in post form action URL.
*
* @return mixed False if cannot find the referer URL.
*/
public function get_referer_url() {
$referrer_url = wp_get_referer();
$using_cb = false === $referrer_url;
$using_cb = apply_filters( 'ppw_use_callback_url', $using_cb );
if ( $using_cb ) {
// We need to get the callback URL in the password form action URL.
// in case Referrer-Policy is set no-referrer.
$cb_param = PPW_Constants::CALL_BACK_URL_PARAM;
if ( isset( $_GET[ $cb_param ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We no need to handle nonce verfication for this one because already verify in parent function.
$referrer_url = rawurldecode( esc_url_raw( wp_unslash( $_GET[ $cb_param ] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We no need to handle nonce verfication for this one because already verify in parent function.
}
}
// If doesn't have callback URL and no-referer then return to home page.
if ( false === $referrer_url ) {
global $wp;
$referrer_url = home_url( $wp->request );
}
return $referrer_url;
}
/**
* Handle and check condition before create new password
*
* @param int|string $id The post ID.
* @param string $role_selected The role user select on client.
* @param array $new_global_passwords List global passwords user enter on client.
* @param string $new_role_password Role password user enter on client.
*
* @return array|mixed
*/
public function create_new_password( $id, $role_selected, $new_global_passwords, $new_role_password ) {
$post_meta = get_post_meta( $id, PPW_Constants::POST_PROTECTION_ROLES, true );
$current_roles_password = ppw_free_fix_serialize_data( $post_meta );
$current_global_passwords = get_post_meta( $id, PPW_Constants::GLOBAL_PASSWORDS, true );
if ( 'global' === $role_selected ) {
return $this->create_password_type_global( $id, $new_global_passwords, $current_global_passwords, $current_roles_password, $role_selected );
}
return $this->create_password_type_role( $id, $role_selected, $new_role_password, $current_global_passwords, $current_roles_password );
}
/**
* Check condition before create new password type global
*
* @param int|string $id The post ID.
* @param array $new_global_passwords List global passwords user enter on client.
* @param array $current_global_passwords List all current global passwords.
* @param array $current_roles_password List all current role passwords.
* @param string $role_selected The role user select on client.
*
* @return mixed
*/
public function create_password_type_global( $id, $new_global_passwords, $current_global_passwords, $current_roles_password, $role_selected ) {
// Validate global password(check bad request).
if ( $this->global_passwords_is_bad_request( $new_global_passwords ) ) {
wp_send_json(
array(
'is_error' => true,
'message' => PPW_Constants::BAD_REQUEST_MESSAGE,
),
400
);
wp_die();
}
// Validate global password(empty and duplicate).
ppw_free_validate_password_type_global( $new_global_passwords, $current_global_passwords, $current_roles_password );
update_post_meta( $id, PPW_Constants::GLOBAL_PASSWORDS, $new_global_passwords );
// Clear cache for Cache plugin.
ppw_core_clear_cache_by_id( $id );
/*
// Handle cache for page/post have password type is global with Super Cache plugin.
$free_cache = new PPW_Cache_Services();
$free_cache->handle_cache_for_password_type_global_with_super_cache( $new_global_passwords, $id, $current_roles_password );
*/
$current_roles_password[ $role_selected ] = implode( "\n", $new_global_passwords );
return $current_roles_password;
}
/**
* Check bad request with data type is global passwords
*
* @param array $passwords Global passwords.
*
* @return bool
*/
private function global_passwords_is_bad_request( $passwords ) {
foreach ( $passwords as $password ) {
if ( strpos( $password, ' ' ) !== false ) {
return true;
}
}
// Check element unique in array.
return count( $passwords ) !== count( array_unique( $passwords ) );
}
/**
* Check condition before create new password type role
*
* @param int|string $id The post ID.
* @param string $role_selected The role user select on client.
* @param string $new_role_password Role password user enter on client.
* @param array $current_global_passwords List all current global passwords.
* @param array $current_roles_password List all current role passwords.
*
* @return mixed
*/
public function create_password_type_role( $id, $role_selected, $new_role_password, $current_global_passwords, $current_roles_password ) {
// Validate role password(check bad request).
if ( $this->role_password_is_bad_request( $new_role_password ) ) {
wp_send_json(
array(
'is_error' => true,
'message' => PPW_Constants::BAD_REQUEST_MESSAGE,
),
400
);
wp_die();
}
// Validate role password(empty and duplicate).
ppw_free_validate_password_type_role( $role_selected, $new_role_password, $current_global_passwords, $current_roles_password );
$current_roles_password[ $role_selected ] = $new_role_password;
delete_post_meta( $id, PPW_Constants::POST_PROTECTION_ROLES );
add_post_meta( $id, PPW_Constants::POST_PROTECTION_ROLES, $current_roles_password );
// Clear cache for Cache plugin.
ppw_core_clear_cache_by_id( $id );
/*
// Handle cache for page/post have password type is role with Super Cache plugin.
$free_cache = new PPW_Cache_Services();
$free_cache->handle_cache_for_password_type_role_with_super_cache( $new_role_password, $id, $current_roles_password, $current_global_passwords );
*/
if ( ! empty( $current_global_passwords ) ) {
$current_roles_password['global'] = implode( "\n", $current_global_passwords );
}
return $current_roles_password;
}
/**
* Check bad request with data type is role password
*
* @param string $password Role password.
*
* @return bool
*/
private function role_password_is_bad_request( $password ) {
return strpos( $password, ' ' ) !== false;
}
/**
* Password is empty with not 0.
*
* @param string $pwd Password.
*
* @return bool
*/
public function has_no_empty_password( $pwd ) {
return ! empty( $pwd ) || '0' === $pwd;
}
/**
* Get all passwords
*
* @param int|string $post_id The post ID.
*
* @return array
*/
public function get_passwords( $post_id ) {
// 1. Get all passwords.
$global_passwords = get_post_meta( $post_id, PPW_Constants::GLOBAL_PASSWORDS, true );
$global_passwords = ! empty( $global_passwords ) ? $global_passwords : array();
$has_global_passwords = ! empty( $global_passwords ) && is_array( $global_passwords );
$raw_data = get_post_meta( $post_id, PPW_Constants::POST_PROTECTION_ROLES, true );
$protected_roles = ppw_free_fix_serialize_data( $raw_data );
$filtered_protected_roles = array_filter(
$protected_roles,
function ( $pass ) {
return $this->has_no_empty_password( $pass );
}
);
$has_role_passwords = ! empty( $filtered_protected_roles );
$has_current_role_password = false;
if ( $has_role_passwords ) {
$roles = ppw_core_get_current_role();
foreach ( $roles as $role ) {
if ( array_key_exists( $role, $filtered_protected_roles ) ) {
$has_current_role_password = true;
array_push( $global_passwords, $protected_roles[ $role ] );
}
}
}
return array(
'passwords' => $global_passwords,
'has_role_passwords' => $has_role_passwords,
'has_current_role_password' => $has_current_role_password,
'has_global_passwords' => $has_global_passwords,
);
}
/**
* Check password type is global
*
* @param $post_id
* @param $password
*
* @return bool
*/
public function check_password_type_is_global( $post_id, $password ) {
$global_passwords = get_post_meta( $post_id, PPW_Constants::GLOBAL_PASSWORDS, true );
if ( empty( $global_passwords ) || ! is_array( $global_passwords ) ) {
return false;
}
foreach ( $global_passwords as $pass ) {
if ( $password === $pass ) {
return true;
}
}
return false;
}
/**
* Check password type is roles
*
* @param $current_roles
* @param $protectedRoles
* @param $password
* @param $post_id
*
* @return bool
*/
public function check_password_type_is_roles( $current_roles, $protectedRoles, $password, $post_id ) {
foreach ( $current_roles as $role ) {
if ( ! array_key_exists( $role, $protectedRoles ) || ! $this->has_no_empty_password( $protectedRoles[ $role ] ) || $protectedRoles[ $role ] !== $password ) {
continue;
}
$this->set_cookie_bypass_cache( $password . $role . $post_id, PPW_Constants::COOKIE_NAME . $post_id );
return true;
}
return false;
}
/**
* Migrate default password and update post password of Wordpress for free version
* TODO: need to revamp the logic.
*/
function migrate_default_password() {
$posts = ppw_core_get_posts_password_protected_by_wp();
error_log( '[Migrate Default PWD]Things to migrate: ' . wp_json_encode( $posts ) );
error_log( sprintf( '[Migrate Default PWD]Total: %d', count( $posts ) ) );
foreach ( $posts as $post ) {
$post_id = $post->ID;
error_log( sprintf( '[Migrate Default PWD]Migrating password for post %d', $post_id ) );
$global_password = get_post_meta( $post_id, PPW_Constants::GLOBAL_PASSWORDS, true );
$global_password = ! empty( $global_password ) ? $global_password : array();
$raw_data = get_post_meta( $post->ID, PPW_Constants::POST_PROTECTION_ROLES, true );
// 1. Update password for role
$protected_roles = ppw_free_fix_serialize_data( $raw_data );
foreach ( $protected_roles as $key => $value ) {
if ( str_replace( " ", "", $post->post_password ) === $value ) {
$protected_roles[ $key ] = '';
update_post_meta( $post_id, PPW_Constants::POST_PROTECTION_ROLES, $protected_roles );
}
}
// 2. Update password for global
if ( ! in_array( str_replace( " ", "", $post->post_password ), $global_password ) ) {
array_push( $global_password, str_replace( " ", "", $post->post_password ) );
}
update_post_meta( $post_id, PPW_Constants::GLOBAL_PASSWORDS, $global_password );
update_post_meta( $post_id, 'ppwp_post_password_bk', $post->post_password );
// 3. Update default password for Wordpress
wp_update_post( array(
'ID' => $post_id,
'post_password' => '',
) );
}
}
public function get_pw_meta( $post_id = false ) {
global $wpdb;
$table_name = $wpdb->prefix . 'postmeta';
$global_key = PPW_Constants::GLOBAL_PASSWORDS;
$role_key = PPW_Constants::POST_PROTECTION_ROLES;
$query = $wpdb->prepare( "SELECT * FROM {$table_name} where ( meta_key IN ( %s, %s ) )", $global_key, $role_key ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- We don't want to set table name as placeholder
if ( $post_id ) {
$query = $wpdb->prepare( $query . ' AND post_id = %d', $post_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- we already prepare $query above
}
return $wpdb->get_results( $query ); // phpcs:ignore -- db call ok, we already prepare $query above.
}
public function get_data_to_migrate() {
$ids = $this->get_protected_post_ids();
$result = array();
foreach ( $ids as $post_id ) {
$passwords = $this->get_pw_meta( $post_id );
$result[] = array(
'post_id' => $post_id,
'passwords' => $this->massage_pw_from_post_meta( $passwords ),
);
}
$old = get_option( 'ppw_data_checksum', false );
if ( false === $old ) {
update_option( 'ppw_data_checksum', $result );
return $result;
}
$diff = $this->check_sum_migrate_data( $result, $old );
update_option( 'ppw_data_checksum', $result );
return $diff;
}
public function check_sum_migrate_data( $current, $old ) {
if ( count( $current ) > count( $old ) ) {
$large = $current;
$small = $old;
} else {
$large = $old;
$small = $current;
}
$post_ids = array_column( $small, 'post_id' );
$result = array();
foreach ( $large as $cur ) {
$post_id = $cur['post_id'];
$found_index = array_search( $post_id, $post_ids );
if ( false === $found_index ) {
$result[] = $cur;
continue;
}
$found = $small[ $found_index ];
if ( ! isset ( $found['passwords'] ) ) {
continue;
}
if ( $this->compare_passwords( $cur, $found ) ) {
$result[] = $found;
}
}
return $result;
}
/**
* Massage password from post meta
*
* @param array $meta post meta from DB.
*
* @return array
*/
public function massage_pw_from_post_meta( $meta ) {
$result = array(
'global' => array(),
'role' => array(),
);
foreach ( $meta as $val ) {
if ( PPW_Constants::GLOBAL_PASSWORDS === $val->meta_key ) {
$meta_value = ppw_free_fix_serialize_data( $val->meta_value );
$result['global'] = array_merge( $result['global'], $meta_value );
} elseif ( PPW_Constants::POST_PROTECTION_ROLES === $val->meta_key ) {
$meta_value = ppw_free_fix_serialize_data( @unserialize( $val->meta_value ) );
$result['role'] = $this->massage_pw_for_roles_from_post_meta( $meta_value );
}
}
return $result;
}
/**
* Massage by define a password - role map
*
* Input: [ "admin" => "1", "editor" => "2", author => "1"]
* Output: [ "1" => array('admin', 'author'), "2" => array('editor') ]
*
* @param $meta_value
*
* @return array
*/
public function massage_pw_for_roles_from_post_meta( $meta_value ) {
$result = array();
if ( ! is_array( $meta_value ) ) {
return $result;
}
foreach ( $meta_value as $role => $pw ) {
if ( '' === $pw ) {
continue;
}
if ( ! array_key_exists( $pw, $result ) ) {
$result[ $pw ] = array( $role );
}
if ( ! in_array( $role, $result[ $pw ] ) ) {
array_push( $result[ $pw ], $role );
}
}
return $result;
}
public function get_protected_post_ids() {
$role_key = PPW_Constants::POST_PROTECTION_ROLES;
$results = array_filter( $this->get_pw_meta(), function ( $value ) use ( $role_key ) {
$meta_value = ppw_free_fix_serialize_data( @unserialize( $value->meta_value ) );
$is_valid_meta_value = is_array( $meta_value );
if ( $is_valid_meta_value && $role_key === $value->meta_key ) {
foreach ( $meta_value as $meta ) {
return $meta !== '';
}
}
return $is_valid_meta_value && count( $meta_value ) > 0;
} );
return array_unique(
array_map( function ( $val ) {
return $val->post_id;
}, $results )
);
}
/**
* Generate custom row action.
*
* @param array $actions An array for row action.
* @param stdClass $post The post object.
*
* @return array
*/
public function generate_custom_row_action( $actions, $post ) {
$post_id = $post->ID;
$is_protected = $this->is_protected_content( $post_id );
$btn_label = $is_protected ? __( 'Unprotect', PPW_Constants::DOMAIN ) : __( 'Protect', PPW_Constants::DOMAIN );
$title = $is_protected ? __( 'Unprotect this page', PPW_Constants::DOMAIN ) : __( 'Protect this page', PPW_Constants::DOMAIN );
$protection_status = $is_protected ? PPW_Constants::PROTECTION_STATUS['unprotect'] : PPW_Constants::PROTECTION_STATUS['protect'];
$actions['ppw_protect'] = '<a style="cursor: pointer" data-ppw-status="' . $protection_status . '" onclick="ppwpRowAction.handleOnClickRowAction(' . $post_id . ')" id="ppw-protect-post_' . $post_id . '" class="ppw-protect-action" title="' . $title . '">' . $btn_label . '</a>';
return $actions;
}
/**
* Handle protect page/post.
*
* @param int $post_id The post ID.
*/
public function protect_page_post( $post_id ) {
$password = array(
uniqid( '', false )
);
$this->create_new_password( $post_id, 'global', $password, null );
}
/**
* Handle unprotect page/post.
*
* @param int $post_id The post ID.
*/
public function unprotect_page_post( $post_id ) {
delete_post_meta( $post_id, PPW_Constants::POST_PROTECTION_ROLES );
delete_post_meta( $post_id, PPW_Constants::GLOBAL_PASSWORDS );
}
/**
* Update post status request from row action
*
* @param array $request Request from row action.
*/
public function update_post_status( $request ) {
if ( ! isset( $request['postId'] ) || ! isset( $request['status'] ) ) {
send_json_data_error( __( 'Our server cannot understand the data request!', PPW_Constants::DOMAIN ) );
}
$post_id = $request['postId'];
$client_status = (int) $request['status'];
if ( ! in_array( $client_status, array_values( PPW_Constants::PROTECTION_STATUS ), true ) ) {
send_json_data_error( __( 'Our server cannot understand the data request!', PPW_Constants::DOMAIN ) );
}
$server_status = $client_status;
$message = __( 'Oops! Something went wrong. Please reload the page and try again.', PPW_Constants::DOMAIN );
$status_request = 400;
if ( PPW_Constants::PROTECTION_STATUS['protect'] === $client_status ) {
if ( ! $this->is_protected_content( $post_id ) ) {
$this->protect_page_post( $post_id );
$server_status = PPW_Constants::PROTECTION_STATUS['unprotect'];
$message = __( 'Great! You\'ve successfully protected this page.', PPW_Constants::DOMAIN );
$status_request = 200;
}
} else {
if ( $this->is_protected_content( $post_id ) ) {
$this->unprotect_page_post( $post_id );
$server_status = PPW_Constants::PROTECTION_STATUS['protect'];
$message = __( 'Great! You\'ve successfully unprotected this page.', PPW_Constants::DOMAIN );
$status_request = 200;
}
}
wp_send_json(
array(
'is_error' => 200 === $status_request ? false : true,
'server_status' => $server_status,
'message' => $message,
),
$status_request
);
wp_die();
}
/**
* @param $pwds
*
* @return array
*/
private function massage_role_pwd( $pwds ) {
return array_map( function ( $v ) {
natsort( $v );
return implode( ',', $v );
}, $pwds );
}
/**
* @param $cur
* @param $found
*
* @return bool
*/
private function compare_passwords( $cur, $found ) {
$global_diff = $this->advance_array_diff( $cur['passwords']['global'], $found['passwords']['global'] );
if ( ! empty( $global_diff ) ) {
return true;
}
$current_roles = $this->massage_role_pwd( $cur['passwords']['role'] );
$new_roles = $this->massage_role_pwd( $found['passwords']['role'] );
$role_diff = $this->advance_array_diff( $current_roles, $new_roles );
return ! empty ( $role_diff );
}
/**
* @param $first
* @param $second
*
* @return array
*/
private function advance_array_diff( $first, $second ) {
return array_merge( array_diff(
$first,
$second
), array_diff(
$second,
$first
) );
}
/**
* Valid permission of post ID.
*
* @param bool $required Required Password.
* @param int $post_id Post ID.
*
* @return bool True|False. True: Password is required so it will render form.
*/
public function is_valid_permission( $required, $post_id ) {
// 1. Check page/post is protected.
$result = $this->is_protected_content( $post_id );
if ( false === $result ) {
return false;
}
// 2. Check master password is valid.
$is_valid_master_password = $this->check_master_password_is_valid( $post_id );
if ( apply_filters( 'ppw_is_valid_cookie', $is_valid_master_password, $post_id ) ) {
return false;
}
// 3. Check password in cookie.
$passwords = $result['passwords'];
$is_valid = $this->is_valid_cookie( $post_id, $passwords, PPW_Constants::COOKIE_NAME );
return false === apply_filters( 'ppw_is_valid_cookie', $is_valid, $post_id );
}
/**
* Check password is exist when user enter password in form.
*
* @param int $post_id Post ID.
* @param string $password Password which user enter.
*/
public function handle_after_enter_password_in_password_form( $post_id, $password ) {
$using_recaptcha = PPW_Recaptcha::get_instance()->using_single_recaptcha();
if ( $using_recaptcha && ! PPW_Recaptcha::get_instance()->is_valid_recaptcha() ) {
do_action( 'ppw_redirect_after_enter_password', false );
}
$is_valid = $this->is_valid_password_from_request( $post_id, $password );
do_action( 'ppw_redirect_after_enter_password', $is_valid );
}
public function is_valid_password_from_request( $post_id, $password ) {
// Get current role of current user.
$current_roles = ppw_core_get_current_role();
$is_pro_activate = apply_filters( PPW_Constants::HOOK_IS_PRO_ACTIVATE, false );
if ( $is_pro_activate ) {
$is_valid = apply_filters( PPW_Constants::HOOK_CHECK_PASSWORD_IS_VALID, false, $password, $post_id, $current_roles );
/**
* Check post is protected by pro version to handle master password.
*/
if ( $this->is_protected_content_by_pro( $post_id ) ) {
$is_valid = $this->handle_master_passwords( $password, $is_valid, $current_roles, $post_id );
}
} else {
$is_valid = $this->is_valid_free_password( $post_id, $password, $current_roles );
}
return apply_filters( 'ppw_is_valid_password', $is_valid, $post_id, $password, $current_roles );
}
/**
* Is valid free Password.
*
* @param integer $post_id Post ID.
* @param string $password Password.
* @param array $current_roles Current user roles.
*
* @return bool True is valid password, false is no.
*/
public function is_valid_free_password( $post_id, $password, $current_roles ) {
$is_valid = $this->is_valid_password( $password, $post_id, $current_roles );
if ( $this->is_protected_content( $post_id ) ) {
$is_valid = $this->handle_master_passwords( $password, $is_valid, $current_roles, $post_id );
}
return $is_valid;
}
/**
* Check is protected content by pro version.
*
* @param integer $post_id Post ID.
*
* @return bool Is content protected by pro.
*/
public function is_protected_content_by_pro( $post_id ) {
if ( ! function_exists( 'ppw_pro_get_post_id_follow_protect_child_page' ) || ! method_exists( 'PPW_Pro_Password_Services', 'is_protected_content' ) ) {
return false;
}
$password_pro_service = new PPW_Pro_Password_Services();
/**
* Get parent post id if post have parent-child page.
*/
$new_post_id = ppw_pro_get_post_id_follow_protect_child_page( $post_id );
/**
* Check post or page is protected.
* TODO: Improve with global variable and refactor PPWP pro to check post is protected with Global variables.
*/
return apply_filters( PPW_Constants::HOOK_CHECK_CONTENT_IS_PROTECTED_BY_PRO, $password_pro_service->is_protected_content( $new_post_id ), $post_id );
}
/**
* Check master password is exist and apply for this post.
*
* @param string $password Password.
* @param bool $is_valid Valid password before.
* @param array $current_roles Current roles.
* @param int $post_id Post ID.
*
* @return bool Allow open content or not.
*/
public function handle_master_passwords( $password, $is_valid, $current_roles, $post_id ) {
$password_info = $this->passwords_repository->get_master_password_info_by_password( $password );
if ( is_null( $password_info ) ) {
return $is_valid;
}
// Check post type is exist in password.
if ( ! $this->check_post_type_for_master_password( $post_id, $password_info ) ) {
return $is_valid;
}
$result = $this->check_valid_master_password( $password_info, $current_roles, $password );
if ( $result['is_valid'] ) {
/**
* Save cookie to client.
* If $result['role'] is not empty then password will be role password
* Else global password.
*/
$this->set_cookie_bypass_cache( $password . $result['role'] . $password_info->id, PPW_Constants::MASTER_COOKIE_NAME . $password_info->id );
// Count when user enter right password.
$this->passwords_repository->update_password(
$password_info->id,
array(
'hits_count' => (int) $password_info->hits_count + 1,
)
);
ppw_tracking_master_password( $post_id, $password, 'single' );
return true;
}
return $is_valid;
}
/**
* Check valid master password when user enter.
*
* @param array $password_info Password information get from database.
* @param array $current_roles Current user roles.
* @param string $password Password.
*
* @return array
*/
public function check_valid_master_password( $password_info, $current_roles, $password ) {
$password_types = $password_info->campaign_app_type;
$default_values = array(
'is_valid' => false,
'role' => '',
);
// Check with global master password.
if ( PPW_Constants::PPW_MASTER_GLOBAL === $password_types ) {
$default_values['is_valid'] = true;
return $default_values;
}
// Check with role master password.
$role = $this->get_role_of_master_password( $current_roles, $password_types );
if ( false !== $role ) {
$default_values['is_valid'] = true;
$default_values['role'] = $role;
return $default_values;
}
return $default_values;
}
/**
* Check valid master password when user enter.
*
* @param array $password_info Password information get from database.
* @param array $current_roles Current user roles.
* @param string $prefix_role Prefix role.
*
* @return array
*/
public function validate_password( $password_info, $current_roles, $prefix_role, $global_role ) {
$password_types = $password_info->campaign_app_type;
$default_values = array(
'is_valid' => false,
'role' => '',
);
// Check with global master password.
if ( $global_role === $password_types ) {
$default_values['is_valid'] = true;
return $default_values;
}
// Check with role master password.
$role = $this->get_current_password_role( $current_roles, $password_types, $prefix_role );
if ( false !== $role ) {
$default_values['is_valid'] = true;
$default_values['role'] = $role;
return $default_values;
}
return $default_values;
}
/**
* Check post type is valid for master password.
*
* @param string $post_id Post ID.
* @param object $password_info Password information from database.
*
* @return bool Check post type is exist in database.
*/
public function check_post_type_for_master_password( $post_id, $password_info ) {
$post_type = get_post_type( $post_id );
// Valid post type data.
if ( false === $post_type || empty( $password_info->post_types ) ) {
return false;
}
$post_types_protection = apply_filters( PPW_Constants::HOOK_MASTER_PASSWORDS_VALID_POST_TYPES, array( 'post' ) );
// Check if post type exist in settings.
if ( ! in_array( $post_type, $post_types_protection, true ) ) {
return false;
}
$post_types_selected = explode( ';', $password_info->post_types );
return in_array( $post_type, $post_types_selected, true );
}
/**
* Check password for type is roles.
*
* @param array $current_roles List current roles.
* @param string $password_types password type in DB.
*
* @return bool|mixed
*/
public function get_role_of_master_password( $current_roles, $password_types ) {
$type_array = explode( ';', $password_types );
foreach ( $type_array as $password_type ) {
$role = str_replace( PPW_Constants::PPW_MASTER_ROLE, '', $password_type );
if ( in_array( $role, $current_roles, true ) ) {
return $role;
}
}
return false;
}
/**
* Check password for type is roles.
*
* @param array $current_roles List current roles.
* @param string $password_types password type in DB.
*
* @return bool|mixed
*/
public function get_current_password_role( $current_roles, $password_types, $prefix_role ) {
$type_array = explode( ';', $password_types );
foreach ( $type_array as $password_type ) {
$role = str_replace( $prefix_role, '', $password_type );
if ( in_array( $role, $current_roles, true ) ) {
return $role;
}
}
return false;
}
/**
* Check protection in settings
*
* @param int $post_id The post ID.
*
* @return bool
*/
public function check_protection( $post_id ) {
$post_type = get_post_type( $post_id );
if ( 'post' === $post_type || 'page' === $post_type ) {
return true;
}
return false;
}
/**
* Check if master passwords in cookie is valid.
*
* @param int $post_id Post ID.
*
* @return bool True if password is valid.
*/
public function check_master_password_is_valid( $post_id ) {
if ( ! $this->check_master_cookies_are_exist() ) {
return false;
}
$master_passwords = $this->passwords_repository->get_activate_master_passwords_info();
// Get all passwords which exist current post type.
$master_passwords = $this->massage_master_passwords_with_post_type( $master_passwords, $post_id );
$master_passwords = apply_filters('ppw_cookie_master_passwords', $master_passwords, $post_id );
if ( count( $master_passwords ) > 0 ) {
// Valid master cookies.
foreach ( $master_passwords as $master_password ) {
if ( $this->is_valid_cookie( $master_password->id, array( $master_password->password ), PPW_Constants::MASTER_COOKIE_NAME ) ) {
return true;
}
}
}
return false;
}
/**
* Get master passwords which post type is valid.
*
* @param array $master_passwords List Master Passwords from database.
* @param int $post_id Post ID.
*
* @return array List master passwords after valid post type.
*/
public function massage_master_passwords_with_post_type( $master_passwords, $post_id ) {
$post_type = get_post_type( $post_id );
if ( false === $post_type ) {
return array();
}
$post_types_protection = apply_filters( PPW_Constants::HOOK_MASTER_PASSWORDS_VALID_POST_TYPES, array( 'post' ) );
if ( ! in_array( $post_type, $post_types_protection, true ) ) {
return array();
}
return array_filter(
$master_passwords,
function ( $master_password ) use ( $post_type ) {
if ( empty( $master_password->post_types ) ) {
return false;
}
$post_types = explode( ';', $master_password->post_types );
return in_array( $post_type, $post_types, true );
}
);
}
/**
* Check master cookie is exist.
*
* @return bool True if master cookie is exist.
*/
public function check_master_cookies_are_exist() {
if ( ! isset( $_COOKIE ) ) {
return false;
}
// Check with cookie name which contains master password name.
foreach ( $_COOKIE as $key => $value ) {
if ( false !== strpos( $key, PPW_Constants::MASTER_COOKIE_NAME ) ) {
return true;
}
}
return false;
}
/**
* Get protection post type after convert it to select options.
*
* @return array Protection post types select option.
*/
public function get_protection_post_types_select() {
$post_types_protection = apply_filters( PPW_Constants::HOOK_MASTER_PASSWORDS_VALID_POST_TYPES, array( 'post' ) );
return array_reduce(
ppw_core_get_all_post_types(),
function ( $carry, $post_type ) use ( $post_types_protection ) {
if ( isset( $post_type->name ) && in_array( $post_type->name, $post_types_protection, true ) ) {
$carry[] = array(
'key' => $post_type->name,
'value' => $post_type->label,
);
}
return $carry;
},
array()
);
}
/**
* Check logic and hide pages/posts protected in home, categories, search results, tags, authors, archive, feed.
*
* @param string $where The WHERE clause of the query.
* @param WP_Query $wp_query The WP_Query instance (passed by reference).
*
* @return string
*/
public function handle_hide_post_protected( $where, $wp_query ) {
$post_types = apply_filters( PPW_Constants::HOOK_CUSTOM_POST_TYPE_HIDE_PROTECTED_POST, PPW_Constants::DEFAULT_POST_TYPE );
$protected_ids = $this->custom_protected_ids();
if ( empty( $protected_ids ) ) {
return $where;
}
foreach ( $post_types as $post_type ) {
$is_hide = ppw_core_get_setting_type_bool( PPW_Constants::HIDE_PROTECTED . $post_type );
if ( ! $is_hide ) {
continue;
}
$position_selected = ppw_core_get_setting_type_array( PPW_Constants::HIDE_SELECTED . $post_type );
$where = ppw_core_handle_logic_add_query( $position_selected, $protected_ids, $where, $post_type );
}
return $where;
}
/**
* Check logic and hide posts protected in recent post
*
* @param array $posts_args An array of arguments used to retrieve the recent posts.
*
* @return array
*/
public function handle_hide_post_protected_recent_post( $posts_args ) {
$post_types = apply_filters( PPW_Constants::HOOK_CUSTOM_POST_TYPE_RECENT_POST, array( 'post' ) );
$protected_ids = $this->custom_protected_ids();
if ( empty( $protected_ids ) ) {
return $posts_args;
}
$old_post_not_in = isset( $posts_args['post__not_in'] ) ? $posts_args['post__not_in'] : array();
foreach ( $post_types as $post_type ) {
$is_hide = ppw_core_get_setting_type_bool( PPW_Constants::HIDE_PROTECTED . $post_type );
if ( ! $is_hide ) {
continue;
}
$position_selected = ppw_core_get_setting_type_array( PPW_Constants::HIDE_SELECTED . $post_type );
if ( ! in_array( PPW_Constants::RECENT_POST, $position_selected, true ) ) {
continue;
}
foreach ( $protected_ids as $id ) {
if ( get_post_type( $id ) !== $post_type ) {
continue;
}
$old_post_not_in[] = $id;
}
}
$posts_args['post__not_in'] = $old_post_not_in;
return $posts_args;
}
/**
* Check logic and hide posts protected in next and previous post
*
* @param string $where The WHERE clause of the query.
*
* @return string
*/
public function handle_hide_post_protected_next_and_previous( $where ) {
$post_types = apply_filters( PPW_Constants::HOOK_CUSTOM_POST_TYPE_NEXT_AND_PREVIOUS, array( 'post' ) );
$protected_ids = $this->custom_protected_ids();
if ( empty( $protected_ids ) ) {
return $where;
}
foreach ( $post_types as $post_type ) {
$is_hide = ppw_core_get_setting_type_bool( PPW_Constants::HIDE_PROTECTED . $post_type );
if ( ! $is_hide ) {
continue;
}
$position_selected = ppw_core_get_setting_type_array( PPW_Constants::HIDE_SELECTED . $post_type );
if ( ! in_array( PPW_Constants::NEXT_PREVIOUS, $position_selected, true ) ) {
continue;
}
foreach ( $protected_ids as $id ) {
if ( get_post_type( $id ) !== $post_type ) {
continue;
}
$where .= " AND p.ID != {$id}";
}
}
return $where;
}
/**
* Check condition and exclude protected page in list page get by function get_pages
*
* @param array $pages List of pages to retrieve.
*
* @return array
*/
public function handle_hide_page_protected( $pages ) {
$type = 'page';
$page_hide = ppw_core_get_setting_type_bool( PPW_Constants::HIDE_PROTECTED . $type );
if ( ! $page_hide ) {
return $pages;
}
$protected_ids = $this->custom_protected_ids();
if ( empty( $protected_ids ) ) {
return $pages;
}
$position_selected = ppw_core_get_setting_type_array( PPW_Constants::HIDE_SELECTED . $type );
if ( ! in_array( PPW_Constants::EVERYWHERE_PAGE, $position_selected, true ) ) {
return $pages;
}
foreach ( $protected_ids as $id ) {
if ( 'page' !== get_post_type( $id ) ) {
continue;
}
$pages = array_filter(
$pages,
function ( $page ) use ( $id ) {
return $page->ID !== (int) $id;
}
);
}
return $pages;
}
/**
* Check condition and exclude page/post protected in Yoast SEO XML Sitemaps
*
* @param array $ids List page_id/post_id exclude in Yoast SEO XML Sitemaps.
*
* @return array
*/
public function handle_hide_page_protected_yoast_seo_sitemaps( $ids ) {
$post_types = apply_filters( PPW_Constants::HOOK_CUSTOM_POST_TYPE_HIDE_PROTECTED_POST, PPW_Constants::DEFAULT_POST_TYPE );
$protected_ids = $this->custom_protected_ids();
if ( empty( $protected_ids ) ) {
return $ids;
}
foreach ( $post_types as $post_type ) {
$is_hide = ppw_core_get_setting_type_bool( PPW_Constants::HIDE_PROTECTED . $post_type );
if ( ! $is_hide ) {
continue;
}
$position_selected = ppw_core_get_setting_type_array( PPW_Constants::HIDE_SELECTED . $post_type );
// Push the post ID into list exclude from site map.
$ids = ppw_core_list_posts_exclude_in_site_maps( $position_selected, $protected_ids, $ids, $post_type );
}
return $ids;
}
/**
* Get protected IDs.
* Declare hook for Pro custom protected IDs to handle hide protected posts.
*
* @return array
*/
public function custom_protected_ids() {
$protected_ids = wp_cache_get( 'ppwp_protected_ids' );
if ( false === $protected_ids ) {
$protected_ids = apply_filters( PPW_Constants::HOOK_CUSTOM_POST_ID_HIDE_PROTECTED_POST, array() );
if ( empty( $protected_ids ) ) {
$protected_ids = $this->get_protected_post_ids();
}
wp_cache_set( 'ppwp_protected_ids', $protected_ids );
}
return $protected_ids;
}
/**
* Restore WP Post password.
*/
public function restore_wp_post_password() {
$post_passwords = $this->passwords_repository->get_wp_post_passwords();
if ( empty( $post_passwords ) ) {
return;
}
foreach ( $post_passwords as $post_password ) {
$post_id = wp_update_post(
array(
'ID' => $post_password->post_id,
'post_password' => $post_password->meta_value,
)
);
if ( $post_id ) {
delete_post_meta( $post_id, $post_password->meta_key, $post_password->meta_value );
}
}
}
}
}