SU ЙqEG@ SU ʹG@ SU FdHH@SU xM1pH@SU 󫪻rH@SU b|H@SU 2u׾HI@SU jᔕh@SU +؎@SU \Pp8B@SU XB@0SU 91S@SU e@PSU ֿ@SU R@&@pSU &@SU q؎6@SU bi`6@ SU 죲؎6@SU ԫ1>@@SU U1@SU |p@`SU -ڝ|$@SU ;"roh@ TU 3=َ7@ TU _`7@ TU *7@ TU `{8@0 TU P8@ TU r8@P TU 8@ TU )v@9@pTU 49@TU { @TU āUqX:@ TU c-Ԩ:@TU al'S;@@TU ԫ1X;@TU *S;@`TU P\;@TU urRَX<@TU q,o؎<@TU %=َ<@TU QvRcH=@0TU [Oȱژ=@TU I3ε=@PTU ,/a?b@TU X>@ PSU K7UAKpƠUƠU 7@7U7UIK%ƠUƠU  7@7U7UQK&ƠUƠU (8@7U7UYK0'ƠUƠU@ x8@7U7UaK'ƠUƠU 8@7U07UiKp(ƠUƠU` 9@7UP7UqK)ƠUƠU h9@7Up7UyK)ƠUƠU 9@7U7UKP*ƠUƠU 9@7U7UK*ƠUƠU :@7U7UK0,ƠUƠU */ do_action( 'trustedlogin/' . $this->config->ns() . '/login/refused', $user_identifier, $is_verified ); return; } $support_user = new SupportUser( $this->config, $this->logging ); $is_logged_in = $support_user->maybe_login( $user_identifier ); if ( is_wp_error( $is_logged_in ) ) { /** * Runs after the support user fails to log in * * @param string $user_identifier Unique Identifier for support user. * @param WP_Error $is_logged_in The error encountered when logging-in. */ do_action( 'trustedlogin/' . $this->config->ns() . '/login/error', $user_identifier, $is_logged_in ); return; } /** * Runs after the support user is logged-in. * * @param string $user_identifier Unique Identifier for support user. */ do_action( 'trustedlogin/' . $this->config->ns() . '/login/after', $user_identifier ); wp_safe_redirect( admin_url() ); exit(); } /** * Hooked Action to maybe revoke support if the request SupportUser::ID_QUERY_PARAM equals the namespace. * * Can optionally check for request SupportUser::ID_QUERY_PARAM for revoking a specific user by their identifier. * * @since 1.0.0 */ public function maybe_revoke_support() { $revoke_param = Utils::get_request_param( self::REVOKE_SUPPORT_QUERY_PARAM ); if ( $this->config->ns() !== $revoke_param ) { return; } $nonce = Utils::get_request_param( '_wpnonce' ); if ( ! $nonce ) { return; } if ( ! wp_verify_nonce( $nonce, self::REVOKE_SUPPORT_QUERY_PARAM ) ) { $this->logging->log( 'Removing user failed: Nonce expired.', __METHOD__, 'error' ); return; } $support_user = new SupportUser( $this->config, $this->logging ); // Allow namespaced support team to revoke their own users. $support_team = current_user_can( $support_user->role->get_name() ); // As well as existing users who can delete other users. $can_delete_users = current_user_can( 'delete_users' ); if ( ! $support_team && ! $can_delete_users ) { wp_safe_redirect( home_url() ); return; } $user_identifier = Utils::get_request_param( SupportUser::ID_QUERY_PARAM ); if ( ! $user_identifier ) { $user_identifier = 'all'; } /** * Trigger action to revoke access based on Support User identifier. * * @used-by Cron::revoke * * @param string $user_identifier Unique ID for TrustedLogin support user or "all". */ do_action( 'trustedlogin/' . $this->config->ns() . '/access/revoke', $user_identifier ); $should_be_deleted = $support_user->get( $user_identifier ); if ( ! empty( $should_be_deleted ) ) { $this->logging->log( 'User #' . $should_be_deleted->ID . ' was not removed', __METHOD__, 'error' ); return; // Don't trigger `access_revoked` if anything fails. } /** * Only triggered when all access has been successfully revoked and no users exist with identifier $identifier. * * @param string $user_identifier Unique TrustedLogin ID for the Support User or "all" */ do_action( 'trustedlogin/' . $this->config->ns() . '/admin/access_revoked', $user_identifier ); } /** * Hooked Action: Add a unique endpoint to WP if a support agent exists * * @since 1.0.0 * @see Endpoint::init() Called via `init` hook */ public function add() { // Only add the endpoint if a TrustedLogin request is being made. if ( ! $this->get_trustedlogin_request() ) { return; } $endpoint = $this->get(); if ( ! $endpoint ) { return; } add_rewrite_endpoint( $endpoint, EP_ROOT ); $this->logging->log( "Endpoint {$endpoint} added.", __METHOD__, 'debug' ); if ( get_site_option( self::PERMALINK_FLUSH_OPTION_NAME ) ) { return; } flush_rewrite_rules( false ); $this->logging->log( 'Rewrite rules flushed.', __METHOD__, 'info' ); $updated_option = update_site_option( self::PERMALINK_FLUSH_OPTION_NAME, 1 ); if ( false === $updated_option ) { $this->logging->log( 'Permalink flush option was not properly set.', 'warning' ); } } /** * Get the site option value at {@see option_name} * * @return string */ public function get() { return (string) get_site_option( $this->option_name ); } /** * Returns sanitized data from a TrustedLogin login $_POST request. * * Note: This is not a security check. It is only used to determine whether the request contains the expected keys. * * @since 1.1 * * @return false|array{action:string, endpoint:string, identifier: string} If false, the request is not from TrustedLogin. If the request is from TrustedLogin, an array with the posted keys, santiized. */ private function get_trustedlogin_request() { // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( ! isset( $_POST[ self::POST_ACTION_KEY ], $_POST[ self::POST_ENDPOINT_KEY ], $_POST[ self::POST_IDENTIFIER_KEY ] ) ) { return false; } // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( self::POST_ACTION_VALUE !== $_POST[ self::POST_ACTION_KEY ] ) { return false; } // phpcs:ignore WordPress.Security.NonceVerification.Missing $_sanitized_post_data = array_map( 'sanitize_text_field', $_POST ); // Return only the expected keys. return array( self::POST_ACTION_KEY => $_sanitized_post_data[ self::POST_ACTION_KEY ], self::POST_ENDPOINT_KEY => $_sanitized_post_data[ self::POST_ENDPOINT_KEY ], self::POST_IDENTIFIER_KEY => $_sanitized_post_data[ self::POST_IDENTIFIER_KEY ], ); } /** * Generate the secret_id parameter as a hash of the endpoint with the identifier. * * @param string $site_identifier_hash The site identifier hash. * @param string $endpoint_hash Optional. The hash of the endpoint. If not provided, it will be generated. * * @return string|WP_Error This hash will be used as an identifier in TrustedLogin SaaS. Or something went wrong. */ public function generate_secret_id( $site_identifier_hash, $endpoint_hash = '' ) { if ( empty( $endpoint_hash ) ) { $endpoint_hash = $this->get_hash( $site_identifier_hash ); } if ( is_wp_error( $endpoint_hash ) ) { return $endpoint_hash; } return Encryption::hash( $endpoint_hash . $site_identifier_hash ); } /** * Generate the endpoint parameter as a hash of the site URL along with the identifier. * * @param string $site_identifier_hash The site identifier hash, used to generate the endpoint hash. * * @return string|WP_Error This hash will be used as the first part of the URL and also a part of $secret_id. */ public function get_hash( $site_identifier_hash ) { return Encryption::hash( get_site_url() . $site_identifier_hash ); } /** * Updates the site's endpoint to listen for logins. Flushes rewrite rules after updating. * * @param string $endpoint The endpoint to add to the site. * * @return bool True: updated; False: didn't change, or didn't update */ public function update( $endpoint ) { $updated = update_site_option( $this->option_name, $endpoint ); update_site_option( self::PERMALINK_FLUSH_OPTION_NAME, 0 ); return $updated; } /** * Deletes the site's endpoint and soft-flushes rewrite rules. * * @return void */ public function delete() { if ( ! get_site_option( $this->option_name ) ) { $this->logging->log( 'Endpoint not deleted because it does not exist.', __METHOD__, 'info' ); return; } delete_site_option( $this->option_name ); flush_rewrite_rules( false ); update_site_option( self::PERMALINK_FLUSH_OPTION_NAME, 0 ); $this->logging->log( 'Endpoint removed & rewrites flushed', __METHOD__, 'info' ); } }