diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 461e90da58..0ab166e28d 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -546,33 +546,45 @@ App::post('/v1/teams/:teamId/memberships') throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team'); } - $secret = Auth::tokenGenerator(); - - $membershipId = ID::unique(); - $membership = new Document([ - '$id' => $membershipId, - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::user($invitee->getId())), - Permission::update(Role::team($team->getId(), 'owner')), - Permission::delete(Role::user($invitee->getId())), - Permission::delete(Role::team($team->getId(), 'owner')), - ], - 'userId' => $invitee->getId(), - 'userInternalId' => $invitee->getInternalId(), - 'teamId' => $team->getId(), - 'teamInternalId' => $team->getInternalId(), - 'roles' => $roles, - 'invited' => DateTime::now(), - 'joined' => ($isPrivilegedUser || $isAppUser) ? DateTime::now() : null, - 'confirm' => ($isPrivilegedUser || $isAppUser), - 'secret' => Auth::hash($secret), - 'search' => implode(' ', [$membershipId, $invitee->getId()]) + $membership = $dbForProject->findOne('memberships', [ + Query::equal('userId', [$invitee->getId()]), + Query::equal('teamId', [$team->getId()]), ]); + $createdMembership = false; + + if ($membership->isEmpty()) { + $secret = Auth::tokenGenerator(); + + $membershipId = ID::unique(); + $membership = new Document([ + '$id' => $membershipId, + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::user($invitee->getId())), + Permission::update(Role::team($team->getId(), 'owner')), + Permission::delete(Role::user($invitee->getId())), + Permission::delete(Role::team($team->getId(), 'owner')), + ], + 'userId' => $invitee->getId(), + 'userInternalId' => $invitee->getInternalId(), + 'teamId' => $team->getId(), + 'teamInternalId' => $team->getInternalId(), + 'roles' => $roles, + 'invited' => DateTime::now(), + 'joined' => ($isPrivilegedUser || $isAppUser) ? DateTime::now() : null, + 'confirm' => ($isPrivilegedUser || $isAppUser), + 'secret' => Auth::hash($secret), + 'search' => implode(' ', [$membershipId, $invitee->getId()]) + ]); + + $createdMembership = true; + } if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership try { - $membership = Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)); + $membership = $createdMembership ? + Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)) : + Authorization::skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)); } catch (Duplicate $th) { throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS); } @@ -582,7 +594,9 @@ App::post('/v1/teams/:teamId/memberships') $dbForProject->purgeCachedDocument('users', $invitee->getId()); } else { try { - $membership = $dbForProject->createDocument('memberships', $membership); + $membership = $createdMembership ? + $dbForProject->createDocument('memberships', $membership) : + $dbForProject->updateDocument('memberships', $membership->getId(), $membership); } catch (Duplicate $th) { throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS); } diff --git a/tests/e2e/Services/Teams/TeamsBaseClient.php b/tests/e2e/Services/Teams/TeamsBaseClient.php index ca6082f6d0..cf011d2bf7 100644 --- a/tests/e2e/Services/Teams/TeamsBaseClient.php +++ b/tests/e2e/Services/Teams/TeamsBaseClient.php @@ -292,9 +292,9 @@ trait TeamsBaseClient $this->assertEquals('Invitation to ' . $teamName . ' Team at ' . $this->getProject()['name'], $lastEmail['subject']); /** - * Test for FAILURE + * Test for resend + * SUCCESS */ - $response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -305,7 +305,11 @@ trait TeamsBaseClient 'url' => 'http://localhost:5000/join-us#title' ]); - $this->assertEquals(409, $response['headers']['status-code']); + $this->assertEquals(201, $response['headers']['status-code']); + + /** + * Test for FAILURE + */ $response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Teams/TeamsBaseServer.php b/tests/e2e/Services/Teams/TeamsBaseServer.php index 6a1d05e9d4..226536c604 100644 --- a/tests/e2e/Services/Teams/TeamsBaseServer.php +++ b/tests/e2e/Services/Teams/TeamsBaseServer.php @@ -186,9 +186,9 @@ trait TeamsBaseServer // $this->assertContains('team:'.$teamUid.'/editor', $response['body']['roles']); /** - * Test for FAILURE + * Test for resend + * SUCCESS */ - $response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -199,7 +199,11 @@ trait TeamsBaseServer 'url' => 'http://localhost:5000/join-us#title' ]); - $this->assertEquals(409, $response['headers']['status-code']); + $this->assertEquals(201, $response['headers']['status-code']); + + /** + * Test for FAILURE + */ $response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ 'content-type' => 'application/json',