Completed
Push — feature/caas ( c7df64...f05503 )
by Michiel
03:29
created

CommandController::resolveInstitution()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
cc 4
nc 3
nop 2
1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace Surfnet\StepupMiddleware\ApiBundle\Controller;
20
21
use Psr\Log\LoggerInterface;
22
use Surfnet\StepupMiddleware\ApiBundle\Identity\Service\IdentityService;
23
use Surfnet\StepupMiddleware\ApiBundle\Identity\Service\WhitelistService;
24
use Surfnet\StepupMiddleware\CommandHandlingBundle\Command\Command;
25
use Surfnet\StepupMiddleware\CommandHandlingBundle\Command\Metadata;
26
use Surfnet\StepupMiddleware\CommandHandlingBundle\EventSourcing\MetadataEnricher;
27
use Surfnet\StepupMiddleware\CommandHandlingBundle\Exception\ForbiddenException;
28
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\CreateIdentityCommand;
29
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\UpdateIdentityCommand;
30
use Surfnet\StepupMiddleware\CommandHandlingBundle\Pipeline\TransactionAwarePipeline;
31
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
32
use Symfony\Component\HttpFoundation\JsonResponse;
33
use Symfony\Component\HttpFoundation\Request;
34
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
35
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
36
37
class CommandController extends Controller
38
{
39
    /**
40
     * @var WhitelistService
41
     */
42
    private $whitelistService;
43
44
    /**
45
     * @var IdentityService
46
     */
47
    private $identityService;
48
49
    /**
50
     * @var TransactionAwarePipeline
51
     */
52
    private $pipeline;
53
54
    /**
55
     * @var MetadataEnricher
56
     */
57
    private $metadataEnricher;
58
59
    /**
60
     * @var AuthorizationChecker
61
     */
62
    private $authorizationChecker;
63
64
    /**
65
     * @var LoggerInterface
66
     */
67
    private $logger;
68
69
    public function __construct(
70
        TransactionAwarePipeline $pipeline,
71
        WhitelistService $whitelistService,
72
        IdentityService $identityService,
73
        MetadataEnricher $enricher,
74
        AuthorizationChecker $authorizationChecker,
75
        LoggerInterface $logger
76
    ) {
77
        $this->pipeline = $pipeline;
78
        $this->whitelistService = $whitelistService;
79
        $this->identityService = $identityService;
80
        $this->authorizationChecker = $authorizationChecker;
81
        $this->metadataEnricher = $enricher;
82
        $this->logger = $logger;
83
    }
84
85
    public function handleAction(Command $command, Metadata $metadata, Request $request)
86
    {
87
        $this->denyAccessUnlessGranted(['ROLE_RA', 'ROLE_SS']);
88
89
        $logger = $this->logger;
90
        $logger->notice(sprintf('Received request to process Command "%s"', $command));
91
92
        $this->metadataEnricher->setMetadata($metadata);
93
94
        if ($this->authorizationChecker->isGranted('ROLE_MANAGEMENT')) {
95
            $logger->notice('Command sent through Management API, not enforcing Whitelist');
96
        } else {
97
            $logger->notice('Ensuring that the actor institution is on the whitelist, or the actor is SRAA');
98
99
            $institution = $this->resolveInstitution($command, $metadata);
100
            $this->assertCommandMayBeProcessedOnBehalfOf($institution, $metadata->actorId);
101
        }
102
103
        try {
104
            $command = $this->pipeline->process($command);
105
        } catch (ForbiddenException $e) {
106
            throw new AccessDeniedHttpException(
107
                sprintf('Processing of command "%s" is forbidden for this client', $command),
108
                $e
109
            );
110
        }
111
112
        $serverName = $request->server->get('SERVER_NAME') ?: $request->server->get('SERVER_ADDR');
113
        $response = new JsonResponse(['command' => $command->UUID, 'processed_by' => $serverName]);
0 ignored issues
show
Bug introduced by
Accessing UUID on the interface Surfnet\StepupMiddleware...gBundle\Command\Command suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
114
115
        $logger->notice(sprintf('Command "%s" has been successfully processed', $command));
116
117
        return $response;
118
    }
119
120
    /**
121
     * @param Command  $command
122
     * @param Metadata $metadata
123
     * @return string
124
     */
125
    private function resolveInstitution(Command $command, Metadata $metadata)
126
    {
127
        if ($metadata->actorInstitution) {
128
            return $metadata->actorInstitution;
129
        }
130
131
        // the createIdentityCommand is used to create an Identity for a new user,
132
        // the updateIdentityCommand is used to update name or email of an identity
133
        // Both are only sent by the SS when the Identity is not logged in yet,
134
        // thus there is not Metadata::actorInstitution,
135
        if ($command instanceof CreateIdentityCommand || $command instanceof UpdateIdentityCommand) {
136
            return $command->institution;
137
        }
138
139
        // conservative, if we cannot determine an institution, deny processing.
140
        throw new AccessDeniedHttpException(
141
            'Cannot reliably determine the institution of the actor, denying processing of command'
142
        );
143
    }
144
145
    /**
146
     * @param string      $institution
147
     * @param string|null $actorId
148
     */
149
    private function assertCommandMayBeProcessedOnBehalfOf($institution, $actorId)
150
    {
151
        if ($this->whitelistService->isWhitelisted($institution)) {
152
            return;
153
        }
154
155
        if (!$actorId) {
156
            throw new AccessDeniedHttpException(sprintf(
157
                'Institution "%s" is not on the whitelist and no actor is found, processing of command denied',
158
                $institution
159
            ));
160
        }
161
162
        $registrationAuthorityCredentials = $this->identityService->findRegistrationAuthorityCredentialsOf($actorId);
163
164
        if ($registrationAuthorityCredentials->isSraa()) {
165
            return;
166
        }
167
168
        throw new AccessDeniedHttpException(sprintf(
169
            'Institution "%s" is not on the whitelist and actor "%s" is not an SRAA, processing of command denied',
170
            $institution,
171
            $actorId
172
        ));
173
    }
174
}
175