mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-10-30 18:06:16 +01:00
Fixed lost user access when saving user profile without super user permissions [#1639]
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
1. [](#bugfix)
|
1. [](#bugfix)
|
||||||
* Fixed user edit links if Flex Objects plugin is installed but user isn't Flex User
|
* Fixed user edit links if Flex Objects plugin is installed but user isn't Flex User
|
||||||
* Fixed deprecated `sameas()` Twig test
|
* Fixed deprecated `sameas()` Twig test
|
||||||
|
* Regression: Fixed lost user access when saving user profile without super user permissions [#1639](https://github.com/getgrav/grav-plugin-admin/issues/1639)
|
||||||
|
|
||||||
# v1.9.0-rc.4
|
# v1.9.0-rc.4
|
||||||
## 03/20/2019
|
## 03/20/2019
|
||||||
|
|||||||
@@ -650,19 +650,11 @@ class Admin
|
|||||||
$obj->file($file);
|
$obj->file($file);
|
||||||
|
|
||||||
$data[$type] = $obj;
|
$data[$type] = $obj;
|
||||||
} elseif (preg_match('|users/|', $type)) {
|
} elseif (preg_match('|users?/|', $type)) {
|
||||||
/** @var UserCollectionInterface $users */
|
/** @var UserCollectionInterface $users */
|
||||||
$users = $this->grav['accounts'];
|
$users = $this->grav['accounts'];
|
||||||
|
|
||||||
$obj = $users->load(preg_replace('|users/|', '', $type));
|
$obj = $users->load(preg_replace('|users?/|', '', $type));
|
||||||
$obj->update($this->cleanUserPost($post));
|
|
||||||
|
|
||||||
$data[$type] = $obj;
|
|
||||||
} elseif (preg_match('|user/|', $type)) {
|
|
||||||
/** @var UserCollectionInterface $users */
|
|
||||||
$users = $this->grav['accounts'];
|
|
||||||
|
|
||||||
$obj = $users->load(preg_replace('|user/|', '', $type));
|
|
||||||
$obj->update($this->cleanUserPost($post));
|
$obj->update($this->cleanUserPost($post));
|
||||||
|
|
||||||
$data[$type] = $obj;
|
$data[$type] = $obj;
|
||||||
@@ -714,15 +706,14 @@ class Admin
|
|||||||
* @param array $post
|
* @param array $post
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function cleanUserPost($post)
|
public function cleanUserPost($post)
|
||||||
{
|
{
|
||||||
// Clean fields for all users
|
// Clean fields for all users
|
||||||
unset($post['hashed_password']);
|
unset($post['hashed_password']);
|
||||||
|
|
||||||
// Clean field for users who shouldn't be able to modify these fields
|
// Clean field for users who shouldn't be able to modify these fields
|
||||||
if (!$this->authorize(['admin.user', 'admin.super'])) {
|
if (!$this->authorize(['admin.user', 'admin.super'])) {
|
||||||
unset($post['access']);
|
unset($post['access'], $post['state']);
|
||||||
unset($post['state']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $post;
|
return $post;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use Grav\Common\Media\Interfaces\MediaInterface;
|
|||||||
use Grav\Common\Page\Interfaces\PageInterface;
|
use Grav\Common\Page\Interfaces\PageInterface;
|
||||||
use Grav\Common\Page\Media;
|
use Grav\Common\Page\Media;
|
||||||
use Grav\Common\Uri;
|
use Grav\Common\Uri;
|
||||||
|
use Grav\Common\User\Interfaces\UserInterface;
|
||||||
use Grav\Common\Utils;
|
use Grav\Common\Utils;
|
||||||
use Grav\Common\Plugin;
|
use Grav\Common\Plugin;
|
||||||
use Grav\Common\Theme;
|
use Grav\Common\Theme;
|
||||||
@@ -727,9 +728,9 @@ class AdminBaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param PageInterface|Data $obj
|
* @param PageInterface|UserInterface|Data $obj
|
||||||
*
|
*
|
||||||
* @return PageInterface|Data
|
* @return PageInterface|UserInterface|Data
|
||||||
*/
|
*/
|
||||||
protected function storeFiles($obj)
|
protected function storeFiles($obj)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -556,114 +556,92 @@ class AdminController extends AdminBaseController
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->grav['twig']->twig_vars['current_form_data'] = (array)$this->data;
|
||||||
|
|
||||||
|
switch ($this->view) {
|
||||||
|
case 'pages':
|
||||||
|
return $this->taskSavePage();
|
||||||
|
case 'user':
|
||||||
|
return $this->taskSaveUser();
|
||||||
|
default:
|
||||||
|
return $this->taskSaveDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function taskSavePage()
|
||||||
|
{
|
||||||
$reorder = true;
|
$reorder = true;
|
||||||
$data = (array)$this->data;
|
|
||||||
|
|
||||||
$this->grav['twig']->twig_vars['current_form_data'] = $data;
|
/** @var Pages $pages */
|
||||||
|
$pages = $this->grav['pages'];
|
||||||
|
|
||||||
// Special handler for user data.
|
// Find new parent page in order to build the path.
|
||||||
if ($this->view === 'user') {
|
$route = $data['route'] ?? dirname($this->admin->route);
|
||||||
if (!$this->grav['user']->exists()) {
|
|
||||||
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.NO_USER_EXISTS'),'error');
|
/** @var PageInterface $obj */
|
||||||
return false;
|
$obj = $this->admin->page(true);
|
||||||
}
|
|
||||||
if (!$this->admin->authorize(['admin.super', 'admin.users'])) {
|
if (!isset($data['folder']) || !$data['folder']) {
|
||||||
// no user file or not admin.super or admin.users
|
$data['folder'] = $obj->slug();
|
||||||
if ($this->prepareData($data)->username !== $this->grav['user']->username) {
|
$this->data['folder'] = $obj->slug();
|
||||||
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK') . ' save.','error');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handler for pages data.
|
// Ensure route is prefixed with a forward slash.
|
||||||
if ($this->view === 'pages') {
|
$route = '/' . ltrim($route, '/');
|
||||||
/** @var Pages $pages */
|
|
||||||
$pages = $this->grav['pages'];
|
|
||||||
|
|
||||||
// Find new parent page in order to build the path.
|
// Check for valid frontmatter
|
||||||
$route = !isset($data['route']) ? dirname($this->admin->route) : $data['route'];
|
if (isset($data['frontmatter']) && !$this->checkValidFrontmatter($data['frontmatter'])) {
|
||||||
|
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.INVALID_FRONTMATTER_COULD_NOT_SAVE'),
|
||||||
|
'error');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** @var PageInterface $obj */
|
// XSS Checks for page content
|
||||||
$obj = $this->admin->page(true);
|
$xss_whitelist = $this->grav['config']->get('security.xss_whitelist', 'admin.super');
|
||||||
|
if (!$this->admin->authorize($xss_whitelist)) {
|
||||||
if (!isset($data['folder']) || !$data['folder']) {
|
$check_what = ['header' => $data['header'] ?? '', 'frontmatter' => $data['frontmatter'] ?? '', 'content' => $data['content'] ?? ''];
|
||||||
$data['folder'] = $obj->slug();
|
$results = Security::detectXssFromArray($check_what);
|
||||||
$this->data['folder'] = $obj->slug();
|
if (!empty($results)) {
|
||||||
}
|
$this->admin->setMessage('<i class="fa fa-ban"></i> ' . $this->admin::translate('PLUGIN_ADMIN.XSS_ONSAVE_ISSUE'),
|
||||||
|
|
||||||
// Ensure route is prefixed with a forward slash.
|
|
||||||
$route = '/' . ltrim($route, '/');
|
|
||||||
|
|
||||||
// Check for valid frontmatter
|
|
||||||
if (isset($data['frontmatter']) && !$this->checkValidFrontmatter($data['frontmatter'])) {
|
|
||||||
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.INVALID_FRONTMATTER_COULD_NOT_SAVE'),
|
|
||||||
'error');
|
'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// XSS Checks for page content
|
$parent = $route && $route !== '/' && $route !== '.' && $route !== '/.' ? $pages->dispatch($route, true) : $pages->root();
|
||||||
$xss_whitelist = $this->grav['config']->get('security.xss_whitelist', 'admin.super');
|
$original_order = (int)trim($obj->order(), '.');
|
||||||
if (!$this->admin->authorize($xss_whitelist)) {
|
|
||||||
$check_what = ['header' => $data['header'] ?? '', 'frontmatter' => $data['frontmatter'] ?? '', 'content' => $data['content'] ?? ''];
|
try {
|
||||||
$results = Security::detectXssFromArray($check_what);
|
// Change parent if needed and initialize move (might be needed also on ordering/folder change).
|
||||||
if (!empty($results)) {
|
$obj = $obj->move($parent);
|
||||||
$this->admin->setMessage('<i class="fa fa-ban"></i> ' . $this->admin::translate('PLUGIN_ADMIN.XSS_ONSAVE_ISSUE'),
|
$this->preparePage($obj, false, $obj->language());
|
||||||
'error');
|
$obj->validate();
|
||||||
return false;
|
|
||||||
}
|
} catch (\Exception $e) {
|
||||||
|
$this->admin->setMessage($e->getMessage(), 'error');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$obj->filter();
|
||||||
|
|
||||||
|
// rename folder based on visible
|
||||||
|
if ($original_order === 1000) {
|
||||||
|
// increment order to force reshuffle
|
||||||
|
$obj->order($original_order + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['order']) && !empty($data['order'])) {
|
||||||
|
$reorder = explode(',', $data['order']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add or remove numeric prefix based on ordering value
|
||||||
|
if (isset($data['ordering'])) {
|
||||||
|
if ($data['ordering'] && !$obj->order()) {
|
||||||
|
$obj->order(static::getNextOrderInFolder($obj->parent()->path()));
|
||||||
|
$reorder = false;
|
||||||
|
} elseif (!$data['ordering'] && $obj->order()) {
|
||||||
|
$obj->folder($obj->slug());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$parent = $route && $route !== '/' && $route !== '.' && $route !== '/.' ? $pages->dispatch($route, true) : $pages->root();
|
|
||||||
$original_order = (int)trim($obj->order(), '.');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Change parent if needed and initialize move (might be needed also on ordering/folder change).
|
|
||||||
$obj = $obj->move($parent);
|
|
||||||
$this->preparePage($obj, false, $obj->language());
|
|
||||||
$obj->validate();
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->admin->setMessage($e->getMessage(), 'error');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$obj->filter();
|
|
||||||
|
|
||||||
// rename folder based on visible
|
|
||||||
if ($original_order === 1000) {
|
|
||||||
// increment order to force reshuffle
|
|
||||||
$obj->order($original_order + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($data['order']) && !empty($data['order'])) {
|
|
||||||
$reorder = explode(',', $data['order']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add or remove numeric prefix based on ordering value
|
|
||||||
if (isset($data['ordering'])) {
|
|
||||||
if ($data['ordering'] && !$obj->order()) {
|
|
||||||
$obj->order(static::getNextOrderInFolder($obj->parent()->path()));
|
|
||||||
$reorder = false;
|
|
||||||
} elseif (!$data['ordering'] && $obj->order()) {
|
|
||||||
$obj->folder($obj->slug());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Handle standard data types.
|
|
||||||
$obj = $this->prepareData($data);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$obj->validate();
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->admin->setMessage($e->getMessage(), 'error');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$obj->filter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$obj = $this->storeFiles($obj);
|
$obj = $this->storeFiles($obj);
|
||||||
@@ -676,44 +654,110 @@ class AdminController extends AdminBaseController
|
|||||||
$this->grav->fireEvent('onAdminAfterSave', new Event(['object' => $obj]));
|
$this->grav->fireEvent('onAdminAfterSave', new Event(['object' => $obj]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->view !== 'pages') {
|
if (method_exists($obj, 'unsetRouteSlug')) {
|
||||||
// Force configuration reload.
|
$obj->unsetRouteSlug();
|
||||||
/** @var Config $config */
|
}
|
||||||
$config = $this->grav['config'];
|
|
||||||
$config->reload();
|
|
||||||
|
|
||||||
if ($this->view === 'user') {
|
$multilang = $this->isMultilang();
|
||||||
if ($obj->username === $this->grav['user']->username) {
|
|
||||||
/** @var UserCollectionInterface $users */
|
|
||||||
$users = $this->grav['accounts'];
|
|
||||||
|
|
||||||
//Editing current user. Reload user object
|
if ($multilang) {
|
||||||
unset($this->grav['user']->avatar);
|
if (!$obj->language()) {
|
||||||
$this->grav['user']->merge($users->load($this->admin->route)->toArray());
|
$obj->language($this->grav['session']->admin_lang);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
$admin_route = $this->admin->base;
|
||||||
|
|
||||||
|
$route = $obj->rawRoute();
|
||||||
|
$redirect_url = ($multilang ? '/' . $obj->language() : '') . $admin_route . '/' . $this->view . $route;
|
||||||
|
$this->setRedirect($redirect_url);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function taskSaveUser()
|
||||||
|
{
|
||||||
|
/** @var UserCollectionInterface $users */
|
||||||
|
$users = $this->grav['accounts'];
|
||||||
|
|
||||||
|
$user = $users->load($this->admin->route);
|
||||||
|
|
||||||
|
if (!$this->admin->authorize(['admin.super', 'admin.users'])) {
|
||||||
|
// no user file or not admin.super or admin.users
|
||||||
|
if ($user->username !== $this->grav['user']->username) {
|
||||||
|
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK') . ' save.','error');
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always redirect if a page route was changed, to refresh it
|
/** @var Data\Blueprint $blueprint */
|
||||||
if ($obj instanceof PageInterface) {
|
$blueprint = $user->blueprints();
|
||||||
if (method_exists($obj, 'unsetRouteSlug')) {
|
$data = $blueprint->processForm($this->admin->cleanUserPost((array)$this->data));
|
||||||
$obj->unsetRouteSlug();
|
$data = new Data\Data($data, $blueprint);
|
||||||
}
|
|
||||||
|
|
||||||
$multilang = $this->isMultilang();
|
try {
|
||||||
|
$data->validate();
|
||||||
|
$data->filter();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->admin->setMessage($e->getMessage(), 'error');
|
||||||
|
|
||||||
if ($multilang) {
|
return false;
|
||||||
if (!$obj->language()) {
|
|
||||||
$obj->language($this->grav['session']->admin_lang);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$admin_route = $this->admin->base;
|
|
||||||
|
|
||||||
$route = $obj->rawRoute();
|
|
||||||
$redirect_url = ($multilang ? '/' . $obj->language() : '') . $admin_route . '/' . $this->view . $route;
|
|
||||||
$this->setRedirect($redirect_url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$user->update($data->toArray());
|
||||||
|
|
||||||
|
$user = $this->storeFiles($user);
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
// Event to manipulate data before saving the object
|
||||||
|
$this->grav->fireEvent('onAdminSave', new Event(['object' => &$user]));
|
||||||
|
$user->save();
|
||||||
|
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_SAVED'), 'info');
|
||||||
|
$this->grav->fireEvent('onAdminAfterSave', new Event(['object' => $user]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->username === $this->grav['user']->username) {
|
||||||
|
/** @var UserCollectionInterface $users */
|
||||||
|
$users = $this->grav['accounts'];
|
||||||
|
|
||||||
|
//Editing current user. Reload user object
|
||||||
|
unset($this->grav['user']->avatar);
|
||||||
|
$this->grav['user']->merge($users->load($this->admin->route)->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function taskSaveDefault()
|
||||||
|
{
|
||||||
|
// Handle standard data types.
|
||||||
|
$obj = $this->prepareData((array)$this->data);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$obj->validate();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->admin->setMessage($e->getMessage(), 'error');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$obj->filter();
|
||||||
|
|
||||||
|
$obj = $this->storeFiles($obj);
|
||||||
|
|
||||||
|
if ($obj) {
|
||||||
|
// Event to manipulate data before saving the object
|
||||||
|
$this->grav->fireEvent('onAdminSave', new Event(['object' => &$obj]));
|
||||||
|
$obj->save();
|
||||||
|
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_SAVED'), 'info');
|
||||||
|
$this->grav->fireEvent('onAdminAfterSave', new Event(['object' => $obj]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force configuration reload.
|
||||||
|
/** @var Config $config */
|
||||||
|
$config = $this->grav['config'];
|
||||||
|
$config->reload();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user