Completed
Push — bugfix/enforce_raa ( 1a8a7b...35dd82 )
by
unknown
02:31
created

CommandController::isSraa()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
c 0
b 0
f 0
rs 9.8333
cc 3
nc 3
nop 1
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\Stepup\Identity\Value\IdentityId;
23
use Surfnet\Stepup\Identity\Value\Institution;
24
use Surfnet\StepupMiddleware\ApiBundle\Authorization\Service\IdentityAuthorizationService;
25
use Surfnet\StepupMiddleware\ApiBundle\Identity\Service\WhitelistService;
26
use Surfnet\StepupMiddleware\CommandHandlingBundle\Command\Command;
27
use Surfnet\StepupMiddleware\CommandHandlingBundle\Command\Metadata;
28
use Surfnet\StepupMiddleware\CommandHandlingBundle\EventSourcing\MetadataEnricher;
29
use Surfnet\StepupMiddleware\CommandHandlingBundle\Exception\ForbiddenException;
30
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\CreateIdentityCommand;
31
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\UpdateIdentityCommand;
32
use Surfnet\StepupMiddleware\CommandHandlingBundle\Pipeline\TransactionAwarePipeline;
33
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
34
use Symfony\Component\HttpFoundation\JsonResponse;
35
use Symfony\Component\HttpFoundation\Request;
36
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
37
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
38
39
/**
40
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
41
 */
42
class CommandController extends Controller
43
{
44
    /**
45
     * @var WhitelistService
46
     */
47
    private $whitelistService;
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
    /**
70
     * @var IdentityAuthorizationService
71
     */
72
    private $identityAuthorizationService;
73
74
75
    public function __construct(
76
        TransactionAwarePipeline $pipeline,
77
        WhitelistService $whitelistService,
78
        MetadataEnricher $enricher,
79
        AuthorizationChecker $authorizationChecker,
80
        LoggerInterface $logger,
81
        IdentityAuthorizationService $identityAuthorizationService
82
    ) {
83
        $this->pipeline = $pipeline;
84
        $this->whitelistService = $whitelistService;
85
        $this->authorizationChecker = $authorizationChecker;
86
        $this->metadataEnricher = $enricher;
87
        $this->logger = $logger;
88
        $this->identityAuthorizationService = $identityAuthorizationService;
89
    }
90
91
    public function handleAction(Command $command, Metadata $metadata, Request $request)
92
    {
93
        $this->denyAccessUnlessGranted(['ROLE_RA', 'ROLE_SS']);
94
95
        $logger = $this->logger;
96
        $logger->notice(sprintf('Received request to process Command "%s"', $command));
97
98
        $this->metadataEnricher->setMetadata($metadata);
99
100
        if ($this->authorizationChecker->isGranted('ROLE_MANAGEMENT')) {
101
            $logger->notice('Command sent through Management API, not enforcing Whitelist');
102
        } else {
103
            $logger->notice('Ensuring that the actor institution is on the whitelist, or the actor is SRAA');
104
105
            $institution = $this->resolveInstitution($command, $metadata);
106
            $this->identityAuthorizationService->assertInstitutionIsWhitelisted(
107
                $institution,
108
                new IdentityId($metadata->actorId)
109
            );
110
111
            $logger->notice('Ensuring that the actor is allowed to execute a command based on the fine grained authorization configuration');
112
113
            $this->identityAuthorizationService->assertCommandMayBeExecutedOnBehalfOf(
114
                new IdentityId($metadata->actorId),
115
                new Institution($metadata->actorInstitution),
116
                $command
117
            );
118
119
            $logger->notice('Command authorization succeeded');
120
        }
121
122
        try {
123
            $command = $this->pipeline->process($command);
124
        } catch (ForbiddenException $e) {
125
            throw new AccessDeniedHttpException(
126
                sprintf('Processing of command "%s" is forbidden for this client', $command),
127
                $e
128
            );
129
        }
130
131
        $serverName = $request->server->get('SERVER_NAME') ?: $request->server->get('SERVER_ADDR');
132
        $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...
133
134
        $logger->notice(sprintf('Command "%s" has been successfully processed', $command));
135
136
        return $response;
137
    }
138
139
    /**
140
     * @param Command $command
141
     * @param Metadata $metadata
142
     * @return Institution
143
     */
144
    private function resolveInstitution(Command $command, Metadata $metadata)
145
    {
146
        if ($metadata->actorInstitution) {
147
            return new Institution($metadata->actorInstitution);
148
        }
149
150
        // the createIdentityCommand is used to create an Identity for a new user,
151
        // the updateIdentityCommand is used to update name or email of an identity
152
        // Both are only sent by the SS when the Identity is not logged in yet,
153
        // thus there is not Metadata::actorInstitution,
154
        if ($command instanceof CreateIdentityCommand || $command instanceof UpdateIdentityCommand) {
155
            return new Institution($command->institution);
156
        }
157
158
        // conservative, if we cannot determine an institution, deny processing.
159
        throw new AccessDeniedHttpException(
160
            'Cannot reliably determine the institution of the actor, denying processing of command'
161
        );
162
    }
163
164
}
165