RedmineUserProvider   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 96.83%

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 6
dl 0
loc 188
ccs 61
cts 63
cp 0.9683
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A setUserFactory() 0 4 1
A setApiClient() 0 4 1
A setUserRepository() 0 4 1
B loadUserByCredentials() 0 25 5
A loadUserByUsername() 0 6 1
A refreshUser() 0 6 1
A supportsClass() 0 9 2
B checkUserDomain() 0 17 5
B getUser() 0 26 4
1
<?php
2
/**
3
 * File part of the Redmine User Provider bundle
4
 *
5
 * @category  SymfonyBundle
6
 * @package   GMaissa.RedmineUserProviderBundle
7
 * @author    Guillaume Maïssa <[email protected]>
8
 * @copyright 2017 Guillaume Maïssa
9
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
10
 */
11
12
namespace GMaissa\RedmineUserProviderBundle\Security\Provider;
13
14
use GMaissa\RedmineUserProviderBundle\ApiClient\RedmineApiClientInterface;
15
use GMaissa\RedmineUserProviderBundle\Factory\UserFactoryInterface;
16
use GMaissa\RedmineUserProviderBundle\Model\RedmineUser;
17
use GMaissa\RedmineUserProviderBundle\Repository\UserRepositoryInterface;
18
use Psr\Log\LoggerInterface;
19
use Symfony\Component\Security\Core\Exception\AuthenticationException;
20
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
21
use Symfony\Component\Security\Core\User\UserInterface;
22
use Symfony\Component\Security\Core\User\UserProviderInterface;
23
24
/**
25
 * Redmine User provider class
26
 */
27
class RedmineUserProvider implements UserProviderInterface, CredentialsUserProviderInterface
28
{
29
    /**
30
     * @var UserFactoryInterface
31
     */
32
    protected $userFactory;
33
34
    /**
35
     * @var RedmineApiClientInterface
36
     */
37
    protected $apiClient;
38
39
    /**
40
     * @var UserRepositoryInterface
41
     */
42
    protected $userRepository = false;
43
44
    /**
45
     * @var array
46
     */
47
    protected $allowedDomains;
48
49
    /**
50
     * @var LoggerInterface
51
     */
52
    protected $logger;
53
54
    /**
55
     * Class constructor
56
     *
57
     * @param array           $allowedDomains
58
     * @param LoggerInterface $logger
59
     */
60 11
    public function __construct(array $allowedDomains, LoggerInterface $logger)
61
    {
62 11
        $this->allowedDomains = $allowedDomains;
63 11
        $this->logger         = $logger;
64 11
    }
65
66
    /**
67
     * Set User object factory
68
     *
69
     * @param UserFactoryInterface $userFactory
70
     */
71 11
    public function setUserFactory(UserFactoryInterface $userFactory)
72
    {
73 11
        $this->userFactory = $userFactory;
74 11
    }
75
76
    /**
77
     * Set the Redmine API client
78
     *
79
     * @param RedmineApiClientInterface $apiClient
80
     */
81 11
    public function setApiClient(RedmineApiClientInterface $apiClient)
82
    {
83 11
        $this->apiClient = $apiClient;
84 11
    }
85
86
    /**
87
     * Set the User repository
88
     *
89
     * @param UserRepositoryInterface $userRepository
90
     */
91 1
    public function setUserRepository(UserRepositoryInterface $userRepository)
92
    {
93 1
        $this->userRepository = $userRepository;
94 1
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 6
    public function loadUserByCredentials(string $username, string $password): UserInterface
100
    {
101
        try {
102 6
            $this->apiClient->connect($username, $password);
103 4
            $data = $this->apiClient->api('user')->getCurrentUser();
104
105 4
            if (!$data || !count($data)) {
106
                $this->logger->debug(sprintf('Invalid credentials %s / ****', $username));
107
                throw new AuthenticationException('Invalid credentials');
108
            }
109
110 4
            $this->checkUserDomain($data);
111
112 3
            $user = $this->getUser($data);
113 3
        } catch (AuthenticationException $e) {
114 1
            throw new AuthenticationException($e);
115 2
        } catch (\Exception $e) {
116 2
            $this->logger->debug(
117 2
                sprintf('Invalid credentials')
118
            );
119 2
            throw new AuthenticationException('Invalid credentials');
120
        }
121
122 3
        return $user;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128 1
    public function loadUserByUsername($username)
129
    {
130 1
        throw new UsernameNotFoundException(
131 1
            sprintf('Username "%s" does not exist.', $username)
132
        );
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138 1
    public function refreshUser(UserInterface $user)
139
    {
140 1
        $user = $this->userFactory->refreshUser($user);
141
142 1
        return $user;
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148 2
    public function supportsClass($class)
149
    {
150 2
        $obj = new \ReflectionClass($class);
151
152
        return (
153 2
            $obj->getName() == $this->userFactory->getUserClass() ||
0 ignored issues
show
Bug introduced by
Consider using $obj->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
154 2
            $obj->isSubclassOf($this->userFactory->getUserClass())
155
        );
156
    }
157
158
    /**
159
     * Control if the user's email address domain is allowed
160
     *
161
     * @param array $data
162
     */
163 4
    private function checkUserDomain(array $data)
164
    {
165 4
        if (count($this->allowedDomains)) {
166 4
            $userDomainAllowed = false;
167 4
            foreach ($this->allowedDomains as $allowedDomain) {
168 4
                if (strpos($data['user']['mail'], '@' . $allowedDomain)) {
169 3
                    $userDomainAllowed = true;
170
                }
171
            }
172 4
            if (!$userDomainAllowed) {
173 1
                $this->logger->debug(
174 1
                    sprintf('User email %s from unauthorized domain', $data['user']['mail'])
175
                );
176 1
                throw new AuthenticationException('User not from allowed domain');
177
            }
178
        }
179 3
    }
180
181
    /**
182
     * Retrieve the user or create it
183
     *
184
     * @param array $data
185
     *
186
     * @return RedmineUser
187
     */
188 3
    private function getUser(array $data): RedmineUser
189
    {
190 3
        $user = null;
191
        $userData = [
192 3
            'email'     => $data['user']['mail'],
193 3
            'username'  => $data['user']['login'],
194 3
            'password'  => $data['user']['api_key'],
195
            'enabled'   => true,
196 3
            'firstname' => $data['user']['firstname'],
197 3
            'lastname'  => $data['user']['lastname'],
198
        ];
199
200 3
        if ($this->userRepository) {
201 1
            $user = $this->userRepository->findOneBy(['username' => $data['user']['login']]);
202
        }
203
204 3
        if (is_null($user)) {
205 3
            $user = $this->userFactory->build($userData);
206
        }
207
208 3
        if ($this->userRepository) {
209 1
            $this->userRepository->save($user);
210
        }
211
212 3
        return $user;
213
    }
214
}
215