plugin = $plugin; $this->hooks(); } /** * Initiate our hooks. * * @since 0.0.0 */ public function hooks() { } /** * Authenticate a username/password and require plugin-management capability. * * Every failure mode — unknown username, wrong password, or a valid login * that lacks the activate_plugins capability — returns the SAME generic * error. Distinct codes/messages here would let an unauthenticated caller * probe which usernames exist (user enumeration), so they are deliberately * collapsed into one indistinguishable response. * * @since 0.0.0 * * @param string $username Raw username input. * @param string $password Raw password input. * @return WP_User|WP_Error Plugin-capable user on success, or one generic error. */ public static function authenticate( $username, $password ) { $username = sanitize_user( $username ); $password = trim( $password ); $user = apply_filters( 'authenticate', null, $username, $password ); if ( ! is_a( $user, 'WP_User' ) || ! user_can( $user, 'activate_plugins' ) ) { return new WP_Error( 'authentication_failed', __( 'ERROR: Authentication failed.', 'plugin-detective' ) ); } return $user; } public static function create_nonce( $action, $uid = null ) { if ( null === $uid ) { $uid = get_current_user_id(); } $uid = (int) $uid; if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) { $token = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); } else { $token = ''; } $i = strtotime( gmdate( 'Y-m-d' ) ); // Bind the token to the user it was issued for so a low-privileged user's // nonce can never stand in for an administrator's. The uid travels with the // token (the app treats it as opaque) and is re-verified on each request. return $uid . ':' . substr( sha1( DB_PASSWORD . $i . '|' . $action . '|' . $uid . '|' . $token ), -12, 10 ); } public static function verify_nonce( $nonce, $action ) { $nonce = (string) $nonce; if ( empty( $nonce ) ) { return false; } // Tokens are ":" — recover the uid so the hash is checked against // the user it was minted for. Returns that uid on success for the caller's // capability re-check; false otherwise. $parts = explode( ':', $nonce, 2 ); if ( count( $parts ) !== 2 || '' === $parts[1] ) { return false; } $uid = (int) $parts[0]; $provided = $parts[1]; if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) { $token = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); } else { $token = ''; } $i = strtotime( gmdate( 'Y-m-d' ) ); // Nonce generated today (gmt) $expected = substr( sha1( DB_PASSWORD . $i . '|' . $action . '|' . $uid . '|' . $token ), -12, 10 ); if ( hash_equals( $expected, $provided ) ) { return $uid; } // Nonce generated yesterday (gmt) $expected = substr( sha1( DB_PASSWORD . ( $i - 24*60*60 ) . '|' . $action . '|' . $uid . '|' . $token ), -12, 10 ); if ( hash_equals( $expected, $provided ) ) { return $uid; } // Invalid nonce return false; } }