Changeset 2737154
- Timestamp:
- 06/03/2022 08:58:14 PM (4 years ago)
- Location:
- virtual-public-square/trunk
- Files:
-
- 2 edited
-
psqr.php (modified) (25 diffs)
-
readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
virtual-public-square/trunk/psqr.php
r2723045 r2737154 7 7 Plugin URI: https://vpsqr.com/ 8 8 Description: Virtual Public Squares operate on identity. Add self-hosted, cryptographically verifiable, decentralized identity to your site and authors. 9 Version: 0.1. 19 Version: 0.1.2 10 10 Author: Virtual Public Square 11 11 Author URI: https://vpsqr.com … … 32 32 use Jose\Component\Signature\Serializer\JWSSerializerManager; 33 33 34 if ( ! function_exists('write_log')) { 35 function write_log ( $log ) { 36 $prefix = '|| Virtual Public Square || '; 37 if ( is_array( $log ) || is_object( $log ) ) { 38 error_log( $prefix . print_r( $log, true ) ); 39 } else { 40 error_log( $prefix . $log ); 41 } 42 } 43 } 44 34 45 if ( !class_exists( 'PSQR' ) ) { 35 46 … … 41 52 'application/json', 42 53 'application/did+json' 54 ]; 55 56 // set required php extensions 57 const REQUIRED_EXT = [ 58 'json', 59 'mbstring', 60 'openssl' 43 61 ]; 44 62 … … 65 83 'status' => 400 66 84 ] 67 ] 85 ], 86 'ext_missing' => [ 87 'code' => 'ext_missing', 88 'message' => 'The required php extensions are missing', 89 'data' => [ 90 'status' => 500 91 ] 92 ], 68 93 ]; 69 94 70 private $available_dids = []; 95 private $ext_missing = array(); 96 private $available_dids = array(); 71 97 private JWSSerializerManager $serializer_manager; 72 98 private JWSVerifier $jws_verifier; 73 99 74 100 function __construct() { 101 // check for missing php extensions 102 $this->ext_missing = $this->check_extensions(); 75 103 76 104 // create necessary directories … … 84 112 new CompactSerializer(), 85 113 ]); 114 115 // add notice on admin page 116 add_action('after_plugin_row', array($this, 'add_ext_warning')); 86 117 87 118 // setup did and api response … … 110 141 } 111 142 143 static function check_extensions(): array { 144 $ext_missing = []; 145 foreach (PSQR::REQUIRED_EXT as $ext) { 146 if (extension_loaded($ext) === false) { 147 write_log('WARNING: missing php extension required for DID validation: ' . $ext); 148 array_push($ext_missing, $ext); 149 } 150 } 151 152 return $ext_missing; 153 } 154 112 155 static function setup_dirs() { 113 156 // determine dir path … … 120 163 $dir_resp = mkdir($author_dir, 0777, true); 121 164 if ($dir_resp === false) { 165 write_log('ERROR: unable to create psqr-identities directory'); 122 166 return false; 123 167 } … … 134 178 135 179 if ($identity === false) { 180 write_log('ERROR: Unable to find DID from provided data'); 181 write_log($data); 136 182 wp_send_json(PSQR::RESPONSES['did_missing'], 404); 137 183 } … … 147 193 148 194 if (isset($body->id) === false || $body->id !== $request_did) { 195 write_log('ERROR: DID mismatch for ' . $request_did); 149 196 wp_send_json([ 150 197 'code' => 'did_mismatch', … … 159 206 $valid_resp = $this->validate_identity($body); 160 207 if ($valid_resp['valid'] === false) { 208 write_log('ERROR: invalid DID ' . $valid_resp['message']); 161 209 wp_send_json([ 162 210 'code' => 'did_invalid', … … 170 218 $response = $this->store_identity('/author/' . $name, $body); 171 219 if ($response === false) { 220 write_log('ERROR: Unable to store identity for ' . $name); 172 221 wp_send_json(PSQR::RESPONSES['did_error'], 400); 173 222 } … … 193 242 194 243 // return empty object if no data found 195 if ($identity_obj === null) {244 if ($identity_obj === NULL) { 196 245 return false; 197 246 } … … 265 314 function validate_update(string $path, string $token): bool 266 315 { 267 $kid ;268 $jws ;316 $kid = NULL; 317 $jws = ''; 269 318 try { 270 319 $jws = $this->serializer_manager->unserialize($token); 271 320 $kid = $jws->getSignatures()[0]->getProtectedHeader()['kid']; 272 321 } catch (\Throwable $th) { 322 write_log('ERROR: Unable to serialize JWS'); 273 323 return false; 274 324 } 275 325 276 326 // get didDoc specified in header 277 $matches ;327 $matches = NULL; 278 328 preg_match('/did:psqr:[^\/]+([^#]+)/', $kid, $matches); 279 329 $kidPath = $matches[1]; … … 281 331 // fail if path from signature doesn't match request path 282 332 if ($path !== $kidPath) { 333 write_log('ERROR: JWS signature path does not match request path'); 283 334 return false; 284 335 } … … 287 338 288 339 if ($didDoc === false) { 289 return false;290 }291 340 write_log('ERROR: Unable to retrieve DID doc for JWS'); 341 return false; 342 } 292 343 293 344 // try to find valid public keys … … 298 349 $k = $keys[$j]; 299 350 if ($k->kid === $kid) { 300 $pubKey = new JWK((array) $k , 0);351 $pubKey = new JWK((array) $k); 301 352 302 353 break; … … 305 356 // return false if no pubKey was found 306 357 if ($pubKey === false) { 358 write_log('ERROR: No pubkey matching ' . $kid . ' found in current DID doc'); 307 359 return false; 308 360 } … … 322 374 // return false if no grant was found or doesn't contain admin 323 375 if ($keyGrant === false || in_array('admin', $keyGrant) === false) { 376 write_log('ERROR: No admin grant found for ' . $kid . ' in current DID doc'); 324 377 return false; 325 378 } … … 333 386 $method = $_SERVER['REQUEST_METHOD']; 334 387 388 error_log('----------------------------------------------------------------------------'); 389 write_log('INFO: Evaluating ' . $method . ' request to path ' . $path); 390 335 391 // retrieve, sanitize, and validate JWS string if present 336 $jws_matches ;392 $jws_matches = array(); 337 393 $raw_input = file_get_contents('php://input'); 338 preg_match('/[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+/', $raw_input, $jws_matches); 339 $jws = empty($jws_matches) ? '' : $jws_matches[0]; 394 395 $jws = ''; 396 $json_object = json_decode($raw_input, false); 397 398 if ($json_object !== NULL) { 399 preg_match('/[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+/', $json_object->token, $jws_matches, PREG_UNMATCHED_AS_NULL); 400 $jws = empty($jws_matches) ? '' : $json_object->token; 401 } 402 403 write_log('INFO: Parsed JWS from response: ' . $jws); 340 404 341 405 $headers = array_change_key_case(array_map('strtolower', getallheaders()), CASE_LOWER); 342 406 $accept_header = $headers['accept']; 407 408 // ensure required php extensions exist for PUTs and DELETEs 409 if ((strtoupper($method) === 'PUT' || strtoupper($method) === 'DELETE') && count($this->ext_missing) !== 0) { 410 write_log('ERROR: there are some required php extensions missing from the current installation: ' . implode(", ", $this->ext_missing)); 411 $error_response = PSQR::RESPONSES['ext_missing']; 412 $error_response['message'] = $error_response['message'] . ': ' . implode(", ", $this->ext_missing); 413 wp_send_json($error_response, 500); 414 } 343 415 344 416 if ($path === '.well-known/psqr') { … … 388 460 function update_did(string $path, string $body) 389 461 { 462 write_log('INFO: updating DID with path ' . $path); 390 463 $signature_valid = $this->validate_update($path, $body); 391 464 392 465 if ($signature_valid === false) { 466 write_log('ERROR: Signature is not valid'); 393 467 wp_send_json(PSQR::RESPONSES['invalid_jws'], 401); 394 468 } … … 400 474 $valid_resp = $this->validate_identity($newDid); 401 475 if ($valid_resp['valid'] === false) { 476 write_log('ERROR: DID from JWS is not valid'); 402 477 wp_send_json([ 403 478 'code' => 'did_invalid', … … 411 486 $response = $this->store_identity($path, $newDid); 412 487 if ($response === false) { 488 write_log('ERROR: there was an issue storing the DID for ' . $path); 413 489 wp_send_json(PSQR::RESPONSES['did_error'], 400); 414 490 } 415 491 492 write_log('INFO: update successful'); 416 493 wp_send_json($newDid, 200); 417 494 } … … 419 496 public function delete_did(string $path, string $body) 420 497 { 498 write_log('INFO: deleting DID with path ' . $path); 421 499 $signature_valid = $this->validate_update($path, $body); 422 500 423 501 if ($signature_valid === false) { 502 write_log('ERROR: Signature is not valid'); 424 503 wp_send_json(PSQR::RESPONSES['invalid_jws'], 401); 425 504 } … … 427 506 $response = $this->delete_identity($path); 428 507 if ($response === false) { 508 write_log('ERROR: there was an error deleting the DID for ' . $path); 429 509 wp_send_json(PSQR::RESPONSES['did_error'], 400); 430 510 } 431 511 512 write_log('INFO: delete successful'); 432 513 wp_send_json([ 433 514 'code' => 'did_deleted', … … 441 522 442 523 return $did; 524 } 525 526 function add_ext_warning(string $plugin_file) { 527 // add notice if necessary 528 if ($plugin_file === 'psqr/psqr.php' && count($this->ext_missing) !== 0) { 529 echo '<div class="notice notice-warning"> 530 <p><strong>Virtual Public Square</strong></p> 531 <p>You are missing some required php extensions to manage DIDs. Please install the following: ' . implode(", ", $this->ext_missing) . '</p> 532 </div>'; 533 } 443 534 } 444 535 -
virtual-public-square/trunk/readme.txt
r2737145 r2737154 4 4 Requires at least: 5.4 5 5 Tested up to: 5.9 6 Requires PHP: 7. 07 Stable tag: 0.1. 16 Requires PHP: 7.4 7 Stable tag: 0.1.2 8 8 License: GPLv2 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 21 21 22 22 The primary use of Virtual Public Square technology is to acquire content from specified publishers without third party interference. This will often appear in the format of a news feed. The first commercial use of Virtual Public Square data is to populate widgets on websites containing curated content from a set of publishers that seek to cross-promote each other to keep readers engaged and maximize audience attention. 23 24 == Required PHP Extensions == 25 26 The following PHP extensions are required to update and delete DIDs with an API call: 27 28 * json 29 * mbstring 30 * openssl 23 31 24 32 == Frequently Asked Questions == … … 74 82 == Changelog == 75 83 84 = 0.1.2 = 85 * Update token format to {token: JWSstring} 86 * Add checks for required php extensions 87 * Add debug logs 88 76 89 = 0.1.1 = 77 * Update, delete DID with http PUT; revised DID document in request body90 * Add ability to update DIDs with PUT request and delete DIDs with DELETE 78 91 79 92 = 0.1.0 = 80 93 * Initial release 81
Note: See TracChangeset
for help on using the changeset viewer.