Completed
Push — master ( 0c2aa0...b1edbb )
by Michiel
03:00 queued 10s
created

CommandController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.7666
c 0
b 0
f 0
cc 1
nc 1
nop 6
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
/**
38
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
39
 */
40
class CommandController extends Controller
41
{
42
    /**
43
     * @var WhitelistService
44
     */
45
    private $whitelistService;
46
47
    /**
48
     * @var IdentityService
49
     */
50
    private $identityService;
51
52
    /**
53
     * @var TransactionAwarePipeline
54
     */
55
    private $pipeline;
56
57
    /**
58
     * @var MetadataEnricher
59
     */
60
    private $metadataEnricher;
61
62
    /**
63
     * @var AuthorizationChecker
64
     */
65
    private $authorizationChecker;
66
67
    /**
68
     * @var LoggerInterface
69
     */
70
    private $logger;
71
72
    public function __construct(
73
        TransactionAwarePipeline $pipeline,
74
        WhitelistService $whitelistService,
75
        IdentityService $identityService,
76
        MetadataEnricher $enricher,
77
        AuthorizationChecker $authorizationChecker,
78
        LoggerInterface $logger
79
    ) {
80
        $this->pipeline = $pipeline;
81
        $this->whitelistService = $whitelistService;
82
        $this->identityService = $identityService;
83
        $this->authorizationChecker = $authorizationChecker;
84
        $this->metadataEnricher = $enricher;
85
        $this->logger = $logger;
86
    }
87
88
    public function handleAction(Command $command, Metadata $metadata, Request $request)
89
    {
90
        $this->denyAccessUnlessGranted(['ROLE_RA', 'ROLE_SS']);
91
92
        $logger = $this->logger;
93
        $logger->notice(sprintf('Received request to process Command "%s"', $command));
94
95
        $this->metadataEnricher->setMetadata($metadata);
96
97
        if ($this->authorizationChecker->isGranted('ROLE_MANAGEMENT')) {
98
            $logger->notice('Command sent through Management API, not enforcing Whitelist');
99
        } else {
100
            $logger->notice('Ensuring that the actor institution is on the whitelist, or the actor is SRAA');
101
102
            $institution = $this->resolveInstitution($command, $metadata);
103
            $this->assertCommandMayBeProcessedOnBehalfOf($institution, $metadata->actorId);
104
        }
105
106
        try {
107
            $command = $this->pipeline->process($command);
108
        } catch (ForbiddenException $e) {
109
            throw new AccessDeniedHttpException(
110
                sprintf('Processing of command "%s" is forbidden for this client', $command),
111
                $e
112
            );
113
        }
114
115
        $serverName = $request->server->get('SERVER_NAME') ?: $request->server->get('SERVER_ADDR');
116
        $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...
117
118
        $logger->notice(sprintf('Command "%s" has been successfully processed', $command));
119
120
        return $response;
121
    }
122
123
    /**
124
     * @param Command  $command
125
     * @param Metadata $metadata
126
     * @return string
127
     */
128
    private function resolveInstitution(Command $command, Metadata $metadata)
129
    {
130
        if ($metadata->actorInstitution) {
131
            return $metadata->actorInstitution;
132
        }
133
134
        // the createIdentityCommand is used to create an Identity for a new user,
135
        // the updateIdentityCommand is used to update name or email of an identity
136
        // Both are only sent by the SS when the Identity is not logged in yet,
137
        // thus there is not Metadata::actorInstitution,
138
        if ($command instanceof CreateIdentityCommand || $command instanceof UpdateIdentityCommand) {
139
            return $command->institution;
140
        }
141
142
        // conservative, if we cannot determine an institution, deny processing.
143
        throw new AccessDeniedHttpException(
144
            'Cannot reliably determine the institution of the actor, denying processing of command'
145
        );
146
    }
147
148
    /**
149
     * @param string      $institution
150
     * @param string|null $actorId
151
     */
152
    private function assertCommandMayBeProcessedOnBehalfOf($institution, $actorId)
153
    {
154
        if ($this->whitelistService->isWhitelisted($institution)) {
155
            return;
156
        }
157
158
        if (!$actorId) {
159
            throw new AccessDeniedHttpException(sprintf(
160
                'Institution "%s" is not on the whitelist and no actor is found, processing of command denied',
161
                $institution
162
            ));
163
        }
164
165
        $registrationAuthorityCredentials = $this->identityService->findRegistrationAuthorityCredentialsOf($actorId);
166
167
        if ($registrationAuthorityCredentials->isSraa()) {
168
            return;
169
        }
170
171
        throw new AccessDeniedHttpException(sprintf(
172
            'Institution "%s" is not on the whitelist and actor "%s" is not an SRAA, processing of command denied',
173
            $institution,
174
            $actorId
175
        ));
176
    }
177
}
178