# jetpack/16.2/src/reprint-export/class-rest-controller.php

Jetpack – WP Security, Backup, Speed, &amp; Growth, version 16.2. 144 lines.

- Page: https://pluginprobe.com/plugins/jetpack/16.2/code/src/reprint-export/class-rest-controller.php
- Raw: https://pluginprobe.com/plugins/jetpack/16.2/raw/src/reprint-export/class-rest-controller.php
- Modified: 2026-09-08T18:39:34+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/jetpack/16.2/code/src/reprint-export/class-rest-controller.php#L10-L20`.

```php
<?php
/**
 * REST controller for Jetpack Reprint export provisioning endpoints.
 *
 * Requires a verified Jetpack user token for a site administrator.
 *
 * @package automattic/jetpack
 */

namespace Automattic\Jetpack\Reprint_Export;

use Automattic\Jetpack\Connection\Rest_Authentication;
use WP_REST_Controller;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Reprint exporter REST controller.
 */
class REST_Controller extends WP_REST_Controller {

	/**
	 * The API namespace.
	 *
	 * @var string
	 */
	protected $namespace = 'jetpack/v4';

	/**
	 * The REST base path.
	 *
	 * @var string
	 */
	protected $rest_base = 'reprint';

	/**
	 * Registers the reprint export routes.
	 */
	public function register_routes() {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/rotate-export-secret',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'rotate_secret' ),
					'permission_callback' => array( $this, 'permission_check' ),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/enable-export',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'enable_export' ),
					'permission_callback' => array( $this, 'permission_check' ),
				),
			)
		);
	}

	/**
	 * Opens the export window without rotating the secret, so a client that
	 * already has one can reopen a window that has closed.
	 *
	 * Registered only where the feature is available, so clients also use a 404
	 * from here to tell whether the site supports export at all.
	 *
	 * @return WP_REST_Response The unix timestamp the window was opened at.
	 */
	public function enable_export() {
		$enabled_at = Reprint_Exporter::open_export_window();

		Reprint_Exporter::record_event(
			'window_opened',
			array( 'user_id' => get_current_user_id() )
		);

		return new WP_REST_Response( array( 'enabled_at' => $enabled_at ), 200 );
	}

	/**
	 * Rotates the shared secret and returns it.
	 *
	 * Uses random_bytes() rather than wp_generate_password(). That helper is for
	 * passwords a person reads and types, and sites can filter it through
	 * `random_password` to enforce their own policy — an extension point we do
	 * not want on a credential. random_bytes() also throws rather than quietly
	 * falling back to a weaker source, which wp_rand() will do.
	 *
	 * @return WP_REST_Response The new secret on success, or a 500 error.
	 */
	public function rotate_secret() {
		$secret = bin2hex( random_bytes( 32 ) );

		if ( ! Reprint_Exporter::store_secret( $secret ) ) {
			return new WP_REST_Response(
				array( 'error' => 'Failed to persist the new secret.' ),
				500
			);
		}

		Reprint_Exporter::record_event(
			'secret_rotated',
			array( 'user_id' => get_current_user_id() )
		);

		return new WP_REST_Response( array( 'secret' => $secret ), 200 );
	}

	/**
	 * Permission callback: a Jetpack-signed request from a site administrator.
	 *
	 * Deliberately a role check, not a capability one. This hands out a secret
	 * that streams the whole database and file tree, and no capability says
	 * that — `manage_options` is the closest, but plugins grant it to shop
	 * managers and the like.
	 *
	 * @return bool
	 */
	public function permission_check() {
		if ( ! Rest_Authentication::is_signed_with_user_token() ) {
			return false;
		}

		$user = wp_get_current_user();
		if ( ! $user || ! $user->exists() ) {
			return false;
		}

		// Network administrator only: the export takes every table and everything
		// under ABSPATH, so a subsite administrator would leave with every other
		// site's users, content and uploads.
		if ( is_multisite() ) {
			return is_super_admin( $user->ID );
		}

		return in_array( 'administrator', $user->roles, true );
	}
}

```
