Issues (97)

src/Controller/SsoController.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace App\Controller;
4
5
use LightSaml\Idp\Builder\Action\Profile\SingleSignOn\Idp\SsoIdpAssertionActionBuilder;
6
use RuntimeException;
7
use App\Entity\SamlServiceProvider;
8
use App\Entity\User;
9
use App\Repository\ServiceProviderRepositoryInterface;
10
use App\Saml\AttributeValueProvider;
11
use App\Security\Voter\ServiceProviderVoter;
12
use App\Service\ServiceProviderConfirmationService;
13
use LightSaml\Binding\SamlPostResponse;
14
use LightSaml\Build\Container\BuildContainerInterface;
15
use LightSaml\Idp\Builder\Profile\WebBrowserSso\Idp\SsoIdpReceiveAuthnRequestProfileBuilder;
16
use SchulIT\LightSamlIdpBundle\Builder\Profile\WebBrowserSso\Idp\SsoIdpSendResponseProfileBuilderFactory;
17
use SchulIT\LightSamlIdpBundle\RequestStorage\RequestStorageInterface;
18
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
19
use Symfony\Component\HttpFoundation\RedirectResponse;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\Response;
22
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
23
use Symfony\Component\Routing\Annotation\Route;
24
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
25
26
class SsoController extends AbstractController {
27
28
    private const CSRF_TOKEN_ID = '_confirmation_token';
29 1
30 1
    public function __construct(private ServiceProviderConfirmationService $confirmationService)
31 1
    {
32
    }
33
34
    #[Route(path: '/idp/saml', name: 'idp_saml')]
35
    public function saml(RequestStorageInterface $requestStorage, AttributeValueProvider $attributeValueProvider,
36
                         SsoIdpReceiveAuthnRequestProfileBuilder $receiveBuilder,
37
                         SsoIdpSendResponseProfileBuilderFactory $sendResponseBuilder,
38
                         CsrfTokenManagerInterface $tokenManager, ServiceProviderRepositoryInterface $serviceProviderRepository,
39
                         BuildContainerInterface $buildContainer): Response {
40
        if($requestStorage->has() !== true) {
41
            return $this->redirectToRoute('dashboard');
42
        }
43
44
        /** @var User $user */
45
        $user = $this->getUser();
46
47
        if($user->getType()->isCanLinkStudents() && count($user->getLinkedStudents()) === 0) {
48
            $this->addFlash('error', 'sso.error.registration_incomplete');
49
            $requestStorage->clear();
50
            return $this->redirectToRoute('dashboard');
51
        }
52
53
        $requestStorage->load();
54
55
        $context = $receiveBuilder->buildContext();
56
        $action = $receiveBuilder->buildAction();
57
58
        $action->execute($context);
59
60
        $partyContext = $context->getPartyEntityContext();
61
        $endpoint = $context->getEndpoint();
62
        $message = $context->getInboundMessage();
63
64
        // check authorization
65
        $serviceProvider = $serviceProviderRepository
66
            ->findOneByEntityId($partyContext->getEntityId());
67
68
        if($serviceProvider === null || !$serviceProvider instanceof SamlServiceProvider) {
69
            throw new BadRequestHttpException('The issusing service provider does not exist.');
70
        }
71
72
        if(!$this->isGranted(ServiceProviderVoter::ENABLED, $serviceProvider)) {
73
            $requestStorage->clear();
74
75
            return $this->render('sso/denied.html.twig', [
76
                'service' => $serviceProvider
77
            ], new Response(null, Response::HTTP_FORBIDDEN));
78
        }
79
80
        $sendBuilder = $sendResponseBuilder->build(
81
            [new SsoIdpAssertionActionBuilder($buildContainer)],
82
            $partyContext->getEntityDescriptor()->getEntityID()
83
        );
84
        $sendBuilder->setPartyEntityDescriptor($partyContext->getEntityDescriptor());
85
        $sendBuilder->setPartyTrustOptions($partyContext->getTrustOptions());
86
        $sendBuilder->setEndpoint($endpoint);
87
        $sendBuilder->setMessage($message);
88
89
        $context = $sendBuilder->buildContext();
90
        $action = $sendBuilder->buildAction();
91
92
        $action->execute($context);
93
94
        $requestStorage->clear();
95
96
        $response = $context->getHttpResponseContext()->getResponse();
97
98
        $type = $this->confirmationService->needsConfirmation($user, $serviceProvider) ? 'confirm' : 'redirect';
0 ignored issues
show
It seems like $serviceProvider can also be of type null; however, parameter $serviceProvider of App\Service\ServiceProvi...ce::needsConfirmation() does only seem to accept App\Entity\SamlServiceProvider, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

98
        $type = $this->confirmationService->needsConfirmation($user, /** @scrutinizer ignore-type */ $serviceProvider) ? 'confirm' : 'redirect';
Loading history...
99
        $token = $tokenManager->getToken(self::CSRF_TOKEN_ID);
100
101
        if ($response instanceof SamlPostResponse) {
102
            $data = $response->getData();
103
            $destination = $response->getDestination();
104
            $attributes = $attributeValueProvider->getValuesForUser($this->getUser(), $serviceProvider->getEntityId());
105
106
            return $this->render('sso/' . $type . '_post.html.twig', [
107
                'service' => $serviceProvider,
108
                'data' => $data,
109
                'destination' => $destination,
110
                'attributes' => $attributes,
111
                'csrf_token' => $token->getValue()
112
            ]);
113
        } else if ($response instanceof RedirectResponse) {
114
            $destination = $response->getTargetUrl();
115
116
            $attributes = $attributeValueProvider->getValuesForUser($this->getUser(), $serviceProvider->getEntityId());
117
118
            return $this->render('sso/' . $type . '_uri.html.twig', [
119
                'service' => $serviceProvider,
120
                'destination' => $destination,
121
                'attributes' => $attributes,
122
                'csrf_token' => $token->getValue()
123
            ]);
124
        }
125
126
        throw new RuntimeException('Unsupported Binding!');
127
    }
128
129
    #[Route(path: '/idp/saml/confirm/{uuid}', name: 'confirm_redirect')]
130
    public function confirm(Request $request, SamlServiceProvider $serviceProvider, AttributeValueProvider $attributeValueProvider, CsrfTokenManagerInterface $tokenManager): Response {
131
        $type = $request->request->get('type');
132
        $destination = $request->request->get('destination');
133
        $data = $request->request->all('data');
134
        $token = $request->request->get('_csrf_token');
135
136
        if($this->isCsrfTokenValid(self::CSRF_TOKEN_ID, $token) !== true) {
137
            $token = $tokenManager->refreshToken(self::CSRF_TOKEN_ID);
138
139
            if ($type === 'post') {
140
                $attributes = $attributeValueProvider->getValuesForUser($this->getUser(), $serviceProvider->getEntityId());
141
142
                return $this->render('sso/confirm_post.html.twig', [
143
                    'service' => $serviceProvider,
144
                    'data' => $data,
145
                    'destination' => $destination,
146
                    'attributes' => $attributes,
147
                    'csrf_token' => $token->getValue()
148
                ]);
149
            } else if ($type === 'redirect') {
150
                $attributes = $attributeValueProvider->getValuesForUser($this->getUser(), $serviceProvider->getEntityId());
151
152
                return $this->render('sso/confirm_uri.html.twig', [
153
                    'service' => $serviceProvider,
154
                    'destination' => $destination,
155
                    'attributes' => $attributes,
156
                    'csrf_token' => $token->getValue()
157
                ]);
158
            }
159
        } else {
160
            /** @var User $user */
161
            $user = $this->getUser();
162
            $this->confirmationService->saveConfirmation($user, $serviceProvider);
163
164
            if($type === 'post') {
165
                return $this->render('sso/redirect_post.html.twig', [
166
                    'service' => $serviceProvider,
167
                    'data' => $data,
168
                    'destination' => $destination
169
                ]);
170
            } else if($type === 'redirect') {
171
                $token = $tokenManager->refreshToken(self::CSRF_TOKEN_ID);
172
                return $this->render('sso/redirect_uri.html.twig', [
173
                    'service' => $serviceProvider,
174
                    'destination' => $destination,
175
                    'csrf_token' => $token->getValue()
176
                ]);
177
            }
178
        }
179
180
        return $this->redirectToRoute('dashboard');
181
    }
182
}