Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
65.91% |
29 / 44 |
|
40.00% |
2 / 5 |
CRAP | |
0.00% |
0 / 1 |
Data | |
65.91% |
29 / 44 |
|
40.00% |
2 / 5 |
30.84 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
start | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
init | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
delete_token_on_401_response | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
authenticate | |
92.00% |
23 / 25 |
|
0.00% |
0 / 1 |
9.04 |
1 | <?php |
2 | |
3 | namespace NewfoldLabs\WP\Module\Data; |
4 | |
5 | use wpscholar\Url; |
6 | use function WP_Forge\Helpers\dataGet; |
7 | |
8 | /** |
9 | * Main class for the data plugin module |
10 | */ |
11 | class 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 | } |