Skip to content

Commit 39bde2a

Browse files
committed
feat: add user to group endpoint
Signed-off-by: romanetar <roman_ag@hotmail.com>
1 parent 56325c7 commit 39bde2a

File tree

10 files changed

+219
-1
lines changed

10 files changed

+219
-1
lines changed

app/Http/Controllers/Api/OAuth2/OAuth2UserApiController.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@
1313
**/
1414

1515
use App\Http\Controllers\GetAllTrait;
16+
use App\Http\Controllers\Traits\RequestProcessor;
17+
use App\Http\Controllers\UserGroupsValidationRulesFactory;
1618
use App\Http\Controllers\UserValidationRulesFactory;
19+
use App\Http\Exceptions\HTTP403ForbiddenException;
1720
use App\Http\Utils\HTMLCleaner;
1821
use App\ModelSerializers\SerializerRegistry;
1922
use Auth\Repositories\IUserRepository;
23+
use Illuminate\Http\JsonResponse;
2024
use Illuminate\Http\Request as LaravelRequest;
25+
use Illuminate\Support\Facades\App;
2126
use Illuminate\Support\Facades\Auth;
2227
use Illuminate\Support\Facades\Request;
2328
use Illuminate\Support\Facades\Log;
@@ -27,6 +32,7 @@
2732
use models\exceptions\ValidationException;
2833
use OAuth2\Builders\IdTokenBuilder;
2934
use OAuth2\IResourceServerContext;
35+
use OAuth2\Models\IClient;
3036
use OAuth2\Repositories\IClientRepository;
3137
use OAuth2\ResourceServer\IUserService;
3238
use Utils\Http\HttpContentType;
@@ -41,6 +47,8 @@ final class OAuth2UserApiController extends OAuth2ProtectedController
4147
{
4248
use GetAllTrait;
4349

50+
use RequestProcessor;
51+
4452
protected function getAllSerializerType(): string
4553
{
4654
return SerializerRegistry::SerializerType_Private;
@@ -324,4 +332,34 @@ public function get($id)
324332
}
325333
}
326334

335+
/**
336+
* @param $user_id
337+
* @return JsonResponse|mixed
338+
*/
339+
public function addUserToGroup($user_id): mixed
340+
{
341+
return $this->processRequest(function() use($user_id) {
342+
//check if it's a service app
343+
$app_type = $this->resource_server_context->getApplicationType();
344+
if (App::environment() != "testing" && !empty($app_type) && $app_type != IClient::ApplicationType_Service) {
345+
throw new HTTP403ForbiddenException("You are not allowed to perform this action.");
346+
}
347+
348+
if(!Request::isJson()) return $this->error400();
349+
350+
$payload = Request::json()->all();
351+
// Creates a Validator instance and validates the data.
352+
$validation = Validator::make($payload, UserGroupsValidationRulesFactory::build($payload));
353+
if ($validation->fails()) {
354+
$ex = new ValidationException();
355+
throw $ex->setMessages($validation->messages()->toArray());
356+
}
357+
$user_groups_payload = [
358+
"groups" => $payload["groups"],
359+
];
360+
$this->openid_user_service->update(intval($user_id), $user_groups_payload);
361+
return $this->updated();
362+
});
363+
}
364+
327365
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php namespace App\Http\Controllers;
2+
/**
3+
* Copyright 2025 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
use Auth\User;
15+
/**
16+
* Class UserGroupsValidationRulesFactory
17+
* @package App\Http\Controllers
18+
*/
19+
final class UserGroupsValidationRulesFactory
20+
{
21+
/**
22+
* @param array $data
23+
* @param false $update
24+
* @param User|null $currentUser
25+
* @return string[]
26+
*/
27+
public static function build(array $data, $update = false, ?User $currentUser = null){
28+
29+
if($update){
30+
return [
31+
'groups' => 'sometimes|int_array',
32+
];
33+
}
34+
35+
return [
36+
'groups' => 'required|int_array',
37+
];
38+
}
39+
}

app/libs/OAuth2/IUserScopes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ interface IUserScopes
2929
const MeRead = 'me/read';
3030
const MeWrite = 'me/write';
3131
const Write = 'users/write';
32+
const UserGroupWrite = 'users/groups/write';
3233
}

app/libs/OpenId/Services/IUserService.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Illuminate\Http\UploadedFile;
1717
use models\exceptions\EntityNotFoundException;
1818
use models\exceptions\ValidationException;
19+
use models\utils\IEntity;
20+
1921
/**
2022
* Interface IUserService
2123
* @package OpenId\Services
@@ -72,6 +74,15 @@ public function saveProfileInfo($user_id, $show_pic, $show_full_name, $show_emai
7274
*/
7375
public function updateProfilePhoto($user_id, UploadedFile $file, $max_file_size = 10485760):User;
7476

