Skip to content

Commit 716179a

Browse files
committed
Migrating to mutations, adding an 'auto' mode.
1 parent 56e46ef commit 716179a

File tree

6 files changed

+103
-30
lines changed

6 files changed

+103
-30
lines changed

Controller/GraphQL/LoginController.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
1212
use Symfony\Component\Security\Core\User\UserProviderInterface;
1313
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
14+
use TheCodingMachine\GraphQLite\Annotations\Mutation;
1415
use TheCodingMachine\GraphQLite\Annotations\Query;
1516

1617
class LoginController
@@ -52,7 +53,7 @@ public function __construct(UserProviderInterface $userProvider, UserPasswordEnc
5253
}
5354

5455
/**
55-
* @Query()
56+
* @Mutation()
5657
*/
5758
public function login(string $userName, string $password, Request $request): bool
5859
{
@@ -86,7 +87,7 @@ public function login(string $userName, string $password, Request $request): boo
8687
}
8788

8889
/**
89-
* @Query()
90+
* @Mutation()
9091
*/
9192
public function logout(): bool
9293
{

DependencyInjection/Configuration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function getConfigTreeBuilder()
3939
->end()
4040
->arrayNode('security')
4141
->children()
42-
->booleanNode('enable_login')->defaultFalse()->info('Set to true to automatically create a login/logout request')->end()
42+
->enumNode('enable_login')->values(['on', 'off', 'auto'])->defaultValue('auto')->info('Enable to automatically create a login/logout mutation. "on": enable, "auto": enable if security bundle is available.')->end()
4343
->scalarNode('firewall_name')->defaultValue('main')->info('The name of the firewall to use for login')->end()
4444
->end()
4545
->end()

DependencyInjection/GraphqliteCompilerPass.php

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
use Symfony\Component\DependencyInjection\ContainerBuilder;
2828
use Symfony\Component\DependencyInjection\Definition;
2929
use Symfony\Component\DependencyInjection\Reference;
30+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
31+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
32+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
33+
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
34+
use Symfony\Component\Security\Core\User\UserProviderInterface;
3035
use TheCodingMachine\CacheUtils\ClassBoundCache;
3136
use TheCodingMachine\CacheUtils\ClassBoundCacheContract;
3237
use TheCodingMachine\CacheUtils\ClassBoundCacheContractInterface;
@@ -82,20 +87,44 @@ public function process(ContainerBuilder $container)
8287
$typesNamespaces = $container->getParameter('graphqlite.namespace.types');
8388

8489
// 2 seconds of TTL in environment mode. Otherwise, let's cache forever!
85-
$env = $container->getParameter('kernel.environment');
86-
$globTtl = null;
87-
if ($env === 'dev') {
88-
$globTtl = 2;
89-
}
9090

9191
$schemaFactory = $container->getDefinition(SchemaFactory::class);
9292

93+
$env = $container->getParameter('kernel.environment');
94+
if ($env === 'prod') {
95+
$schemaFactory->addMethodCall('prodMode');
96+
} elseif ($env === 'dev') {
97+
$schemaFactory->addMethodCall('devMode');
98+
}
99+
100+
$disableLogin = false;
101+
if ($container->getParameter('graphqlite.security.enable_login') === 'auto'
102+
&& (!$container->has(UserProviderInterface::class) ||
103+
!$container->has(UserPasswordEncoderInterface::class) ||
104+
!$container->has(TokenStorageInterface::class) ||
105+
!$container->has(SessionInterface::class)
106+
)) {
107+
$disableLogin = true;
108+
}
109+
if ($container->getParameter('graphqlite.security.enable_login') === 'off') {
110+
$disableLogin = true;
111+
}
93112
// If the security is disabled, let's remove the LoginController
94-
if ($container->getParameter('graphqlite.security.enable_login') === false) {
113+
if ($disableLogin === true) {
95114
$container->removeDefinition(LoginController::class);
96115
$container->removeDefinition(AggregateControllerQueryProviderFactory::class);
97116
}
98117

118+
if ($container->getParameter('graphqlite.security.enable_login') === 'on') {
119+
if (!$container->has(SessionInterface::class)) {
120+
throw new GraphQLException('In order to enable the login/logout mutations (via the graphqlite.security.enable_login parameter), you need to enable session support (via the "framework.session.enabled" config parameter).');
121+
}
122+
if (!$container->has(UserPasswordEncoderInterface::class) || !$container->has(TokenStorageInterface::class) || !$container->has(UserProviderInterface::class)) {
123+
throw new GraphQLException('In order to enable the login/logout mutations (via the graphqlite.security.enable_login parameter), you need to install the security bundle. Please be sure to correctly configure the user provider (in the security.providers configuration settings)');
124+
}
125+
}
126+
127+
99128
foreach ($container->getDefinitions() as $id => $definition) {
100129
if ($definition->isAbstract() || $definition->getClass() === null) {
101130
continue;

DependencyInjection/GraphqliteExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function load(array $configs, ContainerBuilder $container)
5454
$namespaceType = [];
5555
}
5656

57-
$enableLogin = $configs[0]['security']['enable_login'] ?? false;
57+
$enableLogin = $configs[0]['security']['enable_login'] ?? 'auto';
5858

5959
$container->setParameter('graphqlite.namespace.controllers', $namespaceController);
6060
$container->setParameter('graphqlite.namespace.types', $namespaceType);

Tests/FunctionalTest.php

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
use function spl_object_hash;
99
use Symfony\Component\HttpFoundation\Cookie;
1010
use Symfony\Component\HttpFoundation\Request;
11+
use Symfony\Component\HttpFoundation\Session\Session;
12+
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
1113
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
1214
use Symfony\Component\Security\Core\User\User;
1315
use TheCodingMachine\Graphqlite\Bundle\Controller\GraphqliteController;
1416
use TheCodingMachine\Graphqlite\Bundle\Security\AuthenticationService;
17+
use TheCodingMachine\GraphQLite\GraphQLException;
1518
use TheCodingMachine\GraphQLite\Schema;
1619
use function var_dump;
1720

@@ -187,7 +190,7 @@ public function testLoggedMiddleware2(): void
187190

188191
public function testInjectQuery(): void
189192
{
190-
$kernel = new GraphqliteTestingKernel('test', true);
193+
$kernel = new GraphqliteTestingKernel();
191194
$kernel->boot();
192195

193196
$request = Request::create('/graphql', 'GET', ['query' => '
@@ -211,8 +214,12 @@ public function testLoginQuery(): void
211214
$kernel = new GraphqliteTestingKernel();
212215
$kernel->boot();
213216

214-
$request = Request::create('/graphql', 'GET', ['query' => '
215-
{
217+
$session = new Session(new MockArraySessionStorage());
218+
$container = $kernel->getContainer();
219+
$container->set('session', $session);
220+
221+
$request = Request::create('/graphql', 'POST', ['query' => '
222+
mutation login {
216223
login(userName: "foo", password: "bar")
217224
}']);
218225

@@ -229,19 +236,35 @@ public function testLoginQuery(): void
229236

230237
public function testNoLoginNoSessionQuery(): void
231238
{
232-
$kernel = new GraphqliteTestingKernel(false, false);
239+
$kernel = new GraphqliteTestingKernel(false, 'off');
233240
$kernel->boot();
234241

235-
$request = Request::create('/graphql', 'GET', ['query' => '
236-
{
242+
$request = Request::create('/graphql', 'POST', ['query' => '
243+
mutation login {
237244
login(userName: "foo", password: "bar")
238245
}']);
239246

240247
$response = $kernel->handle($request);
241248

242249
$result = json_decode($response->getContent(), true);
243250

244-
$this->assertSame('Cannot query field "login" on type "Query".', $result['errors'][0]['message']);
251+
$this->assertSame('Cannot query field "login" on type "Mutation".', $result['errors'][0]['message']);
252+
}
253+
254+
public function testForceLoginNoSession(): void
255+
{
256+
$kernel = new GraphqliteTestingKernel(false, 'on');
257+
$this->expectException(GraphQLException::class);
258+
$this->expectExceptionMessage('In order to enable the login/logout mutations (via the graphqlite.security.enable_login parameter), you need to enable session support (via the "framework.session.enabled" config parameter)');
259+
$kernel->boot();
260+
}
261+
262+
public function testForceLoginNoSecurity(): void
263+
{
264+
$kernel = new GraphqliteTestingKernel(true, 'on', false);
265+
$this->expectException(GraphQLException::class);
266+
$this->expectExceptionMessage('In order to enable the login/logout mutations (via the graphqlite.security.enable_login parameter), you need to install the security bundle. Please be sure to correctly configure the user provider (in the security.providers configuration settings)');
267+
$kernel->boot();
245268
}
246269

247270
private function logIn(ContainerInterface $container)

Tests/GraphqliteTestingKernel.php

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\HttpKernel\Kernel;
1515
use Symfony\Component\Routing\RouteCollectionBuilder;
1616
use TheCodingMachine\Graphqlite\Bundle\GraphqliteBundle;
17+
use Symfony\Component\Security\Core\User\User;
1718

1819
class GraphqliteTestingKernel extends Kernel
1920
{
@@ -25,15 +26,20 @@ class GraphqliteTestingKernel extends Kernel
2526
*/
2627
private $enableSession;
2728
/**
28-
* @var bool
29+
* @var string
2930
*/
3031
private $enableLogin;
32+
/**
33+
* @var bool
34+
*/
35+
private $enableSecurity;
3136

32-
public function __construct(bool $enableSession = true, bool $enableLogin = true)
37+
public function __construct(bool $enableSession = true, ?string $enableLogin = null, bool $enableSecurity = true)
3338
{
3439
parent::__construct('test', true);
3540
$this->enableSession = $enableSession;
3641
$this->enableLogin = $enableLogin;
42+
$this->enableSecurity = $enableSecurity;
3743
}
3844

3945
public function registerBundles()
@@ -60,16 +66,30 @@ public function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
6066
}
6167

6268
$container->loadFromExtension('framework', $frameworkConf);
63-
$container->loadFromExtension('security', array(
64-
'providers' => [
65-
'in_memory' => ['memory' => null],
66-
],
67-
'firewalls' => [
68-
'main' => [
69-
'anonymous' => true
70-
]
71-
]
72-
));
69+
if ($this->enableSecurity) {
70+
$container->loadFromExtension('security', array(
71+
'providers' => [
72+
'in_memory' => [
73+
'memory' => [
74+
'users' => [
75+
'foo' => [
76+
'password' => 'bar',
77+
'roles' => 'ROLE_USER',
78+
],
79+
],
80+
],
81+
],
82+
],
83+
'firewalls' => [
84+
'main' => [
85+
'anonymous' => true
86+
]
87+
],
88+
'encoders' => [
89+
User::class => 'plaintext',
90+
],
91+
));
92+
}
7393

7494
$graphqliteConf = array(
7595
'namespace' => [
@@ -80,7 +100,7 @@ public function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
80100

81101
if ($this->enableLogin) {
82102
$graphqliteConf['security'] = [
83-
'enable_login' => true,
103+
'enable_login' => $this->enableLogin,
84104
];
85105
}
86106

0 commit comments

Comments
 (0)