PluginProbe
Blocks – Reusable Content, Shortcodes & Site Variables / trunk
Blocks – Reusable Content, Shortcodes & Site Variables vtrunk
26.09.03.15 26.08.31.21 26.08.22.20 26.08.22.22 26.08.22.17 26.08.22.13 26.08.21.19 26.08.07.23 26.07.19.14 26.07.13.21 26.07.13.17 26.07.12.13 026.07.07.21 026.07.05.18 026.06.26.20 026.06.26.21 026.06.08.20 026.05.13.14 026.04.29.10 trunk 026.02.22.22 026.03.16.23 026.04.23.13
blocks / includes / signls / src / Identity.php

Identity.php in Blocks – Reusable Content, Shortcodes & Site Variables trunk, at includes/signls/src/Identity.php

100 lines 2.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Stable installation and rotating device identity.
4 *
5 * @package signls-sdk
6 * @license GPL-3.0-or-later
7 */
8
9 namespace Signls\Sdk\V1;
10
11 defined( 'ABSPATH' ) || exit;
12
13 final class Identity {
14
15 private $state;
16
17 public function __construct( StateStore $state ) {
18 $this->state = $state;
19 }
20
21 public function device_id(): string {
22 $current_origin = $this->origin_fingerprint();
23 $stored_origin = (string) $this->state->get( 'origin_fingerprint', '' );
24 $device_id = (string) $this->state->get( 'device_id', '' );
25
26 if ( ! self::valid_uuid( $device_id ) || ( '' !== $stored_origin && ! hash_equals( $stored_origin, $current_origin ) ) ) {
27 $device_id = self::uuid4();
28 $this->state->set_many(
29 array(
30 'device_id' => $device_id,
31 'origin_fingerprint' => $current_origin,
32 )
33 );
34 $this->clear_credential();
35 } elseif ( '' === $stored_origin ) {
36 $this->state->set( 'origin_fingerprint', $current_origin );
37 }
38
39 return $device_id;
40 }
41
42 public function rotate_device(): string {
43 $device_id = self::uuid4();
44 $this->state->set_many(
45 array(
46 'device_id' => $device_id,
47 'origin_fingerprint' => $this->origin_fingerprint(),
48 )
49 );
50 $this->clear_credential();
51 return $device_id;
52 }
53
54 public function clear_credential(): void {
55 $this->state->delete_keys(
56 array_merge(
57 array(
58 'credential_id',
59 'credential_secret',
60 'credential_key_id',
61 'last_acknowledged_sequence',
62 'last_acknowledged_at',
63 'last_success_at',
64 'last_attempt_started_at',
65 'last_attempt_finished_at',
66 'last_http_status',
67 'retry_attempt',
68 'clock_offset',
69 ),
70 StateStore::pending_keys()
71 )
72 );
73 }
74
75 public function forget_device(): void {
76 $this->state->delete_keys( array( 'device_id', 'origin_fingerprint' ) );
77 $this->clear_credential();
78 }
79
80 private function origin_fingerprint(): string {
81 $host = function_exists( 'home_url' ) ? (string) wp_parse_url( home_url(), PHP_URL_HOST ) : 'local';
82 $salt = function_exists( 'wp_salt' ) ? (string) wp_salt( 'auth' ) : 'signls-test-salt';
83 global $wpdb;
84 $prefix = isset( $wpdb->prefix ) ? (string) $wpdb->prefix : 'wp_';
85 return hash_hmac( 'sha256', strtolower( $host ) . '|' . $prefix, $salt );
86 }
87
88 private static function uuid4(): string {
89 $bytes = random_bytes( 16 );
90 $bytes[6] = chr( ( ord( $bytes[6] ) & 0x0f ) | 0x40 );
91 $bytes[8] = chr( ( ord( $bytes[8] ) & 0x3f ) | 0x80 );
92 $hex = bin2hex( $bytes );
93 return substr( $hex, 0, 8 ) . '-' . substr( $hex, 8, 4 ) . '-' . substr( $hex, 12, 4 ) . '-' . substr( $hex, 16, 4 ) . '-' . substr( $hex, 20 );
94 }
95
96 private static function valid_uuid( string $value ): bool {
97 return 1 === preg_match( '/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/', $value );
98 }
99 }
100