feat: add registration control setting, invites (WIP)

Signed-off-by: Miguel Nogueira <me@nogueira.codes>
This commit is contained in:
2025-08-06 13:35:37 +01:00
parent 2062cd247e
commit 22cffaffca
8 changed files with 134 additions and 80 deletions

View File

@@ -55,6 +55,9 @@ class OptionsController extends Controller
'requiresPMC' => Options::getOption('requireGameLicense'), 'requiresPMC' => Options::getOption('requireGameLicense'),
'enforce2fa' => Options::getOption('force2fa'), 'enforce2fa' => Options::getOption('force2fa'),
], ],
'features' => [
'enableRegistrations' => Options::getOption('enable_registrations')
],
'currentGame' => Options::getOption('currentGame'), 'currentGame' => Options::getOption('currentGame'),
]); ]);
} }

View File

@@ -21,6 +21,7 @@ class SecuritySettingsController extends Controller
'pwExpiry' => $request->pwExpiry, 'pwExpiry' => $request->pwExpiry,
'enforce2fa' => $request->enforce2fa, 'enforce2fa' => $request->enforce2fa,
'requirePMC' => $request->requirePMC, 'requirePMC' => $request->requirePMC,
'enable_registrations' => $request->input('enable_registrations')
]); ]);
return redirect() return redirect()

View File

@@ -40,6 +40,7 @@ class SecuritySettingsService
Options::changeOption('password_expiry', $options['pwExpiry']); Options::changeOption('password_expiry', $options['pwExpiry']);
Options::changeOption('force2fa', $options['enforce2fa']); Options::changeOption('force2fa', $options['enforce2fa']);
Options::changeOption('requireGameLicense', $options['requirePMC']); Options::changeOption('requireGameLicense', $options['requirePMC']);
Options::changeOption('enable_registrations', $options['enable_registrations']);
return true; return true;
} }

View File

@@ -54,5 +54,6 @@ class DefaultOptionsSeeder extends Seeder
Options::setOption('enable_slack_notifications', true, 'Enable slack notifications', 'notifications'); Options::setOption('enable_slack_notifications', true, 'Enable slack notifications', 'notifications');
Options::setOption('enable_email_notifications', true, 'Enable e-mail notifications', 'notifications'); Options::setOption('enable_email_notifications', true, 'Enable e-mail notifications', 'notifications');
Options::setOption('enable_registrations', true, 'Enable sign-uos', 'app_features');
} }
} }

View File

@@ -13,6 +13,8 @@
<div class="brand-wrapper"> <div class="brand-wrapper">
<img src="{{ asset(config('adminlte.logo_img_xl')) }}" alt="logo" class="logo rounded mr-2"> <img src="{{ asset(config('adminlte.logo_img_xl')) }}" alt="logo" class="logo rounded mr-2">
</div> <!-- main content start --> </div> <!-- main content start -->
@if(\App\Facades\Options::getOption('enable_registrations') == true)
<p class="login-card-description">{{__('Sign up for an account')}}</p> <p class="login-card-description">{{__('Sign up for an account')}}</p>
@if(\App\Facades\Options::getOption('pw_security_policy') !== 'off') @if(\App\Facades\Options::getOption('pw_security_policy') !== 'off')
@@ -104,6 +106,40 @@
dateFormat: "Y-m-d", dateFormat: "Y-m-d",
}); });
</script> </script>
@else
<div class="alert alert-danger">
<b><i class="fa fa-exclamation-triangle"></i> {{ __('Sorry, but new signups are currently closed.') }}</b>
<p>{{ __('Due to an internal policy, new accounts cannot be created, and so you will not be able to sign up for an account. If you already have an account with us, you can still sign-in as you usually would.') }}</p>
</div>
<div class="alert alert-warning">
<b><i class="fas fa-question-circle"></i> {{ __('I\'m trying to sign up so I can apply for a vacancy here. What does this restriction mean for me?') }}</b>
<p>{{ __('Effectively, this means that you will not be able to apply for any of our vacancies unless you already have an account. However, if you\'ve received an invitation to create an account or apply for a vacancy, you still can, but you must use the sign-up invitation link sent to your email address.') }}</p>
<p>{!! __('Alternatively, if you don\'t have an invitation, feel free to :requestInviteLink.', ['requestInviteLink' => '<a href="#" id="toggleInviteRequestForm">' . __('request an invite') . '</a>']) !!}</p>
</div>
<div id="inviteRequestFormContainer" style="display:none;">
<form action="{{ route('invitations.request') }}" method="POST" id="inviteRequestForm">
@csrf
<div class="form-group">
<label for="inviteEmail" class="sr-only">{{ __('Email address') }}</label>
<input type="email" name="email" id="inviteEmail" class="form-control" placeholder="{{ __('Enter your email address') }}" required>
</div>
<input type="submit" class="btn btn-primary btn-block" value="{{ __('Request Invite') }}">
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
var toggleLink = document.getElementById('toggleInviteRequestForm');
var formContainer = document.getElementById('inviteRequestFormContainer');
toggleLink.addEventListener('click', function(e) {
e.preventDefault();
formContainer.style.display = (formContainer.style.display === 'none') ? 'block' : 'none';
});
});
</script>
@endif
<p class="login-card-footer-text">{{__('Have an account with us?')}} <a href="{{ route('login') }}" class="text-reset">{{__('Sign in here')}}</a></p> <p class="login-card-footer-text">{{__('Have an account with us?')}} <a href="{{ route('login') }}" class="text-reset">{{__('Sign in here')}}</a></p>
<nav class="login-card-footer-nav"> <nav class="login-card-footer-nav">

