Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [0.2.1] - 2026-09-22
- The user directory, a single user, and the engagement report answer to the configured owner, as every other endpoint here already did. They were reachable by any authenticated caller

## [0.2.0] - 2026-09-22

### Added
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "whilesmart/eloquent-admin",
"description": "Shared measurements, user directory and automatic email templates for Laravel applications.",
"type": "library",
"version": "0.2.0",
"version": "0.2.1",
"license": "MIT",
"authors": [{ "name": "Whilesmart Team" }],
"require": {
Expand Down
22 changes: 14 additions & 8 deletions src/Http/Controllers/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class AdminController extends Controller
*/
public function offers(Request $request, OfferRegistry $offers): JsonResponse
{
$this->authorizeOffers($request);
$this->authorizeConsole($request);

return response()->json([
'success' => true,
Expand All @@ -42,7 +42,7 @@ public function offers(Request $request, OfferRegistry $offers): JsonResponse

public function createOffer(Request $request, OfferRegistry $offers, string $provider): JsonResponse
{
$this->authorizeOffers($request);
$this->authorizeConsole($request);

// Defaulted rather than read straight out: a host that published this
// config before the key existed has an array that wins over the one
Expand All @@ -56,19 +56,19 @@ public function createOffer(Request $request, OfferRegistry $offers, string $pro

public function revokeOffer(Request $request, OfferRegistry $offers, string $provider, string $id): JsonResponse
{
$this->authorizeOffers($request);
$this->authorizeConsole($request);

$this->offerProvider($offers, $provider)->revoke($id);

return response()->json(['success' => true]);
}

/**
* Offers answer to the configured owner, the way a mail template answers to
* its own. Checked here rather than in the request, so a host swapping the
* request class cannot drop it.
* The console answers to the configured owner, the way a mail template
* answers to its own. Checked in the controller rather than in a request
* class, so a host swapping that class cannot drop it.
*/
private function authorizeOffers(Request $request): void
private function authorizeConsole(Request $request): void
{
$owner = config('admin.owner');

Expand All @@ -90,6 +90,8 @@ private function offerProvider(OfferRegistry $offers, string $key): OfferProvide

public function metrics(Request $request, EngagementManager $engagement, ClientRegistry $clients): JsonResponse
{
$this->authorizeConsole($request);

$granularity = in_array($request->query('granularity'), ['day', 'week', 'month'], true)
? $request->query('granularity')
: 'day';
Expand All @@ -105,14 +107,18 @@ public function metrics(Request $request, EngagementManager $engagement, ClientR

public function users(Request $request, AdminUserProvider $provider): JsonResponse
{
$this->authorizeConsole($request);

$resource = config('admin.resources.user', AdminUserResource::class);
$users = $provider->paginate((string) $request->input('q', ''), (int) $request->input('per_page', 25));

return response()->json(['success' => true, 'data' => $resource::collection($users)->response()->getData(true)]);
}

public function user(mixed $id, AdminUserProvider $provider): JsonResponse
public function user(Request $request, mixed $id, AdminUserProvider $provider): JsonResponse
{
$this->authorizeConsole($request);

$resource = config('admin.resources.user', AdminUserResource::class);
$user = $provider->find($id);

Expand Down
8 changes: 8 additions & 0 deletions tests/Feature/AdminAuthorizationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,12 @@ public function templates_are_hidden_and_writes_are_forbidden_when_authorization
'body' => 'No',
])->assertForbidden();
}

#[Test]
public function the_directory_and_the_report_are_forbidden_when_authorization_denies(): void
{
$this->getJson('/api/admin/users')->assertForbidden();
$this->getJson('/api/admin/users/1')->assertForbidden();
$this->getJson('/api/admin/metrics')->assertForbidden();
}
}
Loading