Feature: Multi-User Support
Relevant source files
The following files were used as context for generating this wiki page:
PriceBuddy is designed as a multi-tenant application where each user has their own isolated set of products, stores, and tags. This ensures that user data is private and cannot be accessed by other users. The system achieves this through a combination of database schema design, API-level authorization, and scoped queries in the user interface.
The core of this feature is the user_id foreign key present on all user-specific resources. API requests are authenticated using tokens, and authorization policies ensure that a user can only perform actions on resources they own. The Filament admin panel provides a dedicated interface for managing user accounts and their settings.
Sources: README.md:14, tests/Feature/Api/ProductApiTest.php, app/Models/User.php
Data Model and Relationships
The multi-user architecture is centered around the User model. All primary resources that a user creates, such as Product, Tag, and Store, are directly associated with a user via a user_id foreign key.
Sources: app/Models/User.php:28-44
Entity Relationship Diagram
The following diagram illustrates the relationships between the User model and its associated resources.
This diagram shows that Product, Tag, and Store entities all belong to a USER.
Sources: app/Models/User.php:28-44
User Model (app/Models/User.php)
The User model defines the structure and relationships for user accounts.
| Field | Type | Description |
|---|---|---|
id | int | Primary key for the user. |
name | string | The user's name. |
email | string | The user's unique email address, used for login. |
password | string | The user's hashed password. |
email_verified_at | timestamp | Timestamp of when the user verified their email. |
settings | json (cast to AsArrayObject) | Stores user-specific settings, like notifications. |
remember_token | string | Token for "remember me" functionality. |
Sources: app/Models/User.php:14-26
The model defines the following key relationships to enforce data isolation:
products(): AHasManyrelationship to theProductmodel.tags(): AHasManyrelationship to theTagmodel.stores(): AHasManyrelationship to theStoremodel.
Sources: app/Models/User.php:28-44
Data Isolation and Access Control
Data isolation is a critical aspect of the multi-user system. PriceBuddy ensures that users can only access their own data. This is primarily enforced at the API and application level through authorization policies and scoped queries.
API Access Control
The API uses token-based authentication. Each request to a protected endpoint must include an Authorization header with a bearer token. The system then uses this token to identify the authenticated user and scope all data operations to that user.
API tests demonstrate that attempting to access another user's resources results in a 404 Not Found response, effectively preventing unauthorized data access.
// tests/Feature/Api/ProductApiTest.php:90-97
public function test_cannot_show_other_users_product(): void
{
$otherUser = User::factory()->create();
$product = Product::factory()->create(['user_id' => $otherUser->id]);
$response = $this->getJson("/api/products/{$product->id}");
$response->assertNotFound();
}
Sources: tests/Feature/Api/ProductApiTest.php:20-22, tests/Feature/Api/ProductApiTest.php:90-97, tests/Feature/Api/TagApiTest.php:73-80, tests/Feature/Api/StoreApiTest.php:73-80
API Request Flow for Data Access
The sequence diagram below shows how an API request for a product is handled, including the authorization check.
This flow ensures that before any data is returned, the system verifies that the authenticated user is the owner of the requested resource.
Sources: tests/Feature/Api/ProductApiTest.php:90-97, tests/Feature/Api/TagApiTest.php:73-80
Preventing User ID Spoofing
When creating new resources, the user_id is automatically set to the ID of the authenticated user. Any attempt to pass a different user_id in the request payload is ignored, preventing users from creating resources on behalf of others.
// tests/Feature/Api/ProductApiTest.php:128-135
// The product should be created but with the authenticated user's ID, not the spoofed one
$this->assertDatabaseHas('products', [
'title' => 'Test Product',
'user_id' => $this->user->id, // Should be the authenticated user, not the other user
]);
$this->assertDatabaseMissing('products', [
'title' => 'Test Product',
'user_id' => $otherUser->id, // Should not be created with the spoofed user_id
]);
Sources: tests/Feature/Api/ProductApiTest.php:115-139
User Management via Filament
User accounts are managed through the Filament admin panel. The UserResource provides the interface for creating, viewing, and editing users.
Sources: app/Filament/Resources/UserResource.php
User Form
The user creation and editing form contains fields for managing account details and notification preferences.
| Section | Field | Type | Description |
|---|---|---|---|
| Account | name | Text Input | The user's name. Required. |
email | Email Input | The user's unique email address. Required. | |
password | Password Input | The user's password. Required on creation. Automatically hashed. | |
| Notification Settings | enabled | Toggle | Per-channel toggles for Email, Pushover, Gotify, and Apprise. |
user_key | Text Input | Pushover user key. | |
url | Text Input | Gotify server URL. | |
token | Text Input | Gotify/Apprise application token. |
Sources: app/Filament/Resources/UserResource.php:30-70, tests/Feature/Filament/UserTest.php:70-98
The user management functionality is tested in UserTest.php, which confirms that users can be created and their settings can be updated through the Filament interface.
Sources: tests/Feature/Filament/UserTest.php
Summary
PriceBuddy's multi-user support is a foundational feature that ensures data privacy and a personalized user experience. It is robustly implemented through a clear database schema with user_id foreign keys, strict API-level authorization checks, and a comprehensive user management interface in the Filament admin panel. The design prevents unauthorized data access and user ID spoofing, making the application secure for multiple users.