PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.8.1
Defender Security – Malware Scanner, Login Security & Firewall v5.8.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / class-webauthn.php

class-webauthn.php in Defender Security – Malware Scanner, Login Security & Firewall 5.8.1, at src/component/class-webauthn.php

259 lines 7.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles WebAuthn functionalities providing methods to manage and verify user credentials.
4 *
5 * @package WP_Defender\Component
6 */
7
8 namespace WP_Defender\Component;
9
10 use WP_User;
11 use Webauthn\PublicKeyCredentialSource;
12 use Webauthn\PublicKeyCredentialUserEntity;
13 use WP_Defender\Traits\Webauthn as Webauthn_Trait;
14 use Webauthn\PublicKeyCredentialSourceRepository;
15
16 /**
17 * Handles WebAuthn functionalities providing methods to manage and verify user credentials.
18 *
19 * @since 3.0.0
20 */
21 class Webauthn implements PublicKeyCredentialSourceRepository {
22
23 use Webauthn_Trait;
24
25 /**
26 * Option key for storing user credentials.
27 *
28 * @var string
29 */
30 public const CREDENTIAL_OPTION_KEY = 'user_credentials';
31
32 /**
33 * Meta key for storing credentials having userHandle mismatch.
34 *
35 * @var string
36 */
37 public const USER_HANDLE_MISMATCH_KEY = 'user_handle_match_failed';
38
39 /**
40 * Get user credentials.
41 *
42 * @param int $user_id The user ID.
43 *
44 * @return array
45 */
46 public function getCredentials( int $user_id ): array {
47 return $this->get_user_meta( $user_id, self::CREDENTIAL_OPTION_KEY );
48 }
49
50 /**
51 * Set user credentials.
52 *
53 * @param int $user_id The user ID.
54 * @param array $data The credentials data.
55 *
56 * @return bool
57 */
58 public function setCredentials( int $user_id, array $data ): bool {
59 return false !== $this->update_user_meta( $user_id, self::CREDENTIAL_OPTION_KEY, $data );
60 }
61
62 /**
63 * Get one credential by credential ID.
64 *
65 * @param string $public_key_credential_id The public key credential ID.
66 *
67 * @return PublicKeyCredentialSource|null
68 */
69 public function findOneByCredentialId( string $public_key_credential_id ): ?PublicKeyCredentialSource {
70 $username = defender_get_data_from_request( 'username', 'p' );
71 if ( isset( $username ) ) {
72 $user = get_user_by( 'login', $username );
73
74 if ( is_object( $user ) ) {
75 $user_id = $user->ID;
76 } else {
77 $user_id = 0;
78 }
79 } else {
80 $user_id = get_current_user_id();
81 }
82 $data = $this->getCredentials( $user_id );
83 if ( isset( $data[ base64_encode( $public_key_credential_id ) ]['credential_source'] ) ) { // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
84 return PublicKeyCredentialSource::createFromArray( $data[ base64_encode( $public_key_credential_id ) ]['credential_source'] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
85 }
86
87 return null;
88 }
89
90 /**
91 * Get all credentials of a user
92 *
93 * @param PublicKeyCredentialUserEntity $public_key_credential_user_entity The user entity.
94 *
95 * @return array
96 */
97 public function findAllForUserEntity( PublicKeyCredentialUserEntity $public_key_credential_user_entity ): array {
98 $credentials = array();
99 $username = $public_key_credential_user_entity->getName();
100 $user = get_user_by( 'login', $username );
101
102 if ( is_object( $user ) ) {
103 $credentials = $this->findAllForUserByType( $user->ID );
104 }
105
106 return $credentials;
107 }
108
109 /**
110 * Get all credentials of a user by authenticator type.
111 *
112 * @param int $user_id The user ID.
113 * @param null|string $type The type of authenticator.
114 *
115 * @return array
116 * @since 3.1.0
117 */
118 public function findAllForUserByType( int $user_id, $type = null ): array {
119 $sources = array();
120 $user_data = $this->getCredentials( $user_id );
121
122 if ( is_array( $user_data ) ) {
123 foreach ( $user_data as $data ) {
124 if (
125 ! in_array( $type, array( null, '' ), true )
126 && isset( $data['authenticator_type'] )
127 && '' !== $data['authenticator_type']
128 && $type !== $data['authenticator_type']
129 ) {
130 continue;
131 }
132
133 if ( isset( $data['credential_source'] ) ) {
134 $sources[] = PublicKeyCredentialSource::createFromArray( $data['credential_source'] );
135 }
136 }
137 }
138
139 return $sources;
140 }
141
142 /**
143 * Store credential into database.
144 *
145 * @param PublicKeyCredentialSource $public_key_credential_source The credential source to store.
146 *
147 * @return void
148 */
149 public function saveCredentialSource( PublicKeyCredentialSource $public_key_credential_source ): void {
150 $user_id = get_current_user_id();
151 $data = $this->getCredentials( $user_id );
152 $key = base64_encode( $public_key_credential_source->getPublicKeyCredentialId() ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
153
154 if ( ! isset( $data[ $key ] ) ) {
155 $data[ $key ] = array(
156 'label' => defender_get_data_from_request( 'name', 'p' ) ?? '',
157 'added' => time(),
158 'authenticator_type' => defender_get_data_from_request( 'type', 'p' ) ?? '',
159 'user' => $public_key_credential_source->getUserHandle(),
160 'credential_source' => $public_key_credential_source,
161 );
162 } else {
163 $data[ $key ]['credential_source'] = $public_key_credential_source;
164 }
165
166 $this->setCredentials( $user_id, $data );
167 }
168
169 /**
170 * Get userHandle mismatch list.
171 *
172 * @param int $user_id The user ID.
173 *
174 * @return array
175 * @since 3.4.0
176 */
177 public function getUserHandleMatchFailed( int $user_id ): array {
178 $meta_key = $this->option_prefix . self::USER_HANDLE_MISMATCH_KEY;
179 $meta_val = get_user_meta( $user_id, $meta_key, true );
180
181 return is_array( $meta_val ) ? $meta_val : array();
182 }
183
184 /**
185 * Set userHandle mismatch list.
186 *
187 * @param int $user_id The user ID.
188 * @param array $meta_val The mismatches to set.
189 *
190 * @return void
191 * @since 3.4.0
192 */
193 public function setUserHandleMatchFailed( int $user_id, array $meta_val ): void {
194 $meta_key = $this->option_prefix . self::USER_HANDLE_MISMATCH_KEY;
195 update_user_meta( $user_id, $meta_key, $meta_val );
196 }
197
198 /**
199 * Add authenticators to userHandle mismatch list.
200 *
201 * @param WP_User $user The user object.
202 * @param array $data The data to add.
203 *
204 * @return void
205 * @since 3.4.0
206 */
207 public function addUserHandleMatchFailed( $user, $data ): void {
208 if ( isset( $user->ID ) && $user->ID > 0 && '' !== $data['rawId'] ) {
209 $meta_val = $this->getUserHandleMatchFailed( $user->ID );
210 $meta_val['show_notice'] = $meta_val['show_notice'] ?? true;
211
212 if ( ! isset( $meta_val['authenticators'] ) || array() === $meta_val['authenticators'] || ! in_array( $data['rawId'], $meta_val['authenticators'], true ) ) {
213 $meta_val['authenticators'][] = $data['rawId'];
214 }
215
216 $this->setUserHandleMatchFailed( $user->ID, $meta_val );
217 }
218 }
219
220 /**
221 * Remove authenticator from userHandle mismatch list.
222 *
223 * @param int $user_id The user ID.
224 * @param string $auth_id The authenticator ID to remove.
225 *
226 * @return void
227 * @since 3.4.0
228 */
229 public function removeUserHandleMatchFailed( int $user_id, string $auth_id ): void {
230 if ( '' !== $auth_id ) {
231 $meta_val = $this->getUserHandleMatchFailed( $user_id );
232
233 if ( isset( $meta_val['authenticators'] ) && is_array( $meta_val['authenticators'] ) && array() !== $meta_val['authenticators'] ) {
234 $pos = array_search( $auth_id, $meta_val['authenticators'], true );
235
236 if ( false !== $pos ) {
237 array_splice( $meta_val['authenticators'], $pos, 1 );
238 $this->setUserHandleMatchFailed( $user_id, $meta_val );
239 }
240 }
241 }
242 }
243
244 /**
245 * Disable userHandle mismatch notice.
246 *
247 * @param int $user_id The user ID.
248 *
249 * @return void
250 * @since 3.4.0
251 */
252 public function disableUserHandleMatchFailedNotice( int $user_id ): void {
253 $meta_val = $this->getUserHandleMatchFailed( $user_id );
254 $meta_val['show_notice'] = false;
255
256 $this->setUserHandleMatchFailed( $user_id, $meta_val );
257 }
258 }
259