SmartID::__construct()   B
last analyzed

Complexity

Conditions 6
Paths 32

Size

Total Lines 42
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 26
c 1
b 0
f 0
nc 32
nop 2
dl 0
loc 42
rs 8.8817
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\smartattributes\Auth\Process;
6
7
use SimpleSAML\Assert\Assert;
8
use SimpleSAML\Error;
9
10
class SmartID extends \SimpleSAML\Auth\ProcessingFilter
11
{
12
    /**
13
     * Which attributes to use as identifiers?
14
     *
15
     * IMPORTANT: If you use the (default) attributemaps (twitter2name, facebook2name,
16
     * etc., be sure to comment out the entries that map xxx_targetedID to
17
     * eduPersonTargetedID, or there will be no way to see its origin any more.
18
     *
19
     * @var string[]
20
     */
21
    private array $candidates = [
22
        'eduPersonTargetedID',
23
        'eduPersonPrincipalName',
24
        'pairwise-id',
25
        'subject-id',
26
        'openid',
27
        'facebook_targetedID',
28
        'twitter_targetedID',
29
        'windowslive_targetedID',
30
        'linkedin_targetedID',
31
    ];
32
33
    /**
34
     * @var string The name of the generated ID attribute.
35
     */
36
    private string $id_attribute = 'smart_id';
37
38
    /**
39
     * Whether to append the AuthenticatingAuthority, separated by '!'
40
     * This only works when SSP is used as a gateway.
41
     * @var bool
42
     */
43
    private bool $add_authority = true;
44
45
    /**
46
     * Whether to prepend the CandidateID, separated by ':'
47
     * @var bool
48
     */
49
    private bool $add_candidate = true;
50
51
    /**
52
     * Whether a missing identifier is o.k.
53
     * @var bool
54
     */
55
    private bool $fail_if_empty = true;
56
57
58
    /**
59
     * @param array $config
60
     * @param mixed $reserved
61
     * @throws \Exception
62
     */
63
    public function __construct(array $config, $reserved)
64
    {
65
        parent::__construct($config, $reserved);
66
67
        if (array_key_exists('candidates', $config)) {
68
            Assert::isArray(
69
                $config['candidates'],
70
                'SmartID authproc configuration error: \'candidates\' should be an array.',
71
            );
72
            $this->candidates = $config['candidates'];
73
        }
74
75
        if (array_key_exists('id_attribute', $config)) {
76
            Assert::string(
77
                $config['id_attribute'],
78
                'SmartID authproc configuration error: \'id_attribute\' should be a string.',
79
            );
80
            $this->id_attribute = $config['id_attribute'];
81
        }
82
83
        if (array_key_exists('add_authority', $config)) {
84
            Assert::boolean(
85
                $config['add_authority'],
86
                'SmartID authproc configuration error: \'add_authority\' should be a boolean.',
87
            );
88
            $this->add_authority = $config['add_authority'];
89
        }
90
91
        if (array_key_exists('add_candidate', $config)) {
92
            Assert::boolean(
93
                $config['add_candidate'],
94
                'SmartID authproc configuration error: \'add_candidate\' should be a boolean.',
95
            );
96
            $this->add_candidate = $config['add_candidate'];
97
        }
98
99
        if (array_key_exists('fail_if_empty', $config)) {
100
            Assert::boolean(
101
                $config['fail_if_empty'],
102
                'SmartID authproc configuration error: \'fail_if_empty\' should be a boolean.',
103
            );
104
            $this->fail_if_empty = $config['fail_if_empty'];
105
        }
106
    }
107
108
109
    /**
110
     * @param array $attributes
111
     * @param array $request
112
     * @return string
113
     * @throws \SimpleSAML\Error\Exception
114
     */
115
    private function addID(array $attributes, array $request): string
116
    {
117
        $state = $request['saml:sp:State'];
118
        foreach ($this->candidates as $idCandidate) {
119
            if (isset($attributes[$idCandidate][0])) {
120
                if ($this->add_authority && count($state['saml:AuthenticatingAuthority']) > 0) {
121
                    $authority = end($state['saml:AuthenticatingAuthority']);
122
                    return ($this->add_candidate ? $idCandidate . ':' : '') . $attributes[$idCandidate][0] . '!' .
123
                        $authority;
124
                } else {
125
                    return ($this->add_candidate ? $idCandidate . ':' : '') . $attributes[$idCandidate][0];
126
                }
127
            }
128
        }
129
130
        /**
131
         * At this stage no usable id_candidate has been detected.
132
         */
133
        if ($this->fail_if_empty) {
134
            throw new Error\Exception('This service needs at least one of the following ' .
135
                'attributes to identity users: ' . implode(', ', $this->candidates) . '. Unfortunately not ' .
136
                'one of them was detected. Please ask your institution administrator to release one of ' .
137
                'them, or try using another identity provider.');
138
        } else {
139
            /**
140
             * Return an empty identifier,
141
             * missing id attribute must be handled by another authproc filter
142
             */
143
            return '';
144
        }
145
    }
146
147
148
    /**
149
     * Apply filter to add or replace attributes.
150
     *
151
     * Add or replace existing attributes with the configured values.
152
     *
153
     * @param array &$state  The current request
154
     */
155
    public function process(array &$state): void
156
    {
157
        Assert::keyExists($state, 'Attributes');
158
159
        $id = $this->addID($state['Attributes'], $state);
160
161
        if (!empty($id)) {
162
            $state['Attributes'][$this->id_attribute] = [$id];
163
        }
164
    }
165
}
166