AssertionValidator   A
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 42
eloc 67
c 1
b 0
f 0
dl 0
loc 158
rs 9.0399

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A validateAssertion() 0 6 1
B validateConditions() 0 25 8
B validateAssertionAttributes() 0 21 7
A validateStatements() 0 8 3
A validateConditionsInterval() 0 7 4
B validateProxyRestriction() 0 10 8
A validateSubject() 0 13 6
A validateAudienceRestriction() 0 9 4

How to fix   Complexity   

Complex Class

Complex classes like AssertionValidator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AssertionValidator, and based on these observations, apply Extract Interface, too.

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\Validator\Model\Assertion;
13
14
use LightSaml\Error\LightSamlValidationException;
15
use LightSaml\Helper;
16
use LightSaml\Model\Assertion\Assertion;
17
use LightSaml\Model\Assertion\AttributeStatement;
18
use LightSaml\Model\Assertion\AudienceRestriction;
19
use LightSaml\Model\Assertion\AuthnStatement;
20
use LightSaml\Model\Assertion\Conditions;
21
use LightSaml\Model\Assertion\OneTimeUse;
22
use LightSaml\Model\Assertion\ProxyRestriction;
23
use LightSaml\SamlConstants;
24
use LightSaml\Validator\Model\NameId\NameIdValidatorInterface;
25
use LightSaml\Validator\Model\Statement\StatementValidatorInterface;
26
use LightSaml\Validator\Model\Subject\SubjectValidatorInterface;
27
28
class AssertionValidator implements AssertionValidatorInterface
29
{
30
    /** @var NameIdValidatorInterface */
31
    protected $nameIdValidator;
32
33
    /** @var SubjectValidatorInterface */
34
    protected $subjectValidator;
35
36
    /** @var StatementValidatorInterface */
37
    protected $statementValidator;
38
39
    public function __construct(
40
        NameIdValidatorInterface $nameIdValidator,
41
        SubjectValidatorInterface $subjectValidator,
42
        StatementValidatorInterface $statementValidator
43
    ) {
44
        $this->nameIdValidator = $nameIdValidator;
45
        $this->subjectValidator = $subjectValidator;
46
        $this->statementValidator = $statementValidator;
47
    }
48
49
    /**
50
     * @return void
51
     */
52
    public function validateAssertion(Assertion $assertion)
53
    {
54
        $this->validateAssertionAttributes($assertion);
55
        $this->validateSubject($assertion);
56
        $this->validateConditions($assertion);
57
        $this->validateStatements($assertion);
58
    }
59
60
    /**
61
     * @throws LightSamlValidationException
62
     */
63
    protected function validateAssertionAttributes(Assertion $assertion)
64
    {
65
        if (false == Helper::validateRequiredString($assertion->getVersion())) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
66
            throw new LightSamlValidationException('Assertion element must have the Version attribute set.');
67
        }
68
        if (SamlConstants::VERSION_20 != $assertion->getVersion()) {
69
            throw new LightSamlValidationException('Assertion element must have the Version attribute value equal to 2.0.');
70
        }
71
        if (false == Helper::validateRequiredString($assertion->getId())) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
72
            throw new LightSamlValidationException('Assertion element must have the ID attribute set.');
73
        }
74
        if (false == Helper::validateIdString($assertion->getId())) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
75
            throw new LightSamlValidationException('Assertion element must have an ID attribute with at least 16 characters (the equivalent of 128 bits).');
76
        }
77
        if (false == $assertion->getIssueInstantTimestamp()) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $assertion->getIssueInstantTimestamp() of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
78
            throw new LightSamlValidationException('Assertion element must have the IssueInstant attribute set.');
79
        }
80
        if (false == $assertion->getIssuer()) {
0 ignored issues
show
introduced by
The condition false == $assertion->getIssuer() is always false.
Loading history...
81
            throw new LightSamlValidationException('Assertion element must have an issuer element.');
82
        }
83
        $this->nameIdValidator->validateNameId($assertion->getIssuer());
84
    }
85
86
    /**
87
     * @throws LightSamlValidationException
88
     */
89
    protected function validateSubject(Assertion $assertion)
