Skip to content

Commit b6ac66c

Browse files
committed
Merge branch 'develop' into 1.2
2 parents 363d932 + 79f2a6d commit b6ac66c

File tree

20 files changed

+374
-41
lines changed

20 files changed

+374
-41
lines changed

modules/backend/behaviors/FormController.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,7 @@ public function __construct($controller)
122122
*/
123123
public function initForm($model, $context = null)
124124
{
125-
if ($context !== null) {
126-
$this->context = $context;
127-
}
128-
129-
$context = $this->formGetContext();
125+
$context = $this->context = $context ?? $this->formGetContext();
130126

131127
/*
132128
* Each page can supply a unique form definition, if desired
@@ -442,7 +438,7 @@ public function formGetModel()
442438
*/
443439
public function formGetContext()
444440
{
445-
return post('form_context', $this->context);
441+
return $this->context;
446442
}
447443

448444
/**

modules/backend/classes/FormField.php

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ class FormField
9191
public $span = 'full';
9292

9393
/**
94-
* @var string Specifies a size. Possible values: tiny, small, large, huge, giant.
94+
* @var string|int Specifies a size. Possible values for textarea: tiny, small, large, huge, giant.
9595
*/
96-
public $size = 'large';
96+
public $size;
9797

9898
/**
9999
* @var string Specifies contextual visibility of this form field.
@@ -211,7 +211,7 @@ public function span($value = 'full')
211211
}
212212

213213
/**
214-
* Sets a side of the field on a form.
214+
* Sets the size of the field on a form.
215215
* @param string $value Specifies a size. Possible values: tiny, small, large, huge, giant
216216
*/
217217
public function size($value = 'large')
@@ -259,6 +259,11 @@ public function options($value = null)
259259
*/
260260
public function displayAs($type, $config = [])
261261
{
262+
if (in_array($type, ['textarea', 'widget'])) {
263+
// defaults to 'large'
264+
$this->size = 'large';
265+
}
266+
262267
$this->type = strtolower($type) ?: $this->type;
263268
$this->config = $this->evalConfig($config);
264269

@@ -281,18 +286,18 @@ protected function evalConfig($config)
281286
*/
282287
$applyConfigValues = [
283288
'commentHtml',
284-
'placeholder',
289+
'context',
290+
'cssClass',
285291
'dependsOn',
286-
'required',
287-
'readOnly',
288292
'disabled',
289-
'cssClass',
290-
'stretch',
291-
'context',
292293
'hidden',
293-
'trigger',
294-
'preset',
295294
'path',
295+
'placeholder',
296+
'preset',
297+
'readOnly',
298+
'required',
299+
'stretch',
300+
'trigger',
296301
];
297302

298303
foreach ($applyConfigValues as $value) {
@@ -729,4 +734,31 @@ protected function getFieldNameFromData($fieldName, $data, $default = null)
729734

730735
return $result;
731736
}
737+
738+
/**
739+
* Implements the getter functionality.
740+
* @param string $name
741+
*/
742+
public function __get($name)
743+
{
744+
if (is_array($this->config) && array_key_exists($name, $this->config)) {
745+
return array_get($this->config, $name);
746+
}
747+
if (property_exists($this, $name)) {
748+
return $this->{$name};
749+
}
750+
return null;
751+
}
752+
753+
/**
754+
* Determine if an attribute exists on the object.
755+
* @param string $name
756+
*/
757+
public function __isset($name)
758+
{
759+
if (is_array($this->config) && array_key_exists($name, $this->config)) {
760+
return true;
761+
}
762+
return property_exists($this, $name) && !is_null($this->{$name});
763+
}
732764
}

modules/backend/lang/en/lang.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@
167167
'updated_at' => 'Updated at',
168168
'deleted_at' => 'Deleted at',
169169
'show_deleted' => 'Show deleted',
170+
'self_escalation_denied' => 'You cannot modify your own role, permissions, or superuser status.',
171+
'superuser_grant_denied' => 'Only superusers can grant superuser status or modify other superuser accounts.',
172+
'manage_users_denied' => 'You do not have permission to manage other administrators.',
170173
'throttle_tab' => 'Failed Logins',
171174
'throttle_tab_label' => 'Failed Login Records',
172175
'throttle_comment' => 'View failed login attempts for this user. These records are automatically generated when login attempts fail. Users are suspended after exceeding the attempt limit.',

modules/backend/models/User.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
use Backend\Facades\Backend;
44
use Backend\Facades\BackendAuth;
55
use Illuminate\Support\Facades\Event;
6+
use Illuminate\Support\Facades\Lang;
7+
use Winter\Storm\Auth\AuthorizationException;
68
use Winter\Storm\Auth\Models\User as UserBase;
79
use Winter\Storm\Support\Facades\Mail;
810

@@ -126,6 +128,36 @@ public function getAvatarThumb($size = 25, $options = null)
126128
'&d='. urlencode($default);
127129
}
128130

131+
/**
132+
* Before save event — enforce authorization rules to prevent privilege escalation.
133+
* @return void
134+
*/
135+
public function beforeSave()
136+
{
137+
$actor = BackendAuth::getUser();
138+
$isCurrentUser = $this->exists && $actor && $actor->getKey() === $this->getKey();
139+
140+
// No authenticated user (CLI, artisan, queue, seeders) — allow everything
141+
if (!$actor) {
142+
return;
143+
}
144+
145+
// Rule 1: Self-escalation — users cannot modify their own role, superuser status, or permissions
146+
if ($isCurrentUser && $this->isDirty(['role_id', 'is_superuser', 'permissions'])) {
147+
throw new AuthorizationException(Lang::get('backend::lang.user.self_escalation_denied'));
148+
}
149+
150+
// Rule 2: Must have backend.manage_users to manage other users
151+
if (!$isCurrentUser && !$actor->hasAccess('backend.manage_users')) {
152+
throw new AuthorizationException(Lang::get('backend::lang.user.manage_users_denied'));
153+
}
154+
155+
// Rule 3: Only superusers can grant superuser status or edit existing superusers
156+
if (!$actor->isSuperUser() && ($this->is_superuser || $this->getOriginal('is_superuser'))) {
157+
throw new AuthorizationException(Lang::get('backend::lang.user.superuser_grant_denied'));
158+
}
159+
}
160+
129161
/**
130162
* After create event
131163
* @return void
@@ -225,7 +257,7 @@ public function unsuspend()
225257

226258
/**
227259
* Returns an array of merged permissions based on the user's individual permissions
228-
* and their group permissions filtering out any permissions the impersonator doesn't
260+
* and their role permissions filtering out any permissions the impersonator doesn't
229261
* have access to (if the current user is being impersonated)
230262
*
231263
* @return array

0 commit comments

Comments
 (0)