Skip to content

Commit 43df997

Browse files
oliverkleeSam Tuke
authored andcommitted
[FEATURE] REST API endpoint for deleting a session (log out) (#101)
1 parent 8ad7a77 commit 43df997

File tree

8 files changed

+140
-12
lines changed

8 files changed

+140
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
77
## x.y.z
88

99
### Added
10+
- REST API endpoint for deleting a session (log out) (#101)
1011
- REST API endpoint for deleting a list (#98)
1112
- REST API endpoint for getting list details (#89)
1213
- System tests for the test and dev environment (#81)

docs/Api/RestApi.apib

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,41 @@ that require authentication. (The basic auth user name can be any string.)
7878
"message": "Not authorized"
7979
}
8080

81+
## Single session [/sessions/{session}]
82+
83+
### Destroy a the session (log out) [DELETE]
84+
85+
+ Request (application/json)
86+
87+
+ Response 204 (application/json)
88+
89+
+ Response 403 (application/json)
90+
91+
+ Body
92+
93+
{
94+
"code": 403,
95+
"message": "No valid session key was provided as basic auth password."
96+
}
97+
98+
+ Response 403 (application/json)
99+
100+
+ Body
101+
102+
{
103+
"code": 403,
104+
"message": "You do not have access to this session."
105+
}
106+
107+
+ Response 404 (application/json)
108+
109+
+ Body
110+
111+
{
112+
"code": 404,
113+
"message": "There is no session with that ID."
114+
}
115+
81116

82117
# Group Lists
83118

src/Controller/SessionController.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
use PhpList\PhpList4\Domain\Model\Identity\AdministratorToken;
1111
use PhpList\PhpList4\Domain\Repository\Identity\AdministratorRepository;
1212
use PhpList\PhpList4\Domain\Repository\Identity\AdministratorTokenRepository;
13+
use PhpList\PhpList4\Security\Authentication;
14+
use PhpList\RestBundle\Controller\Traits\AuthenticationTrait;
1315
use Symfony\Component\HttpFoundation\Request;
1416
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
1518
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1619
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
1720

@@ -22,6 +25,8 @@
2225
*/
2326
class SessionController extends FOSRestController implements ClassResourceInterface
2427
{
28+
use AuthenticationTrait;
29+
2530
/**
2631
* @var AdministratorRepository
2732
*/
@@ -33,13 +38,16 @@ class SessionController extends FOSRestController implements ClassResourceInterf
3338
private $administratorTokenRepository = null;
3439

3540
/**
41+
* @param Authentication $authentication
3642
* @param AdministratorRepository $administratorRepository
3743
* @param AdministratorTokenRepository $tokenRepository
3844
*/
3945
public function __construct(
46+
Authentication $authentication,
4047
AdministratorRepository $administratorRepository,
4148
AdministratorTokenRepository $tokenRepository
4249
) {
50+
$this->authentication = $authentication;
4351
$this->administratorRepository = $administratorRepository;
4452
$this->administratorTokenRepository = $tokenRepository;
4553
}
@@ -69,6 +77,30 @@ public function postAction(Request $request): View
6977
return View::create()->setStatusCode(Response::HTTP_CREATED)->setData($token);
7078
}
7179

80+
/**
81+
* Deletes a session.
82+
*
83+
* This action may only be called for sessions that are owned by the authenticated administrator.
84+
*
85+
* @param Request $request
86+
* @param AdministratorToken $token
87+
*
88+
* @return View
89+
*
90+
* @throws AccessDeniedHttpException
91+
*/
92+
public function deleteAction(Request $request, AdministratorToken $token): View
93+
{
94+
$administrator = $this->requireAuthentication($request);
95+
if ($token->getAdministrator() !== $administrator) {
96+
throw new AccessDeniedHttpException('You do not have access to this session.', null, 1519831644);
97+
}
98+
99+
$this->administratorTokenRepository->remove($token);
100+
101+
return View::create();
102+
}
103+
72104
/**
73105
* Validates the request. If is it not valid, throws an exception.
74106
*

src/Controller/Traits/AuthenticationTrait.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace PhpList\RestBundle\Controller\Traits;
55

6+
use PhpList\PhpList4\Domain\Model\Identity\Administrator;
67
use PhpList\PhpList4\Security\Authentication;
78
use Symfony\Component\HttpFoundation\Request;
89
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
@@ -26,11 +27,11 @@ trait AuthenticationTrait
2627
*
2728
* @param Request $request
2829
*
29-
* @return void
30+
* @return Administrator the authenticated administrator
3031
*
3132
* @throws AccessDeniedHttpException
3233
*/
33-
private function requireAuthentication(Request $request)
34+
private function requireAuthentication(Request $request): Administrator
3435
{
3536
$administrator = $this->authentication->authenticateByApiKey($request);
3637
if ($administrator === null) {
@@ -40,5 +41,7 @@ private function requireAuthentication(Request $request)
4041
1512749701
4142
);
4243
}
44+
45+
return $administrator;
4346
}
4447
}

tests/Integration/Controller/AbstractControllerTest.php

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -192,22 +192,13 @@ protected function assertHttpNotFound()
192192
}
193193

194194
/**
195-
* Asserts that the current client response has a HTTP FORBIDDEN status and the corresponding error message
196-
* provided in the JSON response.
195+
* Asserts that the current client response has a HTTP FORBIDDEN status.
197196
*
198197
* @return void
199198
*/
200199
protected function assertHttpForbidden()
201200
{
202201
$this->assertHttpStatusWithJsonContentType(Response::HTTP_FORBIDDEN);
203-
204-
static::assertSame(
205-
[
206-
'code' => Response::HTTP_FORBIDDEN,
207-
'message' => 'No valid session key was provided as basic auth password.',
208-
],
209-
$this->getDecodedJsonResponseContent()
210-
);
211202
}
212203

213204
/**
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
id,loginname,email,created,modified,password,passwordchanged,disabled,superuser
22
1,"john.doe","[email protected]","2017-06-22 15:01:17","2017-06-23 19:50:43","1491a3c7e7b23b9a6393323babbb095dee0d7d81b2199617b487bd0fb5236f3c","2017-06-28",0,1
3+
2,"jane.doe","[email protected]","2017-06-22 15:01:17","2017-06-23 19:50:43","1491a3c7e7b23b9a6393323babbb095dee0d7d81b2199617b487bd0fb5236f3d","2017-06-28",0,1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
id,value,expires,adminid,entered
22
1,"cfdf64eecbbf336628b0f3071adba762","2027-06-22 16:43:29",1,1512582100
33
2,"cfdf64eecbbf336628b0f3071adba763","2017-06-22 16:43:29",1,1512582100
4+
3,"cfdf64eecbbf336628b0f3071adba764","2017-06-22 16:43:29",2,1512582100

tests/Integration/Controller/SessionControllerTest.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,68 @@ public function postSessionsActionWithValidCredentialsCreatesToken()
196196
static::assertSame($expiry, $token->getExpiry()->format(\DateTime::ATOM));
197197
static::assertSame($administratorId, $token->getAdministrator()->getId());
198198
}
199+
200+
/**
201+
* @test
202+
*/
203+
public function deleteSessionWithoutSessionKeyForExistingSessionReturnsForbiddenStatus()
204+
{
205+
$this->getDataSet()->addTable(static::ADMINISTRATOR_TABLE_NAME, __DIR__ . '/Fixtures/Administrator.csv');
206+
$this->getDataSet()->addTable(static::TOKEN_TABLE_NAME, __DIR__ . '/Fixtures/AdministratorToken.csv');
207+
$this->applyDatabaseChanges();
208+
209+
$this->client->request('delete', '/api/v2/sessions/1');
210+
211+
$this->assertHttpForbidden();
212+
}
213+
214+
/**
215+
* @test
216+
*/
217+
public function deleteSessionWithCurrentSessionKeyForExistingSessionReturnsNoContentStatus()
218+
{
219+
$this->authenticatedJsonRequest('delete', '/api/v2/sessions/1');
220+
221+
$this->assertHttpNoContent();
222+
}
223+
224+
/**
225+
* @test
226+
*/
227+
public function deleteSessionWithCurrentSessionKeyForInexistentSessionReturnsNotFoundStatus()
228+
{
229+
$this->authenticatedJsonRequest('delete', '/api/v2/sessions/999');
230+
231+
$this->assertHttpNotFound();
232+
}
233+
234+
/**
235+
* @test
236+
*/
237+
public function deleteSessionWithCurrentSessionAndOwnSessionKeyDeletesSession()
238+
{
239+
$this->authenticatedJsonRequest('delete', '/api/v2/sessions/1');
240+
241+
static::assertNull($this->administratorTokenRepository->find(1));
242+
}
243+
244+
/**
245+
* @test
246+
*/
247+
public function deleteSessionWithCurrentSessionAndOwnSessionKeyKeepsReturnsForbiddenStatus()
248+
{
249+
$this->authenticatedJsonRequest('delete', '/api/v2/sessions/3');
250+
251+
$this->assertHttpForbidden();
252+
}
253+
254+
/**
255+
* @test
256+
*/
257+
public function deleteSessionWithCurrentSessionAndOwnSessionKeyKeepsSession()
258+
{
259+
$this->authenticatedJsonRequest('delete', '/api/v2/sessions/3');
260+
261+
static::assertNotNull($this->administratorTokenRepository->find(3));
262+
}
199263
}

0 commit comments

Comments
 (0)