Upgrade to 3.8.1
This commit is contained in:
@@ -1062,7 +1062,7 @@ class App
|
||||
|
||||
// load the main config options
|
||||
$root = $this->root('config');
|
||||
$options = F::load($root . '/config.php', []);
|
||||
$options = F::load($root . '/config.php', [], allowOutput: false);
|
||||
|
||||
// merge into one clean options array
|
||||
return $this->options = array_replace_recursive(Config::$data, $options);
|
||||
@@ -1080,7 +1080,7 @@ class App
|
||||
$root = $this->root('config');
|
||||
|
||||
// first load `config/env.php` to access its `url` option
|
||||
$envOptions = F::load($root . '/env.php', []);
|
||||
$envOptions = F::load($root . '/env.php', [], allowOutput: false);
|
||||
|
||||
// use the option from the main `config.php`,
|
||||
// but allow the `env.php` to override it
|
||||
|
||||
@@ -163,19 +163,19 @@ trait AppErrors
|
||||
$whoops = $this->whoops();
|
||||
$whoops->clearHandlers();
|
||||
$whoops->pushHandler($handler);
|
||||
$whoops->pushHandler($this->getExceptionHookWhoopsHandler());
|
||||
$whoops->pushHandler($this->getAdditionalWhoopsHandler());
|
||||
$whoops->register(); // will only do something if not already registered
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a callback handler for triggering the `system.exception` hook
|
||||
*
|
||||
* @return \Whoops\Handler\CallbackHandler
|
||||
* Whoops callback handler for additional error handling
|
||||
* (`system.exception` hook and output to error log)
|
||||
*/
|
||||
protected function getExceptionHookWhoopsHandler(): CallbackHandler
|
||||
protected function getAdditionalWhoopsHandler(): CallbackHandler
|
||||
{
|
||||
return new CallbackHandler(function ($exception, $inspector, $run) {
|
||||
$this->trigger('system.exception', compact('exception'));
|
||||
error_log($exception);
|
||||
return Handler::DONE;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -717,7 +717,7 @@ trait AppPlugins
|
||||
$class = str_replace(['.', '-', '_'], '', $name) . 'Page';
|
||||
|
||||
// load the model class
|
||||
F::loadOnce($model);
|
||||
F::loadOnce($model, allowOutput: false);
|
||||
|
||||
if (class_exists($class) === true) {
|
||||
$models[$name] = $class;
|
||||
@@ -908,7 +908,7 @@ trait AppPlugins
|
||||
$styles = $dir . '/index.css';
|
||||
|
||||
if (is_file($entry) === true) {
|
||||
F::loadOnce($entry);
|
||||
F::loadOnce($entry, allowOutput: false);
|
||||
} elseif (is_file($script) === true || is_file($styles) === true) {
|
||||
// if no PHP file is present but an index.js or index.css,
|
||||
// register as anonymous plugin (without actual extensions)
|
||||
|
||||
@@ -120,12 +120,12 @@ trait AppUsers
|
||||
|
||||
if ($allowImpersonation === true && is_string($this->user) === true) {
|
||||
return $this->auth()->impersonate($this->user);
|
||||
} else {
|
||||
try {
|
||||
return $this->auth()->user(null, $allowImpersonation);
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->auth()->user(null, $allowImpersonation);
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,10 +96,7 @@ class Auth
|
||||
*/
|
||||
public function createChallenge(string $email, bool $long = false, string $mode = 'login')
|
||||
{
|
||||
$email = $this->validateEmail($email);
|
||||
|
||||
// rate-limit the number of challenges for DoS/DDoS protection
|
||||
$this->track($email, false);
|
||||
$email = Idn::decodeEmail($email);
|
||||
|
||||
$session = $this->kirby->session([
|
||||
'createMode' => 'cookie',
|
||||
@@ -108,8 +105,29 @@ class Auth
|
||||
|
||||
$timeout = $this->kirby->option('auth.challenge.timeout', 10 * 60);
|
||||
|
||||
$challenge = null;
|
||||
if ($user = $this->kirby->users()->find($email)) {
|
||||
// catch every exception to hide them from attackers
|
||||
// unless auth debugging is enabled
|
||||
try {
|
||||
$this->checkRateLimit($email);
|
||||
|
||||
// rate-limit the number of challenges for DoS/DDoS protection
|
||||
$this->track($email, false);
|
||||
|
||||
// try to find the provided user
|
||||
$user = $this->kirby->users()->find($email);
|
||||
if ($user === null) {
|
||||
$this->kirby->trigger('user.login:failed', compact('email'));
|
||||
|
||||
throw new NotFoundException([
|
||||
'key' => 'user.notFound',
|
||||
'data' => [
|
||||
'name' => $email
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
// try to find an enabled challenge that is available for that user
|
||||
$challenge = null;
|
||||
foreach ($this->enabledChallenges() as $name) {
|
||||
$class = static::$challenges[$name] ?? null;
|
||||
if (
|
||||
@@ -131,23 +149,13 @@ class Auth
|
||||
}
|
||||
}
|
||||
|
||||
// if no suitable challenge was found, `$challenge === null` at this point;
|
||||
// only leak this in debug mode
|
||||
if ($challenge === null && $this->kirby->option('debug') === true) {
|
||||
// if no suitable challenge was found, `$challenge === null` at this point
|
||||
if ($challenge === null) {
|
||||
throw new LogicException('Could not find a suitable authentication challenge');
|
||||
}
|
||||
} else {
|
||||
$this->kirby->trigger('user.login:failed', compact('email'));
|
||||
|
||||
// only leak the non-existing user in debug mode
|
||||
if ($this->kirby->option('debug') === true) {
|
||||
throw new NotFoundException([
|
||||
'key' => 'user.notFound',
|
||||
'data' => [
|
||||
'name' => $email
|
||||
]
|
||||
]);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
// only throw the exception in auth debug mode
|
||||
$this->fail($e);
|
||||
}
|
||||
|
||||
// always set the email and timeout, even if the challenge
|
||||
@@ -158,7 +166,7 @@ class Auth
|
||||
// sleep for a random amount of milliseconds
|
||||
// to make automated attacks harder and to
|
||||
// avoid leaking whether the user exists
|
||||
usleep(random_int(1000, 300000));
|
||||
usleep(random_int(50000, 300000));
|
||||
|
||||
// clear the status cache
|
||||
$this->status = null;
|
||||
@@ -485,34 +493,21 @@ class Auth
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that email addresses with IDN domains are in Unicode format
|
||||
* and that the rate limit was not exceeded
|
||||
*
|
||||
* @param string $email
|
||||
* @return string The normalized Unicode email address
|
||||
* Ensures that the rate limit was not exceeded
|
||||
*
|
||||
* @throws \Kirby\Exception\PermissionException If the rate limit was exceeded
|
||||
*/
|
||||
protected function validateEmail(string $email): string
|
||||
protected function checkRateLimit(string $email): void
|
||||
{
|
||||
// ensure that email addresses with IDN domains are in Unicode format
|
||||
$email = Idn::decodeEmail($email);
|
||||
|
||||
// check for blocked ips
|
||||
if ($this->isBlocked($email) === true) {
|
||||
$this->kirby->trigger('user.login:failed', compact('email'));
|
||||
|
||||
if ($this->kirby->option('debug') === true) {
|
||||
$message = 'Rate limit exceeded';
|
||||
} else {
|
||||
// avoid leaking security-relevant information
|
||||
$message = ['key' => 'access.login'];
|
||||
}
|
||||
|
||||
throw new PermissionException($message);
|
||||
throw new PermissionException([
|
||||
'details' => ['reason' => 'rate-limited'],
|
||||
'fallback' => 'Rate limit exceeded'
|
||||
]);
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -529,10 +524,12 @@ class Auth
|
||||
*/
|
||||
public function validatePassword(string $email, string $password)
|
||||
{
|
||||
$email = $this->validateEmail($email);
|
||||
$email = Idn::decodeEmail($email);
|
||||
|
||||
// validate the user
|
||||
try {
|
||||
$this->checkRateLimit($email);
|
||||
|
||||
// validate the user and its password
|
||||
if ($user = $this->kirby->users()->find($email)) {
|
||||
if ($user->validatePassword($password) === true) {
|
||||
return $user;
|
||||
@@ -546,20 +543,25 @@ class Auth
|
||||
]
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
// log invalid login trial
|
||||
$this->track($email);
|
||||
$details = is_a($e, 'Kirby\Exception\Exception') === true ? $e->getDetails() : [];
|
||||
|
||||
// log invalid login trial unless the rate limit is already active
|
||||
if (($details['reason'] ?? null) !== 'rate-limited') {
|
||||
try {
|
||||
$this->track($email);
|
||||
} catch (Throwable $e) {
|
||||
// $e is overwritten with the exception
|
||||
// from the track method if there's one
|
||||
}
|
||||
}
|
||||
|
||||
// sleep for a random amount of milliseconds
|
||||
// to make automated attacks harder
|
||||
usleep(random_int(1000, 2000000));
|
||||
usleep(random_int(10000, 2000000));
|
||||
|
||||
// keep throwing the original error in debug mode,
|
||||
// otherwise hide it to avoid leaking security-relevant information
|
||||
if ($this->kirby->option('debug') === true) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
throw new PermissionException(['key' => 'access.login']);
|
||||
$this->fail($e, new PermissionException(['key' => 'access.login']));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -841,10 +843,7 @@ class Auth
|
||||
}
|
||||
|
||||
// rate-limiting
|
||||
if ($this->isBlocked($email) === true) {
|
||||
$this->kirby->trigger('user.login:failed', compact('email'));
|
||||
throw new PermissionException('Rate limit exceeded');
|
||||
}
|
||||
$this->checkRateLimit($email);
|
||||
|
||||
if (
|
||||
isset(static::$challenges[$challenge]) === true &&
|
||||
@@ -860,36 +859,67 @@ class Auth
|
||||
$this->status = null;
|
||||
|
||||
return $user;
|
||||
} else {
|
||||
throw new PermissionException(['key' => 'access.code']);
|
||||
}
|
||||
|
||||
throw new PermissionException(['key' => 'access.code']);
|
||||
}
|
||||
|
||||
throw new LogicException('Invalid authentication challenge: ' . $challenge);
|
||||
} catch (Throwable $e) {
|
||||
if (empty($email) === false && $e->getMessage() !== 'Rate limit exceeded') {
|
||||
$details = $e instanceof \Kirby\Exception\Exception ? $e->getDetails() : [];
|
||||
|
||||
if (
|
||||
empty($email) === false &&
|
||||
($details['reason'] ?? null) !== 'rate-limited'
|
||||
) {
|
||||
$this->track($email);
|
||||
}
|
||||
|
||||
// sleep for a random amount of milliseconds
|
||||
// to make automated attacks harder and to
|
||||
// avoid leaking whether the user exists
|
||||
usleep(random_int(1000, 2000000));
|
||||
usleep(random_int(10000, 2000000));
|
||||
|
||||
// specifically copy over the marker for a destroyed challenge
|
||||
// even in production (used by the Panel to reset to the login form)
|
||||
$challengeDestroyed = $details['challengeDestroyed'] ?? false;
|
||||
|
||||
$fallback = new PermissionException([
|
||||
'details' => compact('challengeDestroyed'),
|
||||
'key' => 'access.code'
|
||||
]);
|
||||
|
||||
// keep throwing the original error in debug mode,
|
||||
// otherwise hide it to avoid leaking security-relevant information
|
||||
if ($this->kirby->option('debug') === true) {
|
||||
throw $e;
|
||||
} else {
|
||||
// specifically copy over the marker for a destroyed challenge
|
||||
// even in production (used by the Panel to reset to the login form)
|
||||
$challengeDestroyed = $e->getDetails()['challengeDestroyed'] ?? false;
|
||||
$this->fail($e, $fallback);
|
||||
}
|
||||
}
|
||||
|
||||
throw new PermissionException([
|
||||
'details' => compact('challengeDestroyed'),
|
||||
'key' => 'access.code'
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Throws an exception only in debug mode, otherwise falls back
|
||||
* to a public error without sensitive information
|
||||
*
|
||||
* @throws \Throwable Either the passed `$exception` or the `$fallback`
|
||||
* (no exception if debugging is disabled and no fallback was passed)
|
||||
*/
|
||||
protected function fail(Throwable $exception, Throwable $fallback = null): void
|
||||
{
|
||||
$debug = $this->kirby->option('auth.debug', 'log');
|
||||
|
||||
// throw the original exception only in debug mode
|
||||
if ($debug === true) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
// otherwise hide the real error and only print it to the error log
|
||||
// unless disabled by setting `auth.debug` to `false`
|
||||
if ($debug === 'log') {
|
||||
error_log($exception); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// only throw an error in production if requested by the calling method
|
||||
if ($fallback !== null) {
|
||||
throw $fallback;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,9 +95,9 @@ class Status
|
||||
|
||||
if ($automaticFallback === false) {
|
||||
return $this->challenge;
|
||||
} else {
|
||||
return $this->challenge ?? $this->challengeFallback;
|
||||
}
|
||||
|
||||
return $this->challenge ?? $this->challengeFallback;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -731,7 +731,7 @@ class Blueprint
|
||||
$preset = static::$presets[$props['preset']];
|
||||
|
||||
if (is_string($preset) === true) {
|
||||
$preset = require $preset;
|
||||
$preset = F::load($preset, allowOutput: false);
|
||||
}
|
||||
|
||||
return $preset($props);
|
||||
|
||||
@@ -114,11 +114,14 @@ class Collection extends BaseCollection
|
||||
{
|
||||
if (count($args) === 1) {
|
||||
// try to determine the key from the provided item
|
||||
if (is_object($args[0]) === true && is_callable([$args[0], 'id']) === true) {
|
||||
if (
|
||||
is_object($args[0]) === true &&
|
||||
is_callable([$args[0], 'id']) === true
|
||||
) {
|
||||
return parent::append($args[0]->id(), $args[0]);
|
||||
} else {
|
||||
return parent::append($args[0]);
|
||||
}
|
||||
|
||||
return parent::append($args[0]);
|
||||
}
|
||||
|
||||
return parent::append(...$args);
|
||||
|
||||
@@ -123,7 +123,7 @@ class Collections
|
||||
$file = $kirby->root('collections') . '/' . $name . '.php';
|
||||
|
||||
if (is_file($file) === true) {
|
||||
$collection = F::load($file);
|
||||
$collection = F::load($file, allowOutput: false);
|
||||
|
||||
if ($collection instanceof Closure) {
|
||||
return $collection;
|
||||
|
||||
@@ -115,7 +115,7 @@ class Content
|
||||
$oldField = $oldFields->get($name);
|
||||
|
||||
// field name and type matches with old template
|
||||
if ($oldField && $oldField->type() === $newField->type()) {
|
||||
if ($oldField?->type() === $newField->type()) {
|
||||
$data[$name] = $content->get($name)->value();
|
||||
} else {
|
||||
$data[$name] = $newField->default();
|
||||
|
||||
@@ -451,9 +451,9 @@ class File extends ModelWithContent
|
||||
* Return the permanent URL to the file using its UUID
|
||||
* @since 3.8.0
|
||||
*/
|
||||
public function permalink(): string
|
||||
public function permalink(): string|null
|
||||
{
|
||||
return $this->uuid()->url();
|
||||
return $this->uuid()?->url();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,7 @@ use Kirby\Exception\LogicException;
|
||||
use Kirby\Filesystem\F;
|
||||
use Kirby\Form\Form;
|
||||
use Kirby\Uuid\Uuid;
|
||||
use Kirby\Uuid\Uuids;
|
||||
|
||||
/**
|
||||
* FileActions
|
||||
@@ -155,7 +156,9 @@ trait FileActions
|
||||
$copy = $page->clone()->file($this->filename());
|
||||
|
||||
// overwrite with new UUID (remove old, add new)
|
||||
$copy = $copy->save(['uuid' => Uuid::generate()]);
|
||||
if (Uuids::enabled() === true) {
|
||||
$copy = $copy->save(['uuid' => Uuid::generate()]);
|
||||
}
|
||||
|
||||
return $copy;
|
||||
}
|
||||
@@ -191,7 +194,9 @@ trait FileActions
|
||||
|
||||
// make sure that a UUID gets generated and
|
||||
// added to content right away
|
||||
$content['uuid'] = Uuid::generate();
|
||||
if (Uuids::enabled() === true) {
|
||||
$content['uuid'] ??= Uuid::generate();
|
||||
}
|
||||
|
||||
// create a form for the file
|
||||
$form = Form::for($file, ['values' => $content]);
|
||||
@@ -336,7 +341,7 @@ trait FileActions
|
||||
$this->lock()?->remove();
|
||||
|
||||
// clear UUID cache
|
||||
$this->uuid()->clear();
|
||||
$this->uuid()?->clear();
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
@@ -81,7 +81,7 @@ class Languages extends Collection
|
||||
$files = glob(App::instance()->root('languages') . '/*.php');
|
||||
|
||||
foreach ($files as $file) {
|
||||
$props = F::load($file);
|
||||
$props = F::load($file, allowOutput: false);
|
||||
|
||||
if (is_array($props) === true) {
|
||||
// inject the language code from the filename
|
||||
|
||||
@@ -160,12 +160,12 @@ class Loader
|
||||
{
|
||||
if (is_string($item) === true) {
|
||||
$item = match (F::extension($item)) {
|
||||
'php' => require $item,
|
||||
'php' => F::load($item, allowOutput: false),
|
||||
default => Data::read($item)
|
||||
};
|
||||
}
|
||||
|
||||
if (is_callable($item)) {
|
||||
if (is_callable($item) === true) {
|
||||
$item = $item($this->kirby);
|
||||
}
|
||||
|
||||
|
||||
@@ -632,7 +632,7 @@ abstract class ModelWithContent extends Model implements Identifiable
|
||||
* Returns the model's UUID
|
||||
* @since 3.8.0
|
||||
*/
|
||||
public function uuid(): Uuid
|
||||
public function uuid(): Uuid|null
|
||||
{
|
||||
return Uuid::for($this);
|
||||
}
|
||||
|
||||
@@ -964,9 +964,9 @@ class Page extends ModelWithContent
|
||||
* Return the permanent URL to the page using its UUID
|
||||
* @since 3.8.0
|
||||
*/
|
||||
public function permalink(): string
|
||||
public function permalink(): string|null
|
||||
{
|
||||
return $this->uuid()->url();
|
||||
return $this->uuid()?->url();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@ use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\I18n;
|
||||
use Kirby\Toolkit\Str;
|
||||
use Kirby\Uuid\Uuid;
|
||||
use Kirby\Uuid\Uuids;
|
||||
|
||||
/**
|
||||
* PageActions
|
||||
@@ -108,7 +109,7 @@ trait PageActions
|
||||
]);
|
||||
|
||||
// clear UUID cache recursively (for children and files as well)
|
||||
$oldPage->uuid()->clear(true);
|
||||
$oldPage->uuid()?->clear(true);
|
||||
|
||||
if ($oldPage->exists() === true) {
|
||||
// remove the lock of the old page
|
||||
@@ -443,7 +444,9 @@ trait PageActions
|
||||
}
|
||||
|
||||
// overwrite with new UUID (remove old, add new)
|
||||
$copy = $copy->save(['uuid' => Uuid::generate()]);
|
||||
if (Uuids::enabled() === true) {
|
||||
$copy = $copy->save(['uuid' => Uuid::generate()]);
|
||||
}
|
||||
|
||||
// add copy to siblings
|
||||
static::updateParentCollections($copy, 'append', $parentModel);
|
||||
@@ -467,7 +470,10 @@ trait PageActions
|
||||
// make sure that a UUID gets generated and
|
||||
// added to content right away
|
||||
$props['content'] ??= [];
|
||||
$props['content']['uuid'] ??= Uuid::generate();
|
||||
|
||||
if (Uuids::enabled() === true) {
|
||||
$props['content']['uuid'] ??= Uuid::generate();
|
||||
}
|
||||
|
||||
// create a temporary page object
|
||||
$page = Page::factory($props);
|
||||
@@ -596,7 +602,7 @@ trait PageActions
|
||||
{
|
||||
return $this->commit('delete', ['page' => $this, 'force' => $force], function ($page, $force) {
|
||||
// clear UUID cache
|
||||
$page->uuid()->clear();
|
||||
$page->uuid()?->clear();
|
||||
|
||||
// delete all files individually
|
||||
foreach ($page->files() as $file) {
|
||||
|
||||
@@ -122,9 +122,9 @@ trait PageSiblings
|
||||
{
|
||||
if ($this->isDraft() === true) {
|
||||
return $this->parentModel()->drafts();
|
||||
} else {
|
||||
return $this->parentModel()->children();
|
||||
}
|
||||
|
||||
return $this->parentModel()->children();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
|
||||
namespace Kirby\Cms;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use Exception;
|
||||
use Kirby\Cms\System\UpdateStatus;
|
||||
use Kirby\Data\Data;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\Str;
|
||||
use Kirby\Toolkit\V;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Represents a Plugin and handles parsing of
|
||||
@@ -269,9 +272,22 @@ class Plugin extends Model
|
||||
*/
|
||||
public function version(): string|null
|
||||
{
|
||||
$version = $this->info()['version'] ?? null;
|
||||
$composerName = $this->info()['name'] ?? null;
|
||||
$version = $this->info()['version'] ?? null;
|
||||
|
||||
if (is_string($version) !== true || $version === '') {
|
||||
try {
|
||||
// if plugin doesn't have version key in composer.json file
|
||||
// try to get version from "vendor/composer/installed.php"
|
||||
$version ??= InstalledVersions::getPrettyVersion($composerName);
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
is_string($version) !== true ||
|
||||
$version === '' ||
|
||||
Str::endsWith($version, '+no-version-set')
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -322,12 +322,11 @@ class System
|
||||
|
||||
// only return the actual license key if the
|
||||
// current user has appropriate permissions
|
||||
$user = $this->app->user();
|
||||
if ($user && $user->isAdmin() === true) {
|
||||
if ($this->app->user()?->isAdmin() === true) {
|
||||
return $license['license'];
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -157,9 +157,9 @@ class User extends ModelWithContent
|
||||
{
|
||||
if ($relative === true) {
|
||||
return 'users/' . $this->id();
|
||||
} else {
|
||||
return $this->kirby()->url('api') . '/users/' . $this->id();
|
||||
}
|
||||
|
||||
return $this->kirby()->url('api') . '/users/' . $this->id();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -308,12 +308,12 @@ trait UserActions
|
||||
$path = $this->root() . '/index.php';
|
||||
|
||||
if (is_file($path) === true) {
|
||||
$credentials = F::load($path);
|
||||
$credentials = F::load($path, allowOutput: false);
|
||||
|
||||
return is_array($credentials) === false ? [] : $credentials;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -148,7 +148,7 @@ class Users extends Collection
|
||||
// get role information
|
||||
$path = $root . '/' . $userDirectory . '/index.php';
|
||||
if (is_file($path) === true) {
|
||||
$credentials = F::load($path);
|
||||
$credentials = F::load($path, allowOutput: false);
|
||||
}
|
||||
|
||||
// create user model based on role
|
||||
|
||||
Reference in New Issue
Block a user