Skip to content

Commit 79340d8

Browse files
feat(UserGroup): allow updating groups with system scope #822
1 parent 5918c74 commit 79340d8

3 files changed

Lines changed: 71 additions & 3 deletions

File tree

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/UserGroup.inc

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use RESTAPI\Fields\IntegerField;
99
use RESTAPI\Fields\StringField;
1010
use RESTAPI\Responses\ForbiddenError;
1111
use RESTAPI\Responses\NotFoundError;
12+
use RESTAPI\Responses\UnprocessableContentError;
1213
use RESTAPI\Responses\ValidationError;
1314
use RESTAPI\Validators\RegexValidator;
1415

@@ -48,9 +49,10 @@ class UserGroup extends Model {
4849
);
4950
$this->scope = new StringField(
5051
default: 'local',
51-
choices: ['local', 'remote'],
52+
choices: ['local', 'remote', 'system'],
5253
help_text: 'The scope of this user group. Use `local` for user groups that only apply to this system. use ' .
53-
'`remote` for groups that also apply to remote authentication servers.',
54+
'`remote` for groups that also apply to remote authentication servers. Please note the `system` scope ' .
55+
'is reserved for built-in, system-defined user groups and cannot be assigned manually.',
5456
);
5557
$this->member = new ForeignModelField(
5658
model_name: 'User',
@@ -95,6 +97,26 @@ class UserGroup extends Model {
9597
return $name;
9698
}
9799

100+
/**
101+
* Adds additional validation to the `scope` field.
102+
* @param string $scope The incoming value to be validated.
103+
* @return string The validated value to be assigned.
104+
* @throws ValidationError When `scope` is set to `system` and this is a newly created object or the existing
105+
* `scope` is not already `system`.
106+
*/
107+
public function validate_scope(string $scope): string {
108+
$existing_scope = $this->initial_object->scope->value ?? null;
109+
110+
if ($scope === 'system' and $scope !== $existing_scope) {
111+
throw new UnprocessableContentError(
112+
message: 'Field `scope` cannot be set to `system` as it is reserved for system-defined user groups only.',
113+
response_id: 'USER_GROUP_SCOPE_CANNOT_BE_SET_TO_SYSTEM',
114+
);
115+
}
116+
117+
return $scope;
118+
}
119+
98120
/**
99121
* Adds additional validation to the `priv` field.
100122
* @param string $priv The incoming value to be validated

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Responses/UnprocessableContentError.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use RESTAPI\Core\Response;
1515
*/
1616
class UnprocessableContentError extends Response {
1717
public $code = 422;
18-
public string $help_text = 'The client has requested a resource that requires a dependency which is not installed.';
18+
public string $help_text = 'The client has requested a resource that requires a dependency or other requirement that was not met.';
1919

2020
public function __construct(
2121
$message,

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsUserGroupTestCase.inc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,50 @@ class APIModelsUserGroupTestCase extends TestCase {
123123
},
124124
);
125125
}
126+
127+
/**
128+
* Ensures that UserGroups cannot be created or updated with the 'system' scope unless the existing object also has
129+
* the 'system' scope.
130+
*/
131+
public function test_cannot_create_or_update_user_groups_with_system_scope(): void {
132+
# Create a UserGroup to test with
133+
$user_group = new UserGroup();
134+
135+
# Ensure attempts to create UserGroups with the system scope are rejected
136+
$this->assert_throws_response(
137+
response_id: 'USER_GROUP_SCOPE_CANNOT_BE_SET_TO_SYSTEM',
138+
code: 422,
139+
callable: function () use ($user_group) {
140+
$user_group->scope->value = 'system';
141+
$user_group->create();
142+
},
143+
);
144+
145+
# Ensure attempts to update existing UserGroups to have the system scope are rejected
146+
$this->assert_throws_response(
147+
response_id: 'USER_GROUP_SCOPE_CANNOT_BE_SET_TO_SYSTEM',
148+
code: 422,
149+
callable: function () use ($user_group) {
150+
# Create a local UserGroup to test with
151+
$user_group = new UserGroup(data: ['name' => 'testgroup', 'scope' => 'local']);
152+
$user_group->create();
153+
154+
# Attempt to update the scope to `system`
155+
$user_group->scope->value = 'system';
156+
$user_group->update();
157+
},
158+
);
159+
160+
# Delete the test UserGroup
161+
$user_group->delete();
162+
163+
# Ensure we CAN update an existing UserGroup with the system scope to still have the system scope
164+
$this->assert_does_not_throw(
165+
callable: function () {
166+
# Create a UserGroup with the system scope to test with
167+
$admin_group = UserGroup::query(name: 'Admins')->first();
168+
$admin_group->validate_scope('system');
169+
},
170+
);
171+
}
126172
}

0 commit comments

Comments
 (0)