[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/secrets/ -> interface-wp-secrets-provider.php (source)

   1  <?php
   2  /**
   3   * Secrets API: WP_Secrets_Provider interface
   4   *
   5   * @package WordPress
   6   * @subpackage Secrets
   7   * @since 7.2.0
   8   */
   9  
  10  /**
  11   * Interface for a secrets provider.
  12   *
  13   * A provider is responsible for a secret: storing it, protecting it, and returning
  14   * it to a caller. It is the outermost extension point in the Secrets API, and the
  15   * one a hosting platform implements.
  16   *
  17   * WordPress ships WP_Secrets_Libsodium_Provider, which encrypts with libsodium and
  18   * stores ciphertext in the options tables. That provider is the default rather than
  19   * a privileged case: a platform that protects credentials in a key management
  20   * service, a hardware security module, or its own control panel implements this
  21   * same interface.
  22   *
  23   * A provider must be stronger than the default, never weaker. Storing a plaintext
  24   * where the default would have stored ciphertext is what this interface exists to
  25   * prevent. Receiving a value over an authenticated channel and holding it in a
  26   * hardware security module satisfies that requirement.
  27   *
  28   * WordPress cannot verify any of this. A provider is loaded from a drop-in, which is
  29   * fully trusted code: it runs before plugins, and could already read every secret by
  30   * implementing the keyring. get_protection_boundary() and is_writable() are
  31   * declarations rather than enforcement. They exist so that a developer reviewing a
  32   * drop-in can see what it claims, Site Health can report where credentials are
  33   * protected, and a settings screen can determine that a write will be refused before
  34   * an operator enters a credential. They are not a security boundary.
  35   *
  36   * Every implementation shares these contracts:
  37   *
  38   * - Three states, never collapsed. get() returns a WP_Secret, null when the secret
  39   *   does not exist, or WP_Error when it exists but cannot be produced. Reporting an
  40   *   unreachable backend as absent turns an outage into an apparently deleted
  41   *   credential.
  42   * - Fail closed. A provider that cannot answer returns WP_Error. It never
  43   *   substitutes a weaker source, and never returns a partial or placeholder value.
  44   * - No filter on the retrieval path. A provider replaces a component; it is not a
  45   *   hook. Nothing is given the opportunity to observe or alter a value in transit.
  46   * - Never persist a value more weakly than the provider protects it. A provider that
  47   *   fetches over the network must not write the plaintext into the persistent object
  48   *   cache. WP_Secret cannot round-trip a plaintext through wp_cache_set(), and
  49   *   caching the raw value alongside it would undo that. Memoize within the request
  50   *   only.
  51   *
  52   * @since 7.2.0
  53   */
  54  interface WP_Secrets_Provider {
  55  
  56      /**
  57       * Protection happens inside WordPress.
  58       *
  59       * The provider holds ciphertext that WordPress produced, and WordPress holds the
  60       * key material.
  61       *
  62       * @since 7.2.0
  63       * @var string
  64       */
  65      const BOUNDARY_WORDPRESS = 'wordpress';
  66  
  67      /**
  68       * Protection happens outside WordPress.
  69       *
  70       * The provider is itself the encryption boundary, and WordPress is a consumer of a
  71       * credential it does not protect.
  72       *
  73       * @since 7.2.0
  74       * @var string
  75       */
  76      const BOUNDARY_PROVIDER = 'provider';
  77  
  78      /**
  79       * Retrieves a secret.
  80       *
  81       * Returning null means "I am not responsible for this name" as well as "this
  82       * name does not exist". The two are the same answer from a caller's point of
  83       * view, and keeping them distinct would require a provider to enumerate names
  84       * it has never heard of.
  85       *
  86       * @since 7.2.0
  87       *
  88       * @param string $name    The secret's name.
  89       * @param string $version A WP_Secret_Version constant. A provider with no
  90       *                        version history returns null for PREVIOUS, which is
  91       *                        absence rather than an error.
  92       * @param bool   $network Whether this is a network-scope secret.
  93       * @return WP_Secret|null|WP_Error
  94       *
  95       * @phpstan-param WP_Secret_Version::CURRENT|WP_Secret_Version::PREVIOUS $version
  96       */
  97  	public function get( string $name, string $version, bool $network = false );
  98  
  99      /**
 100       * Stores a secret.
 101       *
 102       * A provider whose credentials are managed elsewhere, by a control panel, host
 103       * tooling, or a KMS with its own access policy, returns WP_Error here with code
 104       * WP_SECRETS_ERROR_PROVIDER_READ_ONLY, and reports false from is_writable() so
 105       * that callers can find that out without attempting the write first.
 106       *
 107       * Implementations are responsible for firing the `wp_secret_changed` action once
 108       * the write has succeeded, with `$network` as its last argument. The API does
 109       * not fire it on the provider's behalf, because only the provider knows the
 110       * prior fingerprint without paying for an additional read. An audit hook that
 111       * stopped firing once a host installed a provider would be unreliable in exactly
 112       * the situation it matters most, so WP_Secrets_Provider_Conformance checks it.
 113       * A write that fails fires nothing.
 114       *
 115       * @since 7.2.0
 116       *
 117       * @param string      $name           The secret's name.
 118       * @param string      $value          The plaintext value.
 119       * @param bool        $network        Whether this is a network-scope secret.
 120       * @param bool        $needs_rotation Mark the stored secret as needing rotation.
 121       *                                    Set for values that arrived from somewhere
 122       *                                    less protected than this provider, such as
 123       *                                    an imported option. A provider with nowhere
 124       *                                    to record this may ignore it, but must not
 125       *                                    report it as honored.
 126       * @param string|null $action         Overrides the action reported to
 127       *                                    `wp_secret_changed`; null means the
 128       *                                    provider decides between 'created' and
 129       *                                    'updated'.
 130       * @return true|WP_Error
 131       */
 132  	public function set( string $name, $value, bool $network = false, bool $needs_rotation = false, ?string $action = null );
 133  
 134      /**
 135       * Deletes a secret.
 136       *
 137       * Deleting a secret that does not exist is a success.
 138       *
 139       * Fires `wp_secret_changed` with the 'deleted' action once a secret has been
 140       * deleted, for the reason given on set(). A provider that can tell nothing was
 141       * there to delete fires nothing.
 142       *
 143       * @since 7.2.0
 144       *
 145       * @param string $name    The secret's name.
 146       * @param bool   $network Whether this is a network-scope secret.
 147       * @return true|WP_Error
 148       */
 149  	public function delete( string $name, bool $network = false );
 150  
 151      /**
 152       * Clears a secret's previous version, if this provider keeps one.
 153       *
 154       * A provider with no version history treats this as a successful no-op: there
 155       * is nothing to retire, which is the state the caller asked for.
 156       *
 157       * Fires `wp_secret_changed` with the 'retired' action once a previous version
 158       * has been cleared, for the reason given on set(). Nothing fires when there was
 159       * no previous version to clear.
 160       *
 161       * @since 7.2.0
 162       *
 163       * @param string $name    The secret's name.
 164       * @param bool   $network Whether this is a network-scope secret.
 165       * @return true|WP_Error
 166       */
 167  	public function retire_previous( string $name, bool $network = false );
 168  
 169      /**
 170       * Lists secrets by name and metadata. Never values.
 171       *
 172       * @since 7.2.0
 173       *
 174       * @param string $name_prefix Restrict to names beginning with this prefix, or
 175       *                            '' for all of them.
 176       * @param bool   $network     Whether to list network-scope secrets.
 177       * @return array|WP_Error List of arrays with keys 'name', 'fingerprint',
 178       *                        'created', 'has_previous', 'needs_rotation'.
 179       *
 180       * @phpstan-return list<array{name: string, fingerprint: string, created: int, has_previous: bool, needs_rotation: bool}>|WP_Error
 181       */
 182  	public function list_secrets( string $name_prefix = '', bool $network = false );
 183  
 184      /**
 185       * A short human-readable name for this provider, for Site Health.
 186       *
 187       * Describes the protection, not the vendor's marketing: "AWS KMS
 188       * (alias/wp-secrets)" tells an operator something, "SuperSecure Pro" does not.
 189       * Never key material, never a credential, never a value.
 190       *
 191       * @since 7.2.0
 192       *
 193       * @return string
 194       */
 195  	public function get_label(): string;
 196  
 197      /**
 198       * Where secrets this provider holds are actually protected.
 199       *
 200       * One of the BOUNDARY_* constants. This is what lets Site Health and a future
 201       * admin screen tell an operator whether their credentials are protected by
 202       * WordPress's own libsodium envelope or by something outside it, a question
 203       * they currently have no way to answer, and the one hosts asked to be able to
 204       * answer honestly.
 205       *
 206       * @since 7.2.0
 207       *
 208       * @return string
 209       *
 210       * @phpstan-return self::BOUNDARY_*
 211       */
 212  	public function get_protection_boundary(): string;
 213  
 214      /**
 215       * Whether wp_set_secret() can succeed against this provider.
 216       *
 217       * False for a provider whose credentials are managed by host tooling or a
 218       * control panel. Declared rather than discovered so a settings screen can
 219       * disable its save control before an operator types a credential into a field
 220       * that will only reject it.
 221       *
 222       * @since 7.2.0
 223       *
 224       * @return bool
 225       */
 226  	public function is_writable(): bool;
 227  }


Generated : Wed Oct 14 08:20:33 2026 Cross-referenced by PHPXref