feat: add configurable role for Mahak-created users
Build plugin release / release (push) Successful in 5s

This commit is contained in:
2026-08-24 10:39:32 +03:30
parent e7d718cd0e
commit 5f422ac0e4
6 changed files with 131 additions and 19 deletions
+4 -1
View File
@@ -9,6 +9,7 @@ Authenticate WordPress user via json web token from Laravel Application.
- Admin settings page - Admin settings page
- Custom invalid token page - Custom invalid token page
- Optional debug mode with full exception details and stack traces - Optional debug mode with full exception details and stack traces
- Configurable role for users created through Mahak authentication
Debug mode is disabled by default. An administrator can enable it from **Settings > Mahak Authentication** while troubleshooting. The debug page includes server clock/timezone data, safe JWT timing claims, and a Copy Markdown report. JWT contents, secrets, request parameters, and stack-trace arguments are omitted. Because diagnostic pages still expose server paths and other technical details to visitors of the login URL, disable it again after diagnosing the problem. Debug mode is disabled by default. An administrator can enable it from **Settings > Mahak Authentication** while troubleshooting. The debug page includes server clock/timezone data, safe JWT timing claims, and a Copy Markdown report. JWT contents, secrets, request parameters, and stack-trace arguments are omitted. Because diagnostic pages still expose server paths and other technical details to visitors of the login URL, disable it again after diagnosing the problem.
@@ -16,7 +17,9 @@ Debug mode is disabled by default. An administrator can enable it from **Setting
Administrators can check for and install published releases from **Settings > Mahak Authentication**. Updates also appear in WordPress's standard **Plugins** and **Updates** screens. Administrators can check for and install published releases from **Settings > Mahak Authentication**. Updates also appear in WordPress's standard **Plugins** and **Updates** screens.
To publish an update, change the plugin `Version` header and `MAHAK_AUTHENTICATE_BRIDGE_VERSION` constant to the same version, commit the change, and push a matching tag such as `v2.3.0` to Mahgit. The Gitea Actions workflow builds and attaches the ZIP required by the WordPress updater. The **New user role** setting defaults to WordPress Default, using the site's current global new-user role without changing it. Administrators can select a different role only for accounts created by this plugin; existing users keep their current roles.
To publish an update, change the plugin `Version` header and `MAHAK_AUTHENTICATE_BRIDGE_VERSION` constant to the same version, commit the change, and push a matching tag such as `v2.4.0` to Mahgit. The Gitea Actions workflow builds and attaches the ZIP required by the WordPress updater.
## Usage ## Usage
+2 -2
View File
@@ -4,7 +4,7 @@
* Plugin Name: Mahak Authenticate Bridge * Plugin Name: Mahak Authenticate Bridge
* Plugin URI: https://mahgit.ir/dabestaniha/mahak-authenticate-bridge * Plugin URI: https://mahgit.ir/dabestaniha/mahak-authenticate-bridge
* Description: Authenticate WordPress user via json web token from Mahakiha Application. * Description: Authenticate WordPress user via json web token from Mahakiha Application.
* Version: 2.2.3 * Version: 2.3.1
* Requires PHP: 7.4 * Requires PHP: 7.4
* Update URI: https://mahgit.ir/dabestaniha/mahak-authenticate-bridge * Update URI: https://mahgit.ir/dabestaniha/mahak-authenticate-bridge
* Author: Dabestaniha * Author: Dabestaniha
@@ -18,7 +18,7 @@ use Dabestaniha\AuthenticateBridge\App\Http\Controllers\SettingsPageMenuControll
use Dabestaniha\AuthenticateBridge\App\Http\Controllers\SettingsPageFormController; use Dabestaniha\AuthenticateBridge\App\Http\Controllers\SettingsPageFormController;
use Dabestaniha\AuthenticateBridge\App\Support\PluginUpdater; use Dabestaniha\AuthenticateBridge\App\Support\PluginUpdater;
define('MAHAK_AUTHENTICATE_BRIDGE_VERSION', '2.2.3'); define('MAHAK_AUTHENTICATE_BRIDGE_VERSION', '2.3.1');
define('MAHAK_AUTHENTICATE_BRIDGE_FILE', __FILE__); define('MAHAK_AUTHENTICATE_BRIDGE_FILE', __FILE__);
require_once __DIR__.'/src/Autoloader.php'; require_once __DIR__.'/src/Autoloader.php';
@@ -64,7 +64,20 @@ class AuthenticateController extends InvocableController
$username .= '_'.wp_generate_password(4, false); $username .= '_'.wp_generate_password(4, false);
} }
$user_id = wp_create_user($username, $random_password, $email); $configured_role = sanitize_key((string) get_configured_option('new-user-role', 'wordpress_default'));
if ($configured_role === 'wordpress_default') {
$configured_role = sanitize_key((string) get_option('default_role', 'subscriber'));
}
$role = get_role($configured_role) === null ? 'subscriber' : $configured_role;
$user_id = wp_insert_user([
'user_login' => $username,
'user_pass' => $random_password,
'user_email' => $email,
'display_name' => $name,
'role' => $role,
]);
if (is_wp_error($user_id)) { if (is_wp_error($user_id)) {
throw new RuntimeException( throw new RuntimeException(
@@ -72,16 +85,8 @@ class AuthenticateController extends InvocableController
); );
} }
$updated_user_id = wp_update_user([ // Enforce the configured role after user_register callbacks from other plugins have completed.
'ID' => $user_id, (new \WP_User($user_id))->set_role($role);
'display_name' => $name,
]);
if (is_wp_error($updated_user_id)) {
throw new RuntimeException(
'WordPress could not update the user: '.$updated_user_id->get_error_message()
);
}
return $user_id; return $user_id;
} }
@@ -14,6 +14,9 @@ class SettingsPageFormController extends InvocableController
return (int) filter_var($value, FILTER_VALIDATE_BOOLEAN); return (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
}; };
$args['default'] = 0; $args['default'] = 0;
} elseif ($configured === 'new-user-role') {
$args['sanitize_callback'] = fn ($value): string => $this->sanitize_new_user_role($value);
$args['default'] = 'wordpress_default';
} }
register_setting(config('mahak.settings-group'), $options_key, $args); register_setting(config('mahak.settings-group'), $options_key, $args);
@@ -58,6 +61,14 @@ class SettingsPageFormController extends InvocableController
config('mahak.section-id') config('mahak.section-id')
); );
add_settings_field(
config('mahak.options.new-user-role'),
config('mahak.translations.new-user-role'),
fn () => $this->new_user_role(),
config('mahak.settings-page'),
config('mahak.section-id')
);
add_settings_field( add_settings_field(
config('mahak.options.debug-mode'), config('mahak.options.debug-mode'),
config('mahak.translations.debug-mode'), config('mahak.translations.debug-mode'),
@@ -130,4 +141,54 @@ class SettingsPageFormController extends InvocableController
echo "<label><input type='checkbox' name='".esc_attr($name)."' value='1' ".checked($enabled, true, false).' /> '; echo "<label><input type='checkbox' name='".esc_attr($name)."' value='1' ".checked($enabled, true, false).' /> ';
echo esc_html(config('mahak.translations.debug-mode-description')).'</label>'; echo esc_html(config('mahak.translations.debug-mode-description')).'</label>';
} }
public function new_user_role(): void
{
$name = config('mahak.options.new-user-role');
$value = sanitize_key((string) get_option($name, 'wordpress_default'));
$roles = wp_roles()->get_names();
if ($value !== 'wordpress_default' && !isset($roles[$value])) {
$value = 'wordpress_default';
}
echo "<select name='".esc_attr($name)."'>";
$defaultRole = sanitize_key((string) get_option('default_role', 'subscriber'));
$defaultRoleLabel = $roles[$defaultRole] ?? $defaultRole;
$translatedDefaultRole = function_exists('translate_user_role')
? translate_user_role($defaultRoleLabel)
: $defaultRoleLabel;
echo "<option value='wordpress_default' ".selected($value, 'wordpress_default', false).'>';
echo esc_html(config('mahak.translations.wordpress-default-role').' — '.$translatedDefaultRole).'</option>';
foreach ($roles as $role => $label) {
$translatedLabel = function_exists('translate_user_role') ? translate_user_role($label) : $label;
echo "<option value='".esc_attr($role)."' ".selected($value, $role, false).'>';
echo esc_html($translatedLabel).'</option>';
}
echo '</select>';
echo '<p class="description">'.esc_html(config('mahak.translations.new-user-role-description')).'</p>';
}
public function sanitize_new_user_role($value): string
{
$role = sanitize_key((string) $value);
if ($role === 'wordpress_default') {
return $role;
}
if (get_role($role) !== null) {
return $role;
}
add_settings_error(
config('mahak.options.new-user-role'),
'mahak_invalid_new_user_role',
'نقش انتخاب‌شده معتبر نیست. نقش پیش‌فرض وردپرس استفاده شد.'
);
return 'wordpress_default';
}
} }
+4
View File
@@ -11,6 +11,7 @@ return [
'user-identifier' => 'mahak_user_identifier', 'user-identifier' => 'mahak_user_identifier',
'login-route' => 'mahak_login_route', 'login-route' => 'mahak_login_route',
'after-login-route' => 'mahak_after_login_route', 'after-login-route' => 'mahak_after_login_route',
'new-user-role' => 'mahak_new_user_role',
'debug-mode' => 'mahak_debug_mode', 'debug-mode' => 'mahak_debug_mode',
], ],
@@ -38,6 +39,9 @@ return [
'user-identifier' => 'فیلد شناسایی کاربر', 'user-identifier' => 'فیلد شناسایی کاربر',
'login-route' => 'مسیر لاگین ماهک', 'login-route' => 'مسیر لاگین ماهک',
'after-login-route' => 'مسیر بعد از لاگین', 'after-login-route' => 'مسیر بعد از لاگین',
'new-user-role' => 'نقش کاربر جدید',
'wordpress-default-role' => 'پیش‌فرض وردپرس',
'new-user-role-description' => 'در حالت پیش‌فرض وردپرس، نقش عمومی کاربران جدید استفاده می‌شود. نقش انتخابی دیگر فقط به کاربران ساخته‌شده توسط این افزونه اختصاص می‌یابد.',
'debug-mode' => 'حالت اشکال‌زدایی', 'debug-mode' => 'حالت اشکال‌زدایی',
'debug-mode-description' => 'در صورت بروز خطا، جزئیات کامل فنی در صفحه نمایش داده شود. این گزینه را فقط هنگام عیب‌یابی فعال کنید.', 'debug-mode-description' => 'در صورت بروز خطا، جزئیات کامل فنی در صفحه نمایش داده شود. این گزینه را فقط هنگام عیب‌یابی فعال کنید.',
+44 -5
View File
@@ -7,6 +7,7 @@ const TEST_PLUGIN = 'mahak-authenticate-bridge/dabestaniha-authenticate-bridge.p
const TEST_SECRET = 'integration-test-secret'; const TEST_SECRET = 'integration-test-secret';
const TEST_EMAIL = 'mahak-auth-user@example.test'; const TEST_EMAIL = 'mahak-auth-user@example.test';
const TEST_NAME = 'Mahak Auth User'; const TEST_NAME = 'Mahak Auth User';
const TEST_ROLE_EMAIL = 'mahak-role-user@example.test';
function fail(string $message): void function fail(string $message): void
{ {
@@ -86,6 +87,8 @@ function activate_and_configure_plugin(): void
update_option('mahak_user_identifier', 'email'); update_option('mahak_user_identifier', 'email');
update_option('mahak_login_route', 'mahak/login'); update_option('mahak_login_route', 'mahak/login');
update_option('mahak_after_login_route', 'wp-admin/profile.php'); update_option('mahak_after_login_route', 'wp-admin/profile.php');
update_option('default_role', 'subscriber');
update_option('mahak_new_user_role', 'wordpress_default');
pass('Plugin activated and configured'); pass('Plugin activated and configured');
} }
@@ -213,6 +216,10 @@ function test_valid_authentication(): void
assert_true($user !== false, 'Authenticated user was not created.'); assert_true($user !== false, 'Authenticated user was not created.');
assert_true($user->display_name === TEST_NAME, 'Authenticated user display name was not saved.'); assert_true($user->display_name === TEST_NAME, 'Authenticated user display name was not saved.');
assert_true(
$user->roles === ['subscriber'],
'New user did not receive the plugin-configured Subscriber role.'
);
$cookieHeader = cookie_header_from_response($response['headers']); $cookieHeader = cookie_header_from_response($response['headers']);
$profileResponse = http_request(TEST_SITE_URL.'/wp-admin/profile.php', $cookieHeader); $profileResponse = http_request(TEST_SITE_URL.'/wp-admin/profile.php', $cookieHeader);
@@ -222,7 +229,38 @@ function test_valid_authentication(): void
'Issued cookies did not authenticate a follow-up WordPress admin request.' 'Issued cookies did not authenticate a follow-up WordPress admin request.'
); );
pass('Valid JWT creates/logs in user and issued cookies authenticate follow-up request'); pass('Valid JWT creates a Subscriber and issued cookies authenticate follow-up requests');
}
function test_configured_new_user_role_is_enforced(): void
{
$smsRoleOverride = function (int $userId): void {
(new WP_User($userId))->set_role('administrator');
};
add_action('user_register', $smsRoleOverride);
$controller = new Dabestaniha\AuthenticateBridge\App\Http\Controllers\AuthenticateController();
$userId = $controller->find_or_create_user('email', TEST_ROLE_EMAIL, 'Role Test User');
remove_action('user_register', $smsRoleOverride);
$user = get_userdata($userId);
assert_true(
$user !== false && $user->roles === ['subscriber'],
'Configured role was not enforced after another plugin changed the role during user_register.'
);
$user->set_role('author');
$existingUserId = $controller->find_or_create_user('email', TEST_ROLE_EMAIL, 'Role Test User');
$existingUser = get_userdata($existingUserId);
assert_true($existingUserId === $userId, 'Existing user was not reused.');
assert_true(
$existingUser !== false && $existingUser->roles === ['author'],
'Existing user role was changed during authentication.'
);
pass('Configured role overrides creation hooks without changing existing users');
} }
function test_invalid_authentication(): void function test_invalid_authentication(): void
@@ -310,7 +348,7 @@ function test_debug_error_details(): void
function test_plugin_update_discovery(): void function test_plugin_update_discovery(): void
{ {
$packageUrl = 'https://mahgit.ir/dabestaniha/mahak-authenticate-bridge/releases/download/v2.3.0/mahak-authenticate-bridge.zip'; $packageUrl = 'https://mahgit.ir/dabestaniha/mahak-authenticate-bridge/releases/download/v2.4.0/mahak-authenticate-bridge.zip';
$mockRelease = function ($response, array $request, string $url) use ($packageUrl) { $mockRelease = function ($response, array $request, string $url) use ($packageUrl) {
if ($url !== 'https://mahgit.ir/api/v1/repos/dabestaniha/mahak-authenticate-bridge/releases/latest') { if ($url !== 'https://mahgit.ir/api/v1/repos/dabestaniha/mahak-authenticate-bridge/releases/latest') {
return $response; return $response;
@@ -319,8 +357,8 @@ function test_plugin_update_discovery(): void
return [ return [
'headers' => [], 'headers' => [],
'body' => json_encode([ 'body' => json_encode([
'tag_name' => 'v2.3.0', 'tag_name' => 'v2.4.0',
'html_url' => 'https://mahgit.ir/dabestaniha/mahak-authenticate-bridge/releases/tag/v2.3.0', 'html_url' => 'https://mahgit.ir/dabestaniha/mahak-authenticate-bridge/releases/tag/v2.4.0',
'body' => 'Test release', 'body' => 'Test release',
'draft' => false, 'draft' => false,
'prerelease' => false, 'prerelease' => false,
@@ -346,7 +384,7 @@ function test_plugin_update_discovery(): void
remove_filter('pre_http_request', $mockRelease, 10); remove_filter('pre_http_request', $mockRelease, 10);
assert_true(isset($updates->response[TEST_PLUGIN]), 'A newer Mahgit release was not offered as a WordPress update.'); assert_true(isset($updates->response[TEST_PLUGIN]), 'A newer Mahgit release was not offered as a WordPress update.');
assert_true($updates->response[TEST_PLUGIN]->new_version === '2.3.0', 'The offered plugin version was incorrect.'); assert_true($updates->response[TEST_PLUGIN]->new_version === '2.4.0', 'The offered plugin version was incorrect.');
assert_true($updates->response[TEST_PLUGIN]->package === $packageUrl, 'The release package URL was incorrect.'); assert_true($updates->response[TEST_PLUGIN]->package === $packageUrl, 'The release package URL was incorrect.');
pass('Published Mahgit releases are discovered by the WordPress updater'); pass('Published Mahgit releases are discovered by the WordPress updater');
@@ -357,6 +395,7 @@ install_wordpress();
activate_and_configure_plugin(); activate_and_configure_plugin();
wait_for_http(); wait_for_http();
test_valid_authentication(); test_valid_authentication();
test_configured_new_user_role_is_enforced();
test_invalid_authentication(); test_invalid_authentication();
test_debug_error_details(); test_debug_error_details();
test_plugin_update_discovery(); test_plugin_update_discovery();