1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace AerialShip\SamlSPBundle\Security\Core\Authentication\Provider; |
4
|
|
|
|
5
|
|
|
use AerialShip\SamlSPBundle\Bridge\SamlSpInfo; |
6
|
|
|
use AerialShip\SamlSPBundle\Security\Core\Authentication\Token\SamlSpToken; |
7
|
|
|
use AerialShip\SamlSPBundle\Security\Core\User\UserManagerInterface; |
8
|
|
|
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; |
9
|
|
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; |
10
|
|
|
use Symfony\Component\Security\Core\Exception\AuthenticationException; |
11
|
|
|
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; |
12
|
|
|
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; |
13
|
|
|
use Symfony\Component\Security\Core\User\User; |
14
|
|
|
use Symfony\Component\Security\Core\User\UserCheckerInterface; |
15
|
|
|
use Symfony\Component\Security\Core\User\UserInterface; |
16
|
|
|
|
17
|
|
|
class SamlSpAuthenticationProvider implements AuthenticationProviderInterface |
18
|
|
|
{ |
19
|
|
|
/** @var string */ |
20
|
|
|
protected $providerKey; |
21
|
|
|
|
22
|
|
|
/** @var null|UserManagerInterface */ |
23
|
|
|
protected $userProvider; |
24
|
|
|
|
25
|
|
|
/** @var null|\Symfony\Component\Security\Core\User\UserCheckerInterface */ |
26
|
|
|
protected $userChecker; |
27
|
|
|
|
28
|
|
|
/** @var bool */ |
29
|
|
|
protected $createIfNotExists; |
30
|
|
|
|
31
|
|
|
|
32
|
|
|
|
33
|
17 |
|
public function __construct( |
34
|
|
|
$providerKey, |
35
|
|
|
UserManagerInterface $userProvider = null, |
36
|
|
|
UserCheckerInterface $userChecker = null, |
37
|
|
|
$createIfNotExists = false |
38
|
|
|
) { |
39
|
17 |
|
if (null !== $userProvider && null === $userChecker) { |
40
|
1 |
|
throw new \InvalidArgumentException('$userChecker cannot be null, if $userProvider is not null'); |
41
|
|
|
} |
42
|
16 |
|
if (null == $userProvider && $createIfNotExists) { |
43
|
1 |
|
throw new \InvalidArgumentException('$createIfNotExists cannot be true, if $userProvider is null'); |
44
|
|
|
} |
45
|
15 |
|
$this->providerKey = $providerKey; |
46
|
15 |
|
$this->userProvider = $userProvider; |
47
|
15 |
|
$this->userChecker = $userChecker; |
48
|
15 |
|
$this->createIfNotExists = (bool)$createIfNotExists; |
49
|
15 |
|
} |
50
|
|
|
|
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Attempts to authenticate a TokenInterface object. |
54
|
|
|
* @param TokenInterface $token The TokenInterface instance to authenticate |
55
|
|
|
* @return TokenInterface An authenticated TokenInterface instance, never null |
56
|
|
|
* @throws AuthenticationException if the authentication fails |
57
|
|
|
*/ |
58
|
10 |
|
public function authenticate(TokenInterface $token) |
59
|
|
|
{ |
60
|
10 |
|
if (false == $this->supports($token)) { |
|
|
|
|
61
|
1 |
|
return null; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
try { |
65
|
9 |
|
$user = $this->getUser($token); |
|
|
|
|
66
|
|
|
|
67
|
|
|
/** @var $token SamlSpToken */ |
68
|
5 |
|
return $this->createAuthenticatedToken( |
69
|
5 |
|
$token->getSamlSpInfo(), |
70
|
5 |
|
$token->getAttributes(), |
71
|
5 |
|
$user instanceof UserInterface ? $user->getRoles() : array(), |
72
|
|
|
$user |
73
|
5 |
|
); |
74
|
|
|
|
75
|
4 |
|
} catch (AuthenticationException $ex) { |
76
|
1 |
|
throw $ex; |
77
|
3 |
|
} catch (\Exception $ex) { |
78
|
3 |
|
throw new AuthenticationServiceException($ex->getMessage(), (int) $ex->getCode(), $ex); |
79
|
|
|
} |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param SamlSpToken $token |
85
|
|
|
* @return mixed|UserInterface |
86
|
|
|
*/ |
87
|
9 |
|
protected function getUser(SamlSpToken $token) |
88
|
|
|
{ |
89
|
9 |
|
if ($token->getUser() instanceof UserInterface) { |
90
|
2 |
|
$result = $token->getUser(); |
91
|
9 |
|
} else if ($this->userProvider) { |
92
|
6 |
|
$result = $this->getProviderUser($token); |
93
|
2 |
|
} else { |
94
|
1 |
|
$result = $this->getDefaultUser($token); |
95
|
|
|
} |
96
|
|
|
|
97
|
5 |
|
return $result; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Checks whether this provider supports the given token. |
103
|
|
|
* @param \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token |
104
|
|
|
* @return bool true if the implementation supports the Token, false otherwise |
105
|
|
|
*/ |
106
|
12 |
|
public function supports(TokenInterface $token) |
107
|
|
|
{ |
108
|
12 |
|
return $token instanceof SamlSpToken && $this->providerKey === $token->getProviderKey(); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* @param \AerialShip\SamlSPBundle\Bridge\SamlSpInfo $samlInfo |
114
|
|
|
* @param array $attributes |
115
|
|
|
* @param array $roles |
116
|
|
|
* @param mixed $user |
117
|
|
|
* @return SamlSpToken |
118
|
|
|
*/ |
119
|
5 |
|
protected function createAuthenticatedToken(SamlSpInfo $samlInfo, array $attributes, array $roles, $user) |
120
|
|
|
{ |
121
|
5 |
|
if ($user instanceof UserInterface && $this->userChecker) { |
122
|
4 |
|
$this->userChecker->checkPostAuth($user); |
123
|
4 |
|
} |
124
|
5 |
|
$newToken = new SamlSpToken($this->providerKey, $roles); |
125
|
5 |
|
$newToken->setUser($user); |
126
|
5 |
|
$newToken->setAttributes($attributes); |
127
|
5 |
|
$newToken->setSamlSpInfo($samlInfo); |
128
|
5 |
|
$newToken->setAuthenticated(true); |
129
|
5 |
|
if (!in_array('ROLE_USER', $roles)) { |
130
|
4 |
|
$roles[] = 'ROLE_USER'; |
131
|
4 |
|
} |
132
|
5 |
|
return $newToken; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @param SamlSpToken $token |
137
|
|
|
* @return UserInterface |
138
|
|
|
* @throws \Exception |
139
|
|
|
* @throws \Symfony\Component\Security\Core\Exception\UsernameNotFoundException |
140
|
|
|
* @throws \RuntimeException |
141
|
|
|
*/ |
142
|
6 |
|
private function getProviderUser(SamlSpToken $token) |
143
|
|
|
{ |
144
|
6 |
|
if (!$token || !$token->getSamlSpInfo()) { |
145
|
|
|
throw new \RuntimeException('Token does not contain SamlSpInfo'); |
146
|
|
|
} |
147
|
|
|
try { |
148
|
6 |
|
$user = $this->userProvider->loadUserBySamlInfo($token->getSamlSpInfo()); |
149
|
6 |
|
} catch (UsernameNotFoundException $ex) { |
150
|
3 |
|
if (false == $this->createIfNotExists) { |
|
|
|
|
151
|
1 |
|
throw $ex; |
152
|
|
|
} |
153
|
2 |
|
$user = $this->userProvider->createUserFromSamlInfo($token->getSamlSpInfo()); |
154
|
|
|
} |
155
|
|
|
|
156
|
4 |
|
if (false == $user instanceof UserInterface) { |
|
|
|
|
157
|
2 |
|
throw new \RuntimeException('User provider did not return an implementation of user interface.'); |
158
|
|
|
} |
159
|
|
|
|
160
|
2 |
|
return $user; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @param \AerialShip\SamlSPBundle\Security\Core\Authentication\Token\SamlSpToken $token |
165
|
|
|
* @return UserInterface |
166
|
|
|
*/ |
167
|
1 |
|
private function getDefaultUser(SamlSpToken $token) |
168
|
|
|
{ |
169
|
1 |
|
$nameID = $token && $token->getSamlSpInfo()->getNameID() && $token->getSamlSpInfo()->getNameID()->getValue() ? $token->getSamlSpInfo()->getNameID()->getValue() : 'anon.'; |
170
|
1 |
|
$result = new User($nameID, '', array('ROLE_USER')); |
171
|
1 |
|
return $result; |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
|
When comparing two booleans, it is generally considered safer to use the strict comparison operator.