Processor::processAssertions()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\Assertion;
6
7
use Psr\Log\LoggerInterface;
8
use SimpleSAML\SAML2\Assertion\Exception\InvalidAssertionException;
9
use SimpleSAML\SAML2\Assertion\Exception\InvalidSubjectConfirmationException;
10
use SimpleSAML\SAML2\Assertion\Transformer\TransformerInterface;
11
use SimpleSAML\SAML2\Assertion\Validation\AssertionValidator;
12
use SimpleSAML\SAML2\Assertion\Validation\SubjectConfirmationValidator;
13
use SimpleSAML\SAML2\Configuration\IdentityProvider;
14
use SimpleSAML\SAML2\Response\Exception\InvalidSignatureException;
15
use SimpleSAML\SAML2\Signature\Validator;
16
use SimpleSAML\SAML2\Utilities\ArrayCollection;
17
use SimpleSAML\SAML2\XML\saml\Assertion;
18
use SimpleSAML\SAML2\XML\saml\EncryptedAssertion;
19
20
use function implode;
21
use function sprintf;
22
23
class Processor
24
{
25
    /**
26
     * @param \SimpleSAML\SAML2\Assertion\Decrypter $decrypter
27
     * @param \SimpleSAML\SAML2\Signature\Validator $signatureValidator
28
     * @param \SimpleSAML\SAML2\Assertion\Validation\AssertionValidator $assertionValidator
29
     * @param \SimpleSAML\SAML2\Assertion\Validation\SubjectConfirmationValidator $subjectConfirmationValidator
30
     * @param \SimpleSAML\SAML2\Assertion\Transformer\TransformerInterface $transformer
31
     * @param \SimpleSAML\SAML2\Configuration\IdentityProvider $identityProviderConfiguration
32
     * @param \Psr\Log\LoggerInterface $logger
33
     */
34
    public function __construct(
35
        private Decrypter $decrypter,
36
        private Validator $signatureValidator,
37
        private AssertionValidator $assertionValidator,
38
        private SubjectConfirmationValidator $subjectConfirmationValidator,
39
        private TransformerInterface $transformer,
40
        private IdentityProvider $identityProviderConfiguration,
41
        private LoggerInterface $logger,
42
    ) {
43
    }
44
45
46
    /**
47
     * Decrypt assertions, or do nothing if assertions are already decrypted.
48
     *
49
     * @param \SimpleSAML\SAML2\Utilities\ArrayCollection $assertions
50
     * @return \SimpleSAML\SAML2\Utilities\ArrayCollection Collection of processed assertions
51
     */
52
    public function decryptAssertions(ArrayCollection $assertions): ArrayCollection
53
    {
54
        $decrypted = new ArrayCollection();
55
        foreach ($assertions->getIterator() as $assertion) {
56
            if ($assertion instanceof EncryptedAssertion) {
57
                $decrypted->add($this->decryptAssertion($assertion));
58
            } elseif ($assertion instanceof Assertion) {
59
                $decrypted->add($assertion);
60
            } else {
61
                throw new InvalidAssertionException('The assertion must be of type: EncryptedAssertion or Assertion');
62
            }
63
        }
64
65
        return $decrypted;
66
    }
67
68
69
    /**
70
     * @param \SimpleSAML\SAML2\Utilities\ArrayCollection $assertions Collection of decrypted assertions
71
     * @return \SimpleSAML\SAML2\Utilities\ArrayCollection Collection of processed assertions
72
     */
73
    public function processAssertions(ArrayCollection $assertions): ArrayCollection
74
    {
75
        $processed = new ArrayCollection();
76
        foreach ($assertions->getIterator() as $assertion) {
77
            $processed->add($this->process($assertion));
78
        }
79
80
        return $processed;
81
    }
82
83
84
    /**
85
     * @param \SimpleSAML\SAML2\XML\saml\Assertion $assertion
86
     * @return \SimpleSAML\SAML2\XML\saml\Assertion
87
     */
88
    public function process(Assertion $assertion): Assertion
89
    {
90
        if (!$assertion->wasSignedAtConstruction()) {
91
            $this->logger->info(sprintf(
92
                'Assertion with id "%s" was not signed at construction, not verifying the signature',
93
                $assertion->getId(),
94
            ));
95
        } else {
96
            $this->logger->info(sprintf('Verifying signature of Assertion with id "%s"', $assertion->getId()));
97
98
            if (!$this->signatureValidator->hasValidSignature($assertion, $this->identityProviderConfiguration)) {
99
                throw new InvalidSignatureException(
100
                    sprintf('The assertion with id "%s" does not have a valid signature', $assertion->getId()),
101
                );
102
            }
103
        }
104
105
        $this->validateAssertion($assertion);
106
107
        $assertion = $this->transformAssertion($assertion);
108
109
        return $assertion;
110
    }
111
112
113
    /**
114
     * @param \SimpleSAML\SAML2\XML\saml\EncryptedAssertion $assertion
115
     * @return \SimpleSAML\SAML2\XML\saml\Assertion
116
     */
117
    private function decryptAssertion(EncryptedAssertion $assertion): Assertion
118
    {
119
        return $this->decrypter->decrypt($assertion);
120
    }
121
122
123
    /**
124
     * @param \SimpleSAML\SAML2\XML\saml\Assertion $assertion
125
     */
126
    public function validateAssertion(Assertion $assertion): void
127
    {
128
        $assertionValidationResult = $this->assertionValidator->validate($assertion);
129
        if (!$assertionValidationResult->isValid()) {
130
            throw new InvalidAssertionException(sprintf(
131
                'Invalid Assertion in SAML Response, errors: "%s"',
132
                implode('", "', $assertionValidationResult->getErrors()),
133
            ));
134
        }
135
136
        $subject = $assertion->getSubject();
137
        if ($subject !== null) {
138
            foreach ($subject->getSubjectConfirmation() as $subjectConfirmation) {
139
                $subjectConfirmationValidationResult = $this->subjectConfirmationValidator->validate(
140
                    $subjectConfirmation,
141
                );
142
                if (!$subjectConfirmationValidationResult->isValid()) {
143
                    throw new InvalidSubjectConfirmationException(sprintf(
144
                        'Invalid SubjectConfirmation in Assertion, errors: "%s"',
145
                        implode('", "', $subjectConfirmationValidationResult->getErrors()),
146
                    ));
147
                }
148
            }
149
        }
150
    }
151
152
153
    /**
154
     * @param \SimpleSAML\SAML2\XML\saml\Assertion $assertion
155
     * @return \SimpleSAML\SAML2\XML\saml\Assertion
156
     */
157
    private function transformAssertion(Assertion $assertion): Assertion
158
    {
159
        return $this->transformer->transform($assertion);
160
    }
161
}
162