PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 3.5.2
JetFormBuilder — Dynamic Blocks Form Builder v3.5.2
3.6.3.1 3.6.3 3.6.2.2 3.6.2.1 3.6.2 3.6.1.1 3.6.1 3.6.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 3.0.0 3.0.0.1 3.0.0.2 3.0.0.3 3.0.1 3.0.1.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.0.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.3.1 3.3.4 3.3.4.1 3.3.4.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.5.1 3.4.5.2 3.4.6 3.4.7 3.4.7.1 3.5.0 3.5.1 3.5.1.1 3.5.1.2 3.5.2 3.5.2.1 3.5.3 3.5.4 3.5.5 3.5.6 3.5.6.1 3.5.6.2 3.5.6.3 3.6.0
jetformbuilder / modules / webhook / module.php
jetformbuilder / modules / webhook Last commit date
db 2 years ago form-record 2 years ago module.php 2 years ago
module.php
270 lines
1 <?php
2
3
4 namespace JFB_Modules\Webhook;
5
6 use Jet_Form_Builder\Admin\Single_Pages\Meta_Containers\Base_Meta_Container;
7 use Jet_Form_Builder\Db_Queries\Query_Conditions_Builder;
8 use Jet_Form_Builder\Exceptions\Query_Builder_Exception;
9 use JFB_Components\Module\Base_Module_After_Install_It;
10 use JFB_Components\Module\Base_Module_It;
11 use JFB_Modules\Logger\Empty_Logger;
12 use JFB_Modules\Logger\Interfaces\Logger_It;
13 use JFB_Modules\Webhook\Db\Models\Tokens_Model;
14 use JFB_Modules\Webhook\Db\Views\Tokens_View;
15 use JFB_Modules\Security;
16
17 // If this file is called directly, abort.
18 if ( ! defined( 'WPINC' ) ) {
19 die;
20 }
21
22 class Module implements Base_Module_It, Base_Module_After_Install_It {
23
24 const GET_TOKEN_ID = 'jfb_token_id';
25 const GET_TOKEN = 'jfb_token';
26
27 /**
28 * @var Logger_It
29 */
30 private $logger;
31
32 private $verified = false;
33
34 /**
35 * Has value while webhook is processed
36 *
37 * @var string
38 */
39 private $token = '';
40 private $token_id = 0;
41 private $check_hash = true;
42
43 public function rep_item_id() {
44 return 'webhook';
45 }
46
47 public function condition(): bool {
48 return true;
49 }
50
51 public function init_hooks() {
52 if ( $this->is_webhook_request() ) {
53 add_action( 'parse_request', array( $this, 'try_to_catch' ) );
54 }
55 }
56
57 public function remove_hooks() {
58 if ( $this->is_webhook_request() ) {
59 remove_action( 'parse_request', array( $this, 'try_to_catch' ) );
60 }
61 }
62
63 public function on_install() {
64 $this->set_logger( new Empty_Logger() );
65 }
66
67 public function on_uninstall() {
68 }
69
70 public function try_to_catch() {
71 // phpcs:disable WordPress.Security.NonceVerification.Recommended
72 $this->set_token_id( absint( $_GET[ self::GET_TOKEN_ID ] ?? '' ) );
73 $this->set_token( sanitize_key( $_GET[ self::GET_TOKEN ] ?? '' ) );
74 // phpcs:enable WordPress.Security.NonceVerification.Recommended
75
76 $this->confirm();
77 }
78
79 public function confirm() {
80 global $wpdb;
81
82 // reset for case, where multiple webhooks could be triggered
83 $this->verified = false;
84
85 $view = new Tokens_View();
86
87 $expire_conditions = new Query_Conditions_Builder();
88 $expire_conditions->set_relation_or();
89 $expire_conditions->set_condition(
90 array(
91 'type' => Query_Conditions_Builder::TYPE_MORE_STATIC,
92 'values' => array( 'expire_at', current_time( 'mysql', true ) ),
93 )
94 );
95 $expire_conditions->set_condition(
96 array(
97 'type' => Query_Conditions_Builder::TYPE_IS_NULL,
98 'values' => array( 'expire_at' ),
99 )
100 );
101
102 $conditions = array(
103 array(
104 'type' => Query_Conditions_Builder::TYPE_EQUAL,
105 'values' => array( 'id', $this->get_token_id() ),
106 ),
107 $expire_conditions,
108 );
109
110 try {
111 $token_row = $view::findOne( $conditions )->query()->query_one();
112 } catch ( Query_Builder_Exception $exception ) {
113 $this->logger->log(
114 $exception->getMessage(),
115 $conditions
116 );
117
118 return;
119 }
120
121 $table = Tokens_Model::table();
122 $action = $token_row['action'];
123
124 if ( $this->is_check_hash() &&
125 ! Security\Module::get_hasher()->CheckPassword(
126 $this->token,
127 $token_row['hash']
128 )
129 ) {
130 $this->logger->log(
131 sprintf(
132 /* translators: %d - primary id of token row */
133 __( 'Invalid token for %d (primary ID) row', 'jet-form-builder' ),
134 $this->get_token_id()
135 )
136 );
137 do_action( "jet-form-builder/webhook/{$action}", $this );
138
139 return;
140 }
141
142 // custom query to prevent race-condition
143 // phpcs:disabled WordPress.DB
144 $wpdb->query(
145 $wpdb->prepare(
146 "UPDATE {$table} SET
147 {$table}.exec_count = {$table}.exec_count + 1,
148 {$table}.updated_at = %s
149 WHERE 1=1
150 AND {$table}.exec_count < {$table}.limit_exec
151 AND {$table}.id = %d
152 ;
153 ",
154 current_time( 'mysql', 1 ),
155 $this->get_token_id()
156 )
157 );
158 // phpcs:enabled WordPress.DB
159
160 if ( ! $wpdb->rows_affected ) {
161 $this->logger->log(
162 sprintf(
163 /* translators: %d - primary id of token row */
164 __( 'Failed to increment `exec_count` column in %d (primary ID) row', 'jet-form-builder' ),
165 $this->get_token_id()
166 )
167 );
168 do_action( "jet-form-builder/webhook/{$action}", $this );
169
170 return;
171 }
172
173 $this->logger->log(
174 sprintf(
175 /* translators: %d - primary id of token row */
176 __( 'Successfully verified webhook (primary ID -> %d)', 'jet-form-builder' ),
177 $this->get_token_id()
178 ),
179 sprintf( 'hook: jet-form-builder/webhook/%s', $action )
180 );
181
182 $this->verified = true;
183
184 do_action( "jet-form-builder/webhook/{$action}", $this );
185 }
186
187 public function redirect( string $url ) {
188 // make sure we don't have such parameters
189 $url = remove_query_arg( self::GET_TOKEN_ID, $url );
190 $url = remove_query_arg( self::GET_TOKEN, $url );
191
192 // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
193 wp_redirect( $url );
194 die;
195 }
196
197 public function set_logger( Logger_It $logger ) {
198 $this->logger = $logger;
199 }
200
201 /**
202 * @return Logger_It
203 */
204 public function get_logger(): Logger_It {
205 return $this->logger;
206 }
207
208 /**
209 * @return bool
210 */
211 public function is_webhook_request(): bool {
212 // phpcs:disable WordPress.Security.NonceVerification.Recommended
213 return (
214 array_key_exists( self::GET_TOKEN_ID, $_GET ) &&
215 array_key_exists( self::GET_TOKEN, $_GET ) &&
216 is_numeric( $_GET[ self::GET_TOKEN_ID ] )
217 );
218 // phpcs:enable WordPress.Security.NonceVerification.Recommended
219 }
220
221 /**
222 * @return bool
223 */
224 public function is_verified(): bool {
225 return $this->verified;
226 }
227
228 /**
229 * @return int
230 */
231 public function get_token_id(): int {
232 return $this->token_id;
233 }
234
235 /**
236 * @param int $token_id
237 */
238 public function set_token_id( int $token_id ) {
239 $this->token_id = $token_id;
240 }
241
242 /**
243 * @return string
244 */
245 public function get_token(): string {
246 return $this->token;
247 }
248
249 /**
250 * @param string $token
251 */
252 public function set_token( string $token ) {
253 $this->token = $token;
254 }
255
256 /**
257 * @param bool $check_hash
258 */
259 public function set_check_hash( bool $check_hash ) {
260 $this->check_hash = $check_hash;
261 }
262
263 /**
264 * @return bool
265 */
266 public function is_check_hash(): bool {
267 return $this->check_hash;
268 }
269 }
270