RepeatedIdValidatorAction   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 92.45%

Importance

Changes 0
Metric Value
dl 0
loc 112
c 0
b 0
f 0
wmc 15
lcom 1
cbo 6
ccs 49
cts 53
cp 0.9245
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A doExecute() 0 6 2
A validateBearerAssertion() 0 33 4
B getIdExpiryTime() 0 36 8
1
<?php
2
3
/*
4
 * This file is part of the LightSAML-Core package.
5
 *
6
 * (c) Milos Tomic <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace LightSaml\Action\Assertion\Inbound;
13
14
use LightSaml\Action\Assertion\AbstractAssertionAction;
15
use LightSaml\Context\Profile\AssertionContext;
16
use LightSaml\Context\Profile\Helper\LogHelper;
17
use LightSaml\Error\LightSamlContextException;
18
use LightSaml\Store\Id\IdStoreInterface;
19
use Psr\Log\LoggerInterface;
20
21
/**
22
 * 4.1.4.5  POST-Specific Processing Rules
23
 * The service provider MUST ensure that bearer assertions are not replayed, by maintaining the set of used
24
 * ID values for the length of time for which the assertion would be considered valid based on the
25
 * NotOnOrAfter attribute in the <SubjectConfirmationData>.
26
 */
27
class RepeatedIdValidatorAction extends AbstractAssertionAction
28
{
29
    /** @var IdStoreInterface */
30
    protected $idStore;
31
32
    /**
33
     * @param LoggerInterface  $logger
34
     * @param IdStoreInterface $idStore
35
     */
36 9
    public function __construct(LoggerInterface $logger, IdStoreInterface $idStore)
37
    {
38 9
        parent::__construct($logger);
39
40 9
        $this->idStore = $idStore;
41 9
    }
42
43
    /**
44
     * @param AssertionContext $context
45
     *
46
     * @return void
47
     */
48 8
    protected function doExecute(AssertionContext $context)
49
    {
50 8
        if ($context->getAssertion()->hasBearerSubject()) {
51 7
            $this->validateBearerAssertion($context);
52
        }
53 3
    }
54
55
    /**
56
     * @param AssertionContext $context
57
     *
58
     * @throws \LightSaml\Error\LightSamlContextException
59
     */
60 7
    protected function validateBearerAssertion(AssertionContext $context)
61
    {
62 7
        if (null == $context->getAssertion()->getId()) {
63 1
            $message = 'Bearer Assertion must have ID attribute';
64 1
            $this->logger->error($message, LogHelper::getActionErrorContext($context, $this));
65 1
            throw new LightSamlContextException($context, $message);
66
        }
67
68 6
        if (null == $context->getAssertion()->getIssuer()) {
69 1
            $message = 'Bearer Assertion must have Issuer element';
70 1
            $this->logger->error($message, LogHelper::getActionErrorContext($context, $this));
71 1
            throw new LightSamlContextException($context, $message);
72
        }
73
74 5
        if ($this->idStore->has($context->getAssertion()->getIssuer()->getValue(), $context->getAssertion()->getId())) {
75 1
            $message = sprintf(
76 1
                "Repeated assertion id '%s' of issuer '%s'",
77 1
                $context->getAssertion()->getId(),
78 1
                $context->getAssertion()->getIssuer()->getValue()
79
            );
80 1
            $this->logger->error($message, LogHelper::getActionErrorContext($context, $this, [
81 1
                'id' => $context->getAssertion()->getId(),
82 1
                'issuer' => $context->getAssertion()->getIssuer()->getValue(),
83
            ]));
84 1
            throw new LightSamlContextException($context, $message);
85
        }
86
87 4
        $this->idStore->set(
88 4
            $context->getAssertion()->getIssuer()->getValue(),
89 4
            $context->getAssertion()->getId(),
90 4
            $this->getIdExpiryTime($context)
91
        );
92 2
    }
93
94
    /**
95
     * @param AssertionContext $context
96
     *
97
     * @throws \LogicException
98
     * @throws \LightSaml\Error\LightSamlValidationException
99
     *
100
     * @return \DateTime
101
     */
102 4
    protected function getIdExpiryTime(AssertionContext $context)
103
    {
104
        /** @var \DateTime $result */
105 4
        $result = null;
106 4
        $bearerConfirmations = $context->getAssertion()->getSubject()->getBearerConfirmations();
107 4
        if (null == $bearerConfirmations) {
108
            throw new \LogicException('Bearer assertion must have bearer subject confirmations');
109
        }
110
111 4
        foreach ($bearerConfirmations as $subjectConfirmation) {
112 4
            if (null == $subjectConfirmation->getSubjectConfirmationData()) {
113 1
                $message = 'Bearer SubjectConfirmation must have SubjectConfirmationData element';
114 1
                $this->logger->error($message, LogHelper::getActionErrorContext($context, $this));
115 1
                throw new LightSamlContextException($context, $message);
116
            }
117
118 3
            $dt = $subjectConfirmation->getSubjectConfirmationData()->getNotOnOrAfterDateTime();
119 3
            if (null == $dt) {
120 1
                $message = 'Bearer SubjectConfirmation must have NotOnOrAfter attribute';
121 1
                $this->logger->error($message, LogHelper::getActionErrorContext($context, $this));
122 1
                throw new LightSamlContextException($context, $message);
123
            }
124
125 2
            if (null == $result || $result->getTimestamp() < $dt->getTimestamp()) {
126 2
                $result = $dt;
127
            }
128
        }
129
130 2
        if (null == $result) {
131
            $message = 'Unable to find NotOnOrAfter attribute in bearer assertion';
132
            $this->logger->error($message, LogHelper::getActionErrorContext($context, $this));
133
            throw new LightSamlContextException($context, $message);
134
        }
135
136 2
        return $result;
137
    }
138
}
139