77+
/**
78+
* @param int $id
79+
* @param array $payload
80+
* @return IEntity
81+
* @throws ValidationException
82+
* @throws EntityNotFoundException
83+
*/
84+
public function update(int $id, array $payload): IEntity;
85+
7586
/**
7687
* @param string $action
7788
* @param int $user_id

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"Database\\Seeders\\": "database/seeders/"
9090
},
9191
"files": [
92+
"app/Utils/helpers.php",
9293
"app/libs/Utils/Html/HtmlHelpers.php"
9394
]
9495
},
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php namespace Database\Migrations;
2+
/**
3+
* Copyright 2025 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
15+
use App\libs\OAuth2\IUserScopes;
16+
use Database\Seeders\SeedUtils;
17+
use Doctrine\Migrations\AbstractMigration;
18+
use Doctrine\DBAL\Schema\Schema as Schema;
19+
/**
20+
* Class Version20250805084926
21+
* @package Database\Migrations
22+
*/
23+
class Version20250805084926 extends AbstractMigration
24+
{
25+
/**
26+
* @param Schema $schema
27+
*/
28+
public function up(Schema $schema):void
29+
{
30+
SeedUtils::seedScopes([
31+
[
32+
'name' => IUserScopes::UserGroupWrite,
33+
'short_description' => 'Allows associate Users to Groups.',
34+
'description' => 'Allows associate Users to Groups.',
35+
'system' => false,
36+
'default' => false,
37+
'groups' => false,
38+
]
39+
], 'users');
40+
41+
SeedUtils::seedApiEndpoints('users', [
42+
[
43+
'name' => 'add-user-to-groups',
44+
'active' => true,
45+
'route' => '/api/v1/users/{id}/groups',
46+
'http_method' => 'PUT',
47+
'scopes' => [
48+
IUserScopes::UserGroupWrite
49+
],
50+
],
51+
]);
52+
}
53+
54+
/**
55+
* @param Schema $schema
56+
*/
57+
public function down(Schema $schema):void
58+
{
59+
60+
}
61+
}

database/seeds/ApiEndpointSeeder.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
* See the License for the specific language governing permissions and
1212
* limitations under the License.
1313
**/
14+
15+
use App\libs\OAuth2\IUserScopes;
1416
use Illuminate\Database\Seeder;
1517
use Illuminate\Support\Facades\DB;
1618
/**
@@ -119,6 +121,15 @@ private function seedUsersEndpoints()
119121
\App\libs\OAuth2\IUserScopes::MeWrite
120122
],
121123
],
124+
[
125+
'name' => 'add-user-to-groups',
126+
'active' => true,
127+
'route' => '/api/v1/users/{id}/groups',
128+
'http_method' => 'PUT',
129+
'scopes' => [
130+
\App\libs\OAuth2\IUserScopes::UserGroupWrite
131+
],
132+
],
122133
]
123134
);
124135
}

database/seeds/ApiScopeSeeder.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ private function seedUsersScopes(){
9191
'system' => false,
9292
'default' => false,
9393
'groups' => false,
94+
],
95+
[
96+
'name' => IUserScopes::UserGroupWrite,
97+
'short_description' => 'Allows associate Users to Groups',
98+
'description' => 'Allows associate Users to Groups',
99+
'system' => false,
100+
'default' => false,
101+
'groups' => false,
94102
]
95103
], 'users');
96104

routes/api.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
Route::group(['prefix' => '{id}'], function () {
3232
Route::get('', 'OAuth2UserApiController@get');
3333
Route::put('', 'OAuth2UserApiController@update');
34+
Route::put('groups', 'OAuth2UserApiController@addUserToGroup');
3435
});
3536

3637
Route::group(['prefix' => 'me'], function () {

tests/OAuth2UserServiceApiTest.php

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
* limitations under the License.
1313
**/
1414
use App\libs\OAuth2\IUserScopes;
15+
use Auth\Group;
16+
use Auth\User;
17+
use Illuminate\Support\Facades\App;
18+
use LaravelDoctrine\ORM\Facades\EntityManager;
19+
use OAuth2\IResourceServerContext;
20+
use OAuth2\Models\IClient;
1521
use OAuth2\ResourceServer\IUserService;
1622
/**
1723
* Class OAuth2UserServiceApiTest
@@ -102,14 +108,55 @@ public function testGetAllWithoutFilter(){
102108
$this->assertTrue($page->total > 0);
103109
}
104110

111+
public function testAddUserToGroup(){
112+
$repo = EntityManager::getRepository(Group::class);
113+
$group = $repo->getOneBySlug('raw-users');
114+
115+
$repo = EntityManager::getRepository(User::class);
116+
$user = $repo->getAll()[0];
117+
118+
$params = [
119+
'id' => $user->getId()
120+
];
121+
122+
$data = [
123+
'groups' => [$group->getId()],
124+
];
125+
126+
$headers = [
127+
"HTTP_Authorization" => " Bearer " . $this->access_token,
128+
"CONTENT_TYPE" => "application/json"
129+
];
130+
131+
$former_groups_count = count($user->getGroups());
132+
133+
$this->action(
134+
"PUT",
135+
"Api\OAuth2\OAuth2UserApiController@addUserToGroup",
136+
$params,
137+
[],
138+
[],
139+
[],
140+
$headers,
141+
json_encode($data)
142+
);
143+
144+
$this->assertResponseStatus(201);
145+
146+
$user = $repo->getById($user->getId());
147+
$this->assertNotNull($user);
148+
$this->assertCount(1, $user->getGroups());
149+
}
150+
105151
protected function getScopes()
106152
{
107153
$scope = array(
108154
IUserService::UserProfileScope_Address,
109155
IUserService::UserProfileScope_Email,
110156
IUserService::UserProfileScope_Profile,
111157
IUserScopes::MeWrite,
112-
IUserScopes::ReadAll
158+
IUserScopes::ReadAll,
159+
IUserScopes::UserGroupWrite
113160
);
114161

115162
return $scope;

0 commit comments

Comments
 (0)