Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions core/classes/Middleware/AbstractMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

abstract class AbstractMiddleware
{
public MiddlewareType $type = MiddlewareType::Global;

public array $exemptRoutes = [];
}
52 changes: 52 additions & 0 deletions core/classes/Middleware/MiddlewareHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

use DI\Container;
use Symfony\Component\HttpFoundation\Request;

class MiddlewareHandler extends Instanceable
{
/**
* @var class-string<AbstractMiddleware>[]
*/
private array $middleware = [];

/**
* Register a middleware class.
*
* @param class-string<AbstractMiddleware> $class
*/
public function register(string $class): void
{
$this->middleware[] = $class;
}

/**
* Get all registered middleware classes.
*
* @return class-string<AbstractMiddleware>[]
*/
public function getMiddleware(): array
{
return $this->middleware;
}

public function call(MiddlewareType $type, Container $container)
{
$middlewareClasses = $this->getMiddleware();

foreach ($middlewareClasses as $class) {
$middleware = $container->get($class);
$request = $container->get(Request::class);

foreach ($middleware->exemptRoutes as $exemptedRoute) {
if (str_starts_with($request->get('route'), $exemptedRoute)) {
continue 2; // Skip this middleware if the route is exempted
}
}

if ($middleware->type === $type) {
$container->call([$middleware, 'handle']);
}
}
}
}
7 changes: 7 additions & 0 deletions core/classes/Middleware/MiddlewareType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

enum MiddlewareType
{
case Global;
case Frontend;
}
66 changes: 9 additions & 57 deletions core/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
*/

$container = new \DI\Container();

$container->set(\Symfony\Component\HttpFoundation\Request::class, function () {
return \Symfony\Component\HttpFoundation\Request::createFromGlobals();
});

