156 lines
5.8 KiB
PHP
156 lines
5.8 KiB
PHP
|
|
<?php
|
||
|
|
/**
|
||
|
|
* Attaching a wallet to an account that does not have one.
|
||
|
|
*
|
||
|
|
* Two kinds of account arrive without an address: the one the installer made
|
||
|
|
* before anybody had signed anything, and any account created by a plugin
|
||
|
|
* that called `wp_insert_user()` directly. Both can sign in with a password —
|
||
|
|
* and then have no way to sign in once passwords are turned off. This screen
|
||
|
|
* is the bridge.
|
||
|
|
*
|
||
|
|
* It demands a signature rather than accepting a typed address. Typing an
|
||
|
|
* address proves nothing: a typo, or a copy-paste of an address belonging to
|
||
|
|
* somebody else, would attach an account to a key its owner cannot use, and
|
||
|
|
* the failure would only show up later at a login screen with no way back.
|
||
|
|
* Signing proves the key exists and is held, right now, by the person at the
|
||
|
|
* keyboard.
|
||
|
|
*
|
||
|
|
* Administrators can still set an address directly from the user editor,
|
||
|
|
* because account recovery has to be possible for someone. That path is in
|
||
|
|
* SP_Identity and is deliberately capability-gated.
|
||
|
|
*
|
||
|
|
* @package SiriusPress
|
||
|
|
*/
|
||
|
|
|
||
|
|
defined( 'ABSPATH' ) || exit;
|
||
|
|
|
||
|
|
final class SPA_Profile {
|
||
|
|
|
||
|
|
public static function hooks() {
|
||
|
|
add_action( 'show_user_profile', array( __CLASS__, 'render' ), 5 );
|
||
|
|
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue' ) );
|
||
|
|
add_action( 'personal_options_update', array( __CLASS__, 'save' ), 5 );
|
||
|
|
add_action( 'admin_notices', array( __CLASS__, 'nag' ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function enqueue( $hook ) {
|
||
|
|
if ( 'profile.php' !== $hook ) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
SPA_Login::enqueue_wallet();
|
||
|
|
wp_enqueue_style(
|
||
|
|
'sirius-press-login',
|
||
|
|
SIRIUS_PRESS_AUTH_URL . 'assets/login.css',
|
||
|
|
array(),
|
||
|
|
SIRIUS_PRESS_AUTH_VERSION
|
||
|
|
);
|
||
|
|
add_action( 'admin_footer', array( 'SPA_Login', 'print_config' ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Warn an administrator who cannot yet sign in the way this site works.
|
||
|
|
*
|
||
|
|
* Shown before they turn passwords off, not after — after is too late.
|
||
|
|
*/
|
||
|
|
public static function nag() {
|
||
|
|
if ( ! is_user_logged_in() || '' !== sirius_press_address() ) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
|
||
|
|
if ( $screen && 'profile' === $screen->id ) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
printf(
|
||
|
|
'<div class="notice notice-warning"><p>%s <a href="%s">%s</a></p></div>',
|
||
|
|
esc_html__( 'This account has no wallet attached, so it can only sign in with a password. If passwords are turned off, it will be locked out.', 'sirius-press' ),
|
||
|
|
esc_url( admin_url( 'profile.php#sirius-wallet-link' ) ),
|
||
|
|
esc_html__( 'Attach a wallet', 'sirius-press' )
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function render( $user ) {
|
||
|
|
$current = SP_Identity::address_of( $user->ID );
|
||
|
|
$nonce = SPA_Challenge::issue();
|
||
|
|
$message = SPA_Challenge::message( $nonce, SPA_Challenge::PURPOSE_LINK );
|
||
|
|
?>
|
||
|
|
<h2 id="sirius-wallet-link"><?php esc_html_e( 'Wallet sign-in', 'sirius-press' ); ?></h2>
|
||
|
|
<table class="form-table" role="presentation">
|
||
|
|
<tr>
|
||
|
|
<th><?php esc_html_e( 'Current wallet', 'sirius-press' ); ?></th>
|
||
|
|
<td>
|
||
|
|
<?php if ( '' !== $current ) : ?>
|
||
|
|
<code><?php echo esc_html( $current ); ?></code>
|
||
|
|
<p class="description"><?php esc_html_e( 'Signing with the key for this address signs you in.', 'sirius-press' ); ?></p>
|
||
|
|
<?php else : ?>
|
||
|
|
<p><strong><?php esc_html_e( 'None yet.', 'sirius-press' ); ?></strong>
|
||
|
|
<?php esc_html_e( 'This account can only sign in with a password until a wallet is attached.', 'sirius-press' ); ?></p>
|
||
|
|
<?php endif; ?>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<th><?php echo '' !== $current ? esc_html__( 'Change wallet', 'sirius-press' ) : esc_html__( 'Attach a wallet', 'sirius-press' ); ?></th>
|
||
|
|
<td>
|
||
|
|
<?php if ( '' !== $current ) : ?>
|
||
|
|
<p class="description" style="margin-bottom:10px">
|
||
|
|
<?php esc_html_e( 'Signing with a different key moves this account to that key. The old one stops working immediately.', 'sirius-press' ); ?>
|
||
|
|
</p>
|
||
|
|
<?php endif; ?>
|
||
|
|
<?php
|
||
|
|
SPA_Login::render_signing_block(
|
||
|
|
$nonce,
|
||
|
|
$message,
|
||
|
|
SPA_Challenge::PURPOSE_LINK,
|
||
|
|
'' !== $current ? __( 'Sign with the new wallet', 'sirius-press' ) : __( 'Sign to attach', 'sirius-press' )
|
||
|
|
);
|
||
|
|
?>
|
||
|
|
<p class="description">
|
||
|
|
<?php esc_html_e( 'Signing fills the box above; saving this page applies it.', 'sirius-press' ); ?>
|
||
|
|
</p>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
</table>
|
||
|
|
<?php
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function save( $user_id ) {
|
||
|
|
// WordPress has already run check_admin_referer( 'update-user_' . $id )
|
||
|
|
// before this action fires.
|
||
|
|
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
||
|
|
if ( empty( $_POST['sirius_signature'] ) || empty( $_POST['sirius_nonce'] ) ) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
$signature = sanitize_text_field( wp_unslash( $_POST['sirius_signature'] ) );
|
||
|
|
$nonce = sanitize_text_field( wp_unslash( $_POST['sirius_nonce'] ) );
|
||
|
|
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||
|
|
|
||
|
|
if ( get_current_user_id() !== (int) $user_id ) {
|
||
|
|
return; // Only the account holder attaches their own key here.
|
||
|
|
}
|
||
|
|
|
||
|
|
$address = SPA_Challenge::verify( $nonce, $signature, SPA_Challenge::PURPOSE_LINK );
|
||
|
|
if ( is_wp_error( $address ) ) {
|
||
|
|
SP_Inbox::add( $user_id, __( 'Wallet not attached', 'sirius-press' ), $address->get_error_message(), 'auth' );
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$previous = SP_Identity::address_of( $user_id );
|
||
|
|
$result = SP_Identity::set_address( $user_id, $address );
|
||
|
|
if ( is_wp_error( $result ) ) {
|
||
|
|
SP_Inbox::add( $user_id, __( 'Wallet not attached', 'sirius-press' ), $result->get_error_message(), 'auth' );
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
SP_Inbox::add(
|
||
|
|
$user_id,
|
||
|
|
__( 'Wallet attached', 'sirius-press' ),
|
||
|
|
sprintf(
|
||
|
|
/* translators: 1: new address, 2: previous address or a note that there was none. */
|
||
|
|
esc_html__( 'This account now signs in with %1$s. Previously: %2$s.', 'sirius-press' ),
|
||
|
|
'<code>' . esc_html( $address ) . '</code>',
|
||
|
|
'' !== $previous ? '<code>' . esc_html( $previous ) . '</code>' : esc_html__( 'no wallet', 'sirius-press' )
|
||
|
|
),
|
||
|
|
'auth'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|