Test Failed
Pull Request — master (#88)
by Artem
04:05
created

UserResolverHelpers   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 130
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 34
dl 0
loc 130
rs 10
c 1
b 0
f 0
wmc 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A resolveUserEmail() 0 9 2
A extractNameFromEmail() 0 18 2
A resolveUserName() 0 17 2
A firstUserAttribute() 0 3 1
A getUserAttribute() 0 11 3
1
<?php
2
3
namespace Slides\Saml2\Concerns;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Facades\Log;
7
use Illuminate\Support\Str;
8
use Slides\Saml2\Saml2User;
9
10
trait UserResolverHelpers
11
{
12
    /**
13
     * The list of attribute names containing the user full name..
14
     *
15
     * @var array|string[]
16
     */
17
    protected array $userNameAttributes = [
18
        'FullName',
19
        'name',
20
        'givenname',
21
        'displayname'
22
    ];
23
24
    /**
25
     * The list of attribute names containing the user email.
26
     *
27
     * @var array|string[]
28
     */
29
    protected array $userEmailAttributes = [
30
        'emailaddress',
31
        'Email'
32
    ];
33
34
    /**
35
     * Resolve a user email from the request.
36
     *
37
     * @param Saml2User $user
38
     *
39
     * @return string|null
40
     */
41
    protected function resolveUserEmail(Saml2User $user): ?string
42
    {
43
        // There is a chance that email is passed as a UserID.
44
        if (filter_var($user->getUserId(), FILTER_VALIDATE_EMAIL)) {
45
            return $user->getUserId();
46
        }
47
48
        // Otherwise, we need to lookup through attributes.
49
        return $this->firstUserAttribute($user, $this->userEmailAttributes);
50
    }
51
52
    /**
53
     * Resolve a user name.
54
     *
55
     * @param Saml2User $user
56
     *
57
     * @return mixed|string|null
58
     */
59
    protected function resolveUserName(Saml2User $user)
60
    {
61
        // First of all, we need to look up through attributes
62
        if ($name = $this->firstUserAttribute($user, $this->userNameAttributes)) {
63
            return $name;
64
        }
65
66
        Log::warning('[SSO] Not able to resolve user name, extracting from email.', [
67
            'samlAttributes' => $user->getAttributes(),
68
            'samlUserId' => $user->getUserId(),
69
            'samlNameId' => $user->getNameId()
70
        ]);
71
72
        // Not the best solution, but if user name cannot be resolved,
73
        // we can try to extract it from the email address
74
        return $this->extractNameFromEmail(
75
            $this->resolveUserEmail($user)
0 ignored issues
show
Bug introduced by
It seems like $this->resolveUserEmail($user) can also be of type null; however, parameter $email of Slides\Saml2\Concerns\Us...:extractNameFromEmail() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

75
            /** @scrutinizer ignore-type */ $this->resolveUserEmail($user)
Loading history...
76
        );
77
    }
78
79
    /**
80
     * Find a user attribute value using a list of names.
81
     *
82
     * @param Saml2User $user
83
     * @param array $attributes
84
     * @param string|null $default
85
     *
86
     * @return mixed|string|null
87
     */
88
    protected function firstUserAttribute(Saml2User $user, array $attributes, $default = null)
89
    {
90
        return Arr::first($attributes, fn($attribute) => $this->getUserAttribute($user, $attribute), $default);
91
    }
92
93
    /**
94
     * Get user's attribute.
95
     *
96
     * @param Saml2User $user
97
     * @param string $attribute
98
     * @param string|null $default
99
     *
100
     * @return string|mixed|null
101
     */
102
    protected function getUserAttribute(Saml2User $user, string $attribute, string $default = null): ?string
103
    {
104
        foreach ($user->getAttributes() as $claim => $value) {
105
            $value = $value[0];
106
107
            if (strpos($claim, $attribute) !== false) {
108
                return $value;
109
            }
110
        }
111
112
        return $default;
113
    }
114
115
    /**
116
     * Attempt to extract full name from the email address.
117
     *
118
     * @param string $email
119
     *
120
     * @return string|null
121
     */
122
    protected function extractNameFromEmail(string $email): ?string
123
    {
124
        // Extract words from the email name
125
        preg_match_all('/[a-z]+/i', Str::before($email, '@'), $matches);
126
127
        $words = $matches[0];
128
129
        if (!$words) {
130
            return null;
131
        }
132
133
        // Keep only two first words and capitalize them
134
        $words = array_map(
135
            fn(string $word) => Str::title($word),
136
            array_slice($words, 0, 2)
137
        );
138
139
        return implode(' ', $words);
140
    }
141
}
142