90
    {
91
        if (false == $assertion->getSubject()) {
0 ignored issues
show
introduced by
The condition false == $assertion->getSubject() is always false.
Loading history...
92
            if (false == $assertion->getAllItems()) {
93
                throw new LightSamlValidationException('Assertion with no Statements must have a subject.');
94
            }
95
            foreach ($assertion->getAllItems() as $item) {
96
                if ($item instanceof AuthnStatement || $item instanceof AttributeStatement) {
97
                    throw new LightSamlValidationException('AuthnStatement, AuthzDecisionStatement and AttributeStatement require a subject.');
98
                }
99
            }
100
        } else {
101
            $this->subjectValidator->validateSubject($assertion->getSubject());
102
        }
103
    }
104
105
    protected function validateConditions(Assertion $assertion)
106
    {
107
        if (false == $assertion->getConditions()) {
108
            return;
109
        }
110
111
        $this->validateConditionsInterval($assertion->getConditions());
112
113
        $oneTimeUseSeen = $proxyRestrictionSeen = false;
114
115
        foreach ($assertion->getConditions()->getAllItems() as $item) {
116
            if ($item instanceof OneTimeUse) {
117
                if ($oneTimeUseSeen) {
118
                    throw new LightSamlValidationException('Assertion contained more than one condition of type OneTimeUse');
119
                }
120
                $oneTimeUseSeen = true;
121
            } elseif ($item instanceof ProxyRestriction) {
122
                if ($proxyRestrictionSeen) {
123
                    throw new LightSamlValidationException('Assertion contained more than one condition of type ProxyRestriction');
124
                }
125
                $proxyRestrictionSeen = true;
126
127
                $this->validateProxyRestriction($item);
128
            } elseif ($item instanceof AudienceRestriction) {
129
                $this->validateAudienceRestriction($item);
130
            }
131
        }
132
    }
133
134
    protected function validateConditionsInterval(Conditions $conditions)
135
    {
136
        if ($conditions->getNotBeforeTimestamp() &&
0 ignored issues
show
Bug Best Practice introduced by
The expression $conditions->getNotBeforeTimestamp() of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
137
            $conditions->getNotOnOrAfterTimestamp() &&
0 ignored issues
show
Bug Best Practice introduced by
The expression $conditions->getNotOnOrAfterTimestamp() of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
138
            $conditions->getNotBeforeTimestamp() > $conditions->getNotOnOrAfterTimestamp()
139
        ) {
140
            throw new LightSamlValidationException('Conditions NotBefore MUST BE less than NotOnOrAfter');
141
        }
142
    }
143
144
    /**
145
     * @throws LightSamlValidationException
146
     */
147
    protected function validateProxyRestriction(ProxyRestriction $item)
148
    {
149
        if (null === $item->getCount() || '' === $item->getCount() || intval($item->getCount()) != $item->getCount() || $item->getCount() < 0) {
150
            throw new LightSamlValidationException('Count attribute of ProxyRestriction MUST BE a non-negative integer');
151
        }
152
153
        if ($item->getAllAudience()) {
154
            foreach ($item->getAllAudience() as $audience) {
155
                if (false == Helper::validateWellFormedUriString($audience)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
156
                    throw new LightSamlValidationException('ProxyRestriction Audience MUST BE a wellformed uri');
157
                }
158
            }
159
        }
160
    }
161
162
    /**
163
     * @throws LightSamlValidationException
164
     */
165
    protected function validateAudienceRestriction(AudienceRestriction $item)
166
    {
167
        if (false == $item->getAllAudience()) {
168
            return;
169
        }
170
171
        foreach ($item->getAllAudience() as $audience) {
172
            if (false == Helper::validateWellFormedUriString($audience)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
173
                throw new LightSamlValidationException('AudienceRestriction MUST BE a wellformed uri');
174
            }
175
        }
176
    }
177
178
    protected function validateStatements(Assertion $assertion)
179
    {
180
        if (false == $assertion->getAllItems()) {
181
            return;
182
        }
183
184
        foreach ($assertion->getAllItems() as $statement) {
185
            $this->statementValidator->validateStatement($statement);
186
        }
187
    }
188
}
189