Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
65.91% covered (warning)
65.91%
29 / 44
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Data
65.91% covered (warning)
65.91%
29 / 44
40.00% covered (danger)
40.00%
2 / 5
30.84
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 start
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 init
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 delete_token_on_401_response
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 authenticate
92.00% covered (success)
92.00%
23 / 25
0.00% covered (danger)
0.00%
0 / 1
9.04
1<?php
2
3namespace NewfoldLabs\WP\Module\Data;
4
5use wpscholar\Url;
6use function WP_Forge\Helpers\dataGet;
7
8/**
9 * Main class for the data plugin module
10 */
11class Data {
12
13    /**
14     * Hiive Connection instance
15     *
16     * @var HiiveConnection
17     */
18    public $hiive;
19
20    /**
21     * Last instantiated instance of this class.
22     *
23     * @var Data
24     */
25    public static $instance;
26
27    /**
28     * Data constructor.
29     */
30    public function __construct() {
31        self::$instance = $this;
32    }
33
34    /**
35     * Start up the plugin module
36     *
37     * Do this separately so it isn't tied to class creation
38     *
39     * @see bootstrap.php
40     * @see \NewfoldLabs\WP\ModuleLoader\register()
41     */
42    public function start(): void {
43
44        // Delays our primary module setup until init
45        add_action( 'init', array( $this, 'init' ) );
46        add_filter( 'rest_authentication_errors', array( $this, 'authenticate' ) );
47
48        // If we ever get a 401 response from the Hiive API, delete the token.
49        add_filter( 'http_response', array( $this, 'delete_token_on_401_response' ), 10, 3 );
50    }
51
52    /**
53     * Initialize all other module functionality
54     *
55     * @hooked init
56     */
57    public function init(): void {
58
59        $this->hiive = new HiiveConnection();
60
61        $manager = new EventManager();
62        $manager->initialize_rest_endpoint();
63
64        // Initialize the required verification endpoints
65        $this->hiive->register_verification_hooks();
66
67        // If not connected, attempt to connect and
68        // bail before registering the subscribers/listeners
69        if ( ! $this->hiive::is_connected() ) {
70
71            // Attempt to connect
72            $this->hiive->connect();
73
74            return;
75        }
76
77        $manager->init();
78
79        $manager->add_subscriber( $this->hiive );
80
81        if ( defined( 'NFD_DATA_DEBUG' ) && NFD_DATA_DEBUG ) {
82            $this->logger = new Logger();
83            $manager->add_subscriber( $this->logger );
84        }
85    }
86
87    /**
88     * Check HTTP responses for 401 authentication errors from Hiive, delete the invalid token.
89     *
90     * @hooked http_response
91     * @see WP_Http::request()
92     *
93     * @param array  $response The successful HTTP response.
94     * @param array  $args HTTP request arguments.
95     * @param string $url The request URL.
96     *
97     * @return array
98     */
99    public function delete_token_on_401_response( array $response, array $args, string $url ): array {
100
101        if ( strpos( $url, constant( 'NFD_HIIVE_URL' ) ) === 0 && absint( wp_remote_retrieve_response_code( $response ) ) === 401 ) {
102            delete_option( 'nfd_data_token' );
103        }
104
105        return $response;
106    }
107
108    /**
109     * Authenticate incoming REST API requests.
110     *
111     * @hooked rest_authentication_errors
112     *
113     * @param  bool|null|\WP_Error $errors
114     *
115     * @return bool|null|\WP_Error
116     * @see WP_REST_Server::check_authentication()
117     *
118     * @used-by ConnectSite::verifyToken() in Hiive.
119     */
120    public function authenticate( $errors ) {
121
122        // Make sure there wasn't a different authentication method used before this
123        if ( ! is_null( $errors ) ) {
124            return $errors;
125        }
126
127        // Make sure this is a REST API request
128        if ( ! defined( 'REST_REQUEST' ) || ! constant( 'REST_REQUEST' ) ) {
129            return $errors;
130        }
131
132        // If no auth header included, bail to allow a different auth method
133        if ( empty( $_SERVER['HTTP_AUTHORIZATION'] ) ) {
134            return null;
135        }
136
137        $token = str_replace( 'Bearer ', '', $_SERVER['HTTP_AUTHORIZATION'] );
138
139        $data = array(
140            'method'    => $_SERVER['REQUEST_METHOD'],
141            'url'       => Url::getCurrentUrl(),
142            'body'      => file_get_contents( 'php://input' ),
143            'timestamp' => dataGet( getallheaders(), 'X-Timestamp' ),
144        );
145
146        $hash = hash( 'sha256', wp_json_encode( $data ) );
147        $salt = hash( 'sha256', strrev( HiiveConnection::get_auth_token() ) );
148
149        $is_valid = hash( 'sha256', $hash . $salt ) === $token;
150
151        // Allow access if token is valid
152        if ( $is_valid ) {
153
154            if ( isset( $_GET['user_id'] ) ) {
155
156                // If a user ID is provided, use it to find the desired user.
157                $user = get_user_by( 'id', filter_input( INPUT_GET, 'user_id', FILTER_SANITIZE_NUMBER_INT ) );
158
159            } else {
160
161                // If no user ID is provided, find the first admin user.
162                $admins = get_users( array( 'role' => 'administrator' ) );
163                $user   = array_shift( $admins );
164
165            }
166
167            if ( ! empty( $user ) && is_a( $user, \WP_User::class ) ) {
168                wp_set_current_user( $user->ID );
169
170                return true;
171            }
172        }
173
174        // Don't return false, since we could be interfering with a basic auth implementation.
175        return $errors;
176    }
177}