$container->set(Cache::class, function () {
return new Cache([
'name' => 'nameless',
Expand Down Expand Up @@ -159,6 +164,8 @@
}
}

$container->set(User::class, $user);

// Check if we're in a subdirectory
if (isset($directories)) {
if (empty($directories[0])) {
Expand Down Expand Up @@ -357,30 +364,8 @@
}
}

// Maintenance mode?
if (Settings::get('maintenance') === '1') {
// Enabled
// Admins only beyond this point
if (!$user->isLoggedIn() || !$user->canViewStaffCP()) {
// Maintenance mode
if (isset($_GET['route']) && (
rtrim($_GET['route'], '/') === '/login'
|| rtrim($_GET['route'], '/') === '/forgot_password'
|| str_contains($_GET['route'], '/api/')
|| str_contains($_GET['route'], 'queries')
|| str_contains($_GET['route'], 'oauth/')
|| str_contains($_GET['route'], 'store/listener')
)) {
// Can continue as normal
} else {
require(ROOT_PATH . '/core/includes/maintenance.php');
die;
}
} else {
// Display notice to admin stating maintenance mode is enabled
define('BYPASS_MAINTENANCE', true);
}
}
// Execute middleware events
MiddlewareHandler::getInstance()->call(MiddlewareType::Global, $container);

// Webhooks
$hook_array = [];
Expand Down Expand Up @@ -424,21 +409,6 @@
Debugging::setCanViewDetailedError($user->hasPermission('admincp.errors'));
Debugging::setCanGenerateDebugLink($user->hasPermission('admincp.core.debugging'));

// Ensure a user is not banned
if ($user->data()->isbanned == 1) {
$user->logout();
Session::flash('home_error', $language->get('user', 'you_have_been_banned'));
Redirect::to(URL::build('/'));
}

// Is the IP address banned?
$ip_bans = DB::getInstance()->get('ip_bans', ['ip', $ip])->results();
if (count($ip_bans)) {
$user->logout();
Session::flash('home_error', $language->get('user', 'you_have_been_banned'));
Redirect::to(URL::build('/'));
}

// Update user last IP and last online
if (filter_var($ip, FILTER_VALIDATE_IP)) {
$user->update([
Expand Down Expand Up @@ -488,24 +458,6 @@
}
}

// Does their group have TFA forced?
foreach ($user->getGroups() as $group) {
if ($group->force_tfa) {
$forced = true;
break;
}
}

if (isset($forced) && $forced) {
// Do they have TFA configured?
if (!$user->data()->tfa_enabled && rtrim($_GET['route'], '/') != '/logout') {
if (!str_contains($_SERVER['REQUEST_URI'], 'do=enable_tfa') && !isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
Session::put('force_tfa_alert', $language->get('admin', 'force_tfa_alert'));
Redirect::to(URL::build('/user/settings', 'do=enable_tfa'));
}
}
}

$user_integrations = [];
foreach ($user->getIntegrations() as $integrationUser) {
$user_integrations[$integrationUser->getIntegration()->getName()] = [
Expand Down
43 changes: 4 additions & 39 deletions core/templates/frontend_init.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,6 @@
}
}

// Check if any integrations is required before user can continue
if ($user->isLoggedIn() && defined('PAGE') && PAGE != 'cc_connections' && PAGE != 'oauth' && !(PAGE == 'cc_settings' && $_GET['do'] == 'enable_tfa')) {
foreach (Integrations::getInstance()->getEnabledIntegrations() as $integration) {
if ($integration->data()->required && $integration->allowLinking()) {
$integrationUser = $user->getIntegration($integration->getName());
if ($integrationUser === null || !$integrationUser->isVerified()) {
Session::flash('connections_error', $language->get('user', 'integration_required_to_continue'));
Redirect::to(URL::build('/user/connections'));
}
}
}
}

if (defined('PAGE') && PAGE != 404) {
// Auto unset signin tfa variables if set
if (
Expand All @@ -72,6 +59,10 @@
require(ROOT_PATH . '/custom/templates/DefaultRevamp/template.php');
}

$container->set(TemplateBase::class, $template);

MiddlewareHandler::getInstance()->call(MiddlewareType::Frontend, $container);

// Basic template variables
$template->getEngine()->addVariables([
'CONFIG_PATH' => defined('CONFIG_PATH') ? CONFIG_PATH . '/' : '/',
Expand All @@ -88,32 +79,6 @@
$template->getEngine()->addVariable('OG_IMAGE', rtrim(URL::getSelfURL(), '/') . Output::getClean($og_image));
}

// User related actions
if ($user->isLoggedIn()) {
// Warnings
$warnings = DB::getInstance()->get('infractions', ['punished', $user->data()->id])->results();
if (count($warnings)) {
foreach ($warnings as $warning) {
if ($warning->revoked == 0 && $warning->acknowledged == 0) {
$template->getEngine()->addVariables([
'GLOBAL_WARNING_TITLE' => $language->get('user', 'you_have_received_a_warning'),
'GLOBAL_WARNING_REASON' => Output::getClean($warning->reason),
'GLOBAL_WARNING_ACKNOWLEDGE' => $language->get('user', 'acknowledge'),
'GLOBAL_WARNING_ACKNOWLEDGE_LINK' => URL::build('/user/acknowledge/' . urlencode($warning->id)),
]);
break;
}
}
}

// Does the account need verifying?
// Get default group ID
$cache->setCache('default_group');
$default_group = $cache->fetch('default_group', function () {
return Group::find(1, 'default_group')->id;
});
}

// Page metadata
if (isset($_GET['route']) && $_GET['route'] != '/') {
$route = rtrim($_GET['route'], '/');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

class EnsureUserIntegrationsLinkedMiddleware extends AbstractMiddleware
{
public MiddlewareType $type = MiddlewareType::Frontend;

public array $exemptRoutes = [
'/user/connections',
'/oauth',
'/user/settings',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User should only be able to view /user/settings IF they are viewing enable_tfa

];

public function handle(User $user, Language $language): void
{
if (!$user->isLoggedIn()) {
return;
}

// Check if any integrations is required before user can continue
foreach (Integrations::getInstance()->getEnabledIntegrations() as $integration) {
if ($integration->data()->required && $integration->allowLinking()) {
$integrationUser = $user->getIntegration($integration->getName());
if ($integrationUser === null || !$integrationUser->isVerified()) {
Session::flash('connections_error', $language->get('user', 'integration_required_to_continue'));
Redirect::to(URL::build('/user/connections'));
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

class GlobalWarningsMiddleware extends AbstractMiddleware
{
public MiddlewareType $type = MiddlewareType::Frontend;

public function handle(User $user, Language $language, TemplateBase $template): void
{
$warnings = DB::getInstance()->query('SELECT * FROM nl2_infractions WHERE punished = ? AND revoked = 0 AND acknowledged = 0', [$user->data()->id])->results();
foreach ($warnings as $warning) {
$template->getEngine()->addVariables([
'GLOBAL_WARNING_TITLE' => $language->get('user', 'you_have_received_a_warning'),
'GLOBAL_WARNING_REASON' => Output::getClean($warning->reason),
'GLOBAL_WARNING_ACKNOWLEDGE' => $language->get('user', 'acknowledge'),
'GLOBAL_WARNING_ACKNOWLEDGE_LINK' => URL::build('/user/acknowledge/' . urlencode($warning->id)),
]);
break;
}
}
}
23 changes: 23 additions & 0 deletions modules/Core/classes/Middleware/Global/BannedUserMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* Banned User middleware hook.
* Handles user bans and IP bans enforcement.
*
* @package NamelessMC\Hooks
* @author Aberdeener
* @version 2.3.0
* @license MIT
*/
class BannedUserMiddleware extends AbstractMiddleware
{
public function handle(User $user, Language $language): void
{
if (($user->isLoggedIn() && $user->data()->isbanned) || DB::getInstance()->get('ip_bans', ['ip', HttpUtils::getRemoteAddress()])->exists()) {
$user->logout();

Session::flash('home_error', $language->get('user', 'you_have_been_banned'));
Redirect::to(URL::build('/'));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

/**
* Maintenance Mode middleware hook.
* Redirects non-admin users when maintenance mode is enabled.
*
* @package NamelessMC\Hooks
* @author Aberdeener
* @version 2.3.0
* @license MIT
*/
class MaintenanceModeMiddleware extends AbstractMiddleware
{
public array $exemptRoutes = [
'/maintenance',
'/login',
'/forgot_password',
'/api',
'/queries',
'/oauth',
'/store/listener',
];

public function handle(User $user): void
{
// Check if maintenance mode is enabled
if (!Settings::get('maintenance')) {
return;
}

// Allow admin users to bypass maintenance mode
if ($user->isLoggedIn() && $user->canViewStaffCP()) {
// Display notice to admin stating maintenance mode is enabled
define('BYPASS_MAINTENANCE', true);
return;
}

Redirect::to(URL::build('/maintenance'));
Copy link
Copy Markdown
Member

@partydragen partydragen Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not send users to maintenance page thats just annoying, And alot of users like just to refresh site to see if maintenance is complete

Also makes uses lose any details they might have in the URL

}
}
Loading