View File

@@ -9,7 +9,7 @@
<link rel="stylesheet" href="https://cdn.materialdesignicons.com/4.8.95/css/materialdesignicons.min.css"> <link rel="stylesheet" href="https://cdn.materialdesignicons.com/4.8.95/css/materialdesignicons.min.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" />
<script src="https://kit.fontawesome.com/2d0b1aecfa.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/2f2450d84f.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="/css/login.css"> <link rel="stylesheet" href="/css/login.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script> <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>

View File

@@ -200,6 +200,14 @@
<p class="text-muted text-sm"><i class="fas fa-info-circle"></i> {{ __('Choose a game in the section below, if applicable.') }}</p> <p class="text-muted text-sm"><i class="fas fa-info-circle"></i> {{ __('Choose a game in the section below, if applicable.') }}</p>
</div> </div>
<div class="form-group form-check">
<input type="hidden" name="enable_registrations" value="0">
<input type="checkbox" name="enable_registrations" value="1" id="enable_registrations" class="form-check-input" {{ $features['enableRegistrations'] == true ? 'checked' : '' }}>
<label for="enable_registrations">{{ __('Allow people to make new accounts?') }}</label>
<p class="text-muted text-sm"><i class="fas fa-info-circle"></i> {{ __('Unchecking this box will stop people from signing up to :appName and disable the associated form. However, you will still be able to invite users and create new accounts from the administrative UI if you so choose.', ['appName' => config('app.name')]) }}</p>
</div>
</form> </form>
</div> </div>

View File

@@ -29,6 +29,7 @@ use App\Http\Controllers\DashboardController;
use App\Http\Controllers\DevToolsController; use App\Http\Controllers\DevToolsController;
use App\Http\Controllers\FormController; use App\Http\Controllers\FormController;
use App\Http\Controllers\HomeController; use App\Http\Controllers\HomeController;
use App\Http\Controllers\InvitationController;
use App\Http\Controllers\OptionsController; use App\Http\Controllers\OptionsController;
use App\Http\Controllers\ProfileController; use App\Http\Controllers\ProfileController;
use App\Http\Controllers\SecuritySettingsController; use App\Http\Controllers\SecuritySettingsController;
@@ -57,6 +58,9 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['lo
'verify' => true, 'verify' => true,
]); ]);
Route::post('/invitations/request', [InvitationController::class, 'requestInvite'])
->name('invitations.request');
Route::post('/twofa/authenticate', [TwofaController::class, 'verify2FA']) Route::post('/twofa/authenticate', [TwofaController::class, 'verify2FA'])
->name('verify2FA'); ->name('verify2FA');