The assessment looked for three things: whether students can reach each other's bookings, whether payment settings can be dodged, and whether the plugin opens a way into the rest of the install. The student-isolation and payment paths held up. These are what did not. - The front-end login form told WordPress not to work out whether the site was secure, so on HTTPS every student's session cookie was issued without the Secure flag. wp_signon() only derives it from is_ssl() when the second argument is left at its default; an explicit false reads like "no preference" and is not. - The update check took whatever download URL the release API returned and handed it to core, which unpacks it over the installed plugin. The package must now be https on git.unsupervised.ca exactly, compared on the parsed host so a lookalike name cannot pass. - Uninstalling dropped 2 of 14 tables and left the Stripe secret and webhook signing key in wp_options. Removal is now a choice made in advance on Access -> Plugin removal: records are kept unless the owner opts in (with a typed confirmation), while credentials and the borrowed core registration settings go every time. - Open registration switches on the site-wide users_can_register and makes Student the default role, arming any other signup form on the site to mint students who could book and be billed immediately. The pending state is now decided once, on user_register, rather than by whichever form created the account. - Cancel and withdraw answered "not yours" differently from "does not exist", which let a signed-in student enumerate the studio's bookings. Both now give the same 404. Co-Authored-By: Claude Opus 5 <[email protected]>
74 lines
2.4 KiB
PHP
74 lines
2.4 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Unsupervised\Schedular\Auth;
|
|
|
|
use Unsupervised\Schedular\Val;
|
|
|
|
class LoginPage {
|
|
|
|
/**
|
|
* Renders the student login shortcode/block output.
|
|
*
|
|
* @param array<int|string, mixed> $atts Block attributes (`bookingPageId`) or
|
|
* shortcode attributes (`booking_page_id`).
|
|
*/
|
|
public function render( array $atts ): string {
|
|
$bookingPageId = Val::int( $atts['bookingPageId'] ?? $atts['booking_page_id'] ?? 0 );
|
|
|
|
if ( is_user_logged_in() ) {
|
|
return sprintf(
|
|
'<p>%s <a href="%s">%s</a>.</p>',
|
|
esc_html__( 'You are already logged in.', 'unsupervised-schedular' ),
|
|
esc_url( $this->bookingUrl( $bookingPageId ) ?? (string) get_permalink() ),
|
|
esc_html__( 'View available lessons', 'unsupervised-schedular' )
|
|
);
|
|
}
|
|
|
|
$error = '';
|
|
$redirect = sanitize_url( $this->bookingUrl( $bookingPageId ) ?? (string) get_permalink() );
|
|
|
|
if ( isset( $_POST['us_login'] ) && check_admin_referer( 'us_student_login' ) ) {
|
|
$credentials = [
|
|
'user_login' => sanitize_user( Val::string( wp_unslash( $_POST['log'] ?? '' ) ) ),
|
|
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- passwords must not be sanitized.
|
|
'user_password' => Val::string( wp_unslash( $_POST['pwd'] ?? '' ) ),
|
|
'remember' => isset( $_POST['rememberme'] ),
|
|
];
|
|
|
|
// The secure-cookie argument is deliberately left at its default. Only
|
|
// the empty string makes wp_signon() work it out from is_ssl(); passing
|
|
// an explicit false skips that and issues the plain, non-Secure auth
|
|
// cookie on an HTTPS site — a session that then leaks over the first
|
|
// http:// request to the domain.
|
|
$user = wp_signon( $credentials );
|
|
|
|
if ( is_wp_error( $user ) ) {
|
|
$error = esc_html__( 'Invalid username or password.', 'unsupervised-schedular' );
|
|
} else {
|
|
wp_safe_redirect( $redirect );
|
|
exit;
|
|
}
|
|
}
|
|
|
|
ob_start();
|
|
include USC_PLUGIN_DIR . 'templates/frontend/login-page.php';
|
|
return (string) ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Permalink of the configured booking page, or null when no page is
|
|
* chosen (or the chosen page no longer exists). Logged-in visitors are
|
|
* linked (and redirected after login) there instead of the current page.
|
|
*/
|
|
public function bookingUrl( int $bookingPageId ): ?string {
|
|
if ( $bookingPageId <= 0 ) {
|
|
return null;
|
|
}
|
|
|
|
$url = get_permalink( $bookingPageId );
|
|
|
|
return is_string( $url ) ? $url : null;
|
|
}
|
|
}
|