mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-10-08 05:43:18 +01:00
Compare commits
5 Commits
feature/fi
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f1c58e216 | ||
|
|
349623d537 | ||
|
|
a9d016d773 | ||
|
|
2ebbbceefb | ||
|
|
e85058aefd |
@@ -11,6 +11,7 @@ use App\Models\Client;
|
||||
use App\Models\Member;
|
||||
use App\Models\Organization;
|
||||
use App\Models\OrganizationInvitation;
|
||||
use App\Models\Passport\RefreshToken;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectMember;
|
||||
use App\Models\Report;
|
||||
@@ -169,6 +170,10 @@ class DeletionService
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh tokens are not linked to the user directly, so they need to be deleted via their access tokens.
|
||||
// Otherwise a still-valid refresh token could be used to issue a new access token for a deleted user,
|
||||
// which fails with a foreign key violation on oauth_access_tokens.user_id.
|
||||
RefreshToken::query()->whereIn('access_token_id', $user->accessTokens()->pluck('id'))->delete();
|
||||
$user->accessTokens()->delete();
|
||||
$user->authCodes()->delete();
|
||||
|
||||
|
||||
472
composer.lock
generated
472
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"Billing": {
|
||||
"repository": "solidtime-io/extension-billing",
|
||||
"ref": "v0.0.7"
|
||||
"ref": "v0.0.8"
|
||||
},
|
||||
"Services": {
|
||||
"repository": "solidtime-io/extension-services",
|
||||
|
||||
@@ -168,6 +168,10 @@ defineExpose({ submit, focusAfterStart });
|
||||
data-testid="time_entry_description"
|
||||
class="w-full rounded-l-lg py-4 sm:py-2.5 px-3.5 border-b border-b-card-background-separator @2xl:px-4 text-base text-text-primary bg-transparent border-none placeholder-text-secondary focus:ring-0 transition"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
data-1p-ignore
|
||||
data-lpignore="true"
|
||||
data-form-type="other"
|
||||
@keydown.enter="submit"
|
||||
@keydown.esc="showDropdown = false"
|
||||
@blur="updateTimeEntryDescription" />
|
||||
|
||||
@@ -170,6 +170,10 @@ function closeAndFocusInput() {
|
||||
: 'text-text-primary bg-card-background border-border-secondary border border-none'
|
||||
"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
data-1p-ignore
|
||||
data-lpignore="true"
|
||||
data-form-type="other"
|
||||
@focusin="openModalOnTab"
|
||||
@click="openModalOnClick"
|
||||
@keydown.exact.tab="focusNextElement"
|
||||
|
||||
@@ -19,7 +19,7 @@ export const useNotificationsStore = defineStore('notifications', () => {
|
||||
const showActionBlockedModal = ref(false);
|
||||
|
||||
function addNotification(type: NotificationType, title: string, message?: string) {
|
||||
const uuid = crypto.randomUUID();
|
||||
const uuid = Math.random().toString(36).substring(7);
|
||||
notifications.value.push({ title, message, type, uuid });
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -10,6 +10,9 @@ use App\Exceptions\Api\CanNotDeleteUserWhoIsOwnerOfOrganizationWithMultipleMembe
|
||||
use App\Models\Client;
|
||||
use App\Models\Member;
|
||||
use App\Models\Organization;
|
||||
use App\Models\Passport\Client as PassportClient;
|
||||
use App\Models\Passport\RefreshToken;
|
||||
use App\Models\Passport\Token;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectMember;
|
||||
use App\Models\Report;
|
||||
@@ -23,6 +26,7 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use Tests\TestCaseWithDatabase;
|
||||
use TiMacDonald\Log\LogEntry;
|
||||
@@ -424,4 +428,45 @@ class DeletionServiceTest extends TestCaseWithDatabase
|
||||
'role' => Role::Placeholder->value,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_delete_user_deletes_access_tokens_and_their_refresh_tokens_but_does_not_delete_tokens_of_other_users(): void
|
||||
{
|
||||
// Arrange
|
||||
$user = User::factory()->create();
|
||||
$otherUser = User::factory()->create();
|
||||
$passportClient = PassportClient::factory()->create();
|
||||
|
||||
$userToken = Token::factory()->forUser($user)->forClient($passportClient)->create();
|
||||
$userRefreshToken = RefreshToken::query()->create([
|
||||
'id' => Str::random(100),
|
||||
'access_token_id' => $userToken->getKey(),
|
||||
'revoked' => false,
|
||||
'expires_at' => now()->addDays(30),
|
||||
]);
|
||||
|
||||
$otherUserToken = Token::factory()->forUser($otherUser)->forClient($passportClient)->create();
|
||||
$otherUserRefreshToken = RefreshToken::query()->create([
|
||||
'id' => Str::random(100),
|
||||
'access_token_id' => $otherUserToken->getKey(),
|
||||
'revoked' => false,
|
||||
'expires_at' => now()->addDays(30),
|
||||
]);
|
||||
|
||||
// Act
|
||||
$this->deletionService->deleteUser($user);
|
||||
|
||||
// Assert
|
||||
$this->assertDatabaseMissing(Token::class, [
|
||||
'id' => $userToken->getKey(),
|
||||
]);
|
||||
$this->assertDatabaseMissing(RefreshToken::class, [
|
||||
'id' => $userRefreshToken->getKey(),
|
||||
]);
|
||||
$this->assertDatabaseHas(Token::class, [
|
||||
'id' => $otherUserToken->getKey(),
|
||||
]);
|
||||
$this->assertDatabaseHas(RefreshToken::class, [
|
||||
'id' => $otherUserRefreshToken->getKey(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user