PluginProbe ʕ •ᴥ•ʔ
Transferito: WP Migration / trunk
Transferito: WP Migration vtrunk
trunk 11.4.0 12.0.0 13.1.0 14.0.0 14.0.11 14.0.7 14.1.0 14.1.1 14.1.2 14.1.3 14.1.4
transferito / vendor / aws / aws-sdk-php / src / Token / SsoTokenProvider.php
transferito / vendor / aws / aws-sdk-php / src / Token Last commit date
BearerTokenAuthorization.php 11 months ago ParsesIniTrait.php 11 months ago RefreshableTokenProviderInterface.php 11 months ago SsoToken.php 11 months ago SsoTokenProvider.php 11 months ago Token.php 11 months ago TokenAuthorization.php 11 months ago TokenInterface.php 11 months ago TokenProvider.php 11 months ago
SsoTokenProvider.php
210 lines
1 <?php
2 namespace Aws\Token;
3
4 use Aws\Exception\TokenException;
5 use GuzzleHttp\Promise;
6
7 /**
8 * Token that comes from the SSO provider
9 */
10 class SsoTokenProvider implements RefreshableTokenProviderInterface
11 {
12 use ParsesIniTrait;
13
14 const ENV_PROFILE = 'AWS_PROFILE';
15
16 private $ssoProfileName;
17 private $filename;
18 private $ssoOidcClient;
19
20 /**
21 * Constructs a new SsoTokenProvider object, which will fetch a token from an authenticated SSO profile
22 * @param string $ssoProfileName The name of the profile that contains the sso_session key
23 * @param int $filename Name of the config file to sso profile from
24 */
25 public function __construct($ssoProfileName, $filename = null, $ssoOidcClient = null) {
26 $profileName = getenv(self::ENV_PROFILE) ?: 'default';
27 $this->ssoProfileName = !empty($ssoProfileName) ? $ssoProfileName : $profileName;
28 $this->filename = !empty($filename)
29 ? $filename :
30 self::getHomeDir() . '/.aws/config';
31 $this->ssoOidcClient = $ssoOidcClient;
32 }
33
34 /*
35 * Loads cached sso credentials
36 *
37 * @return PromiseInterface
38 */
39 public function __invoke()
40 {
41 return Promise\Coroutine::of(function () {
42 if (!@is_readable($this->filename)) {
43 throw new TokenException("Cannot read profiles from $this->filename");
44 }
45 $profiles = self::loadProfiles($this->filename);
46 if (!isset($profiles[$this->ssoProfileName])) {
47 throw new TokenException("Profile {$this->ssoProfileName} does not exist in {$this->filename}.");
48 }
49 $ssoProfile = $profiles[$this->ssoProfileName];
50 if (empty($ssoProfile['sso_session'])) {
51 throw new TokenException(
52 "Profile {$this->ssoProfileName} in {$this->filename} must contain an sso_session."
53 );
54 }
55
56 $sessionProfileName = 'sso-session ' . $ssoProfile['sso_session'];
57 if (empty($profiles[$sessionProfileName])) {
58 throw new TokenException(
59 "Profile {$this->ssoProfileName} does not exist in {$this->filename}"
60 );
61 }
62
63 $sessionProfileData = $profiles[$sessionProfileName];
64 if (empty($sessionProfileData['sso_start_url'])
65 || empty($sessionProfileData['sso_region'])
66 ) {
67 throw new TokenException(
68 "Profile {$this->ssoProfileName} in {$this->filename} must contain the following keys: "
69 . "sso_start_url and sso_region."
70 );
71 }
72
73 $tokenLocation = self::getTokenLocation($ssoProfile['sso_session']);
74 if (!@is_readable($tokenLocation)) {
75 throw new TokenException("Unable to read token file at $tokenLocation");
76 }
77 $tokenData = $this->getTokenData($tokenLocation);
78 $this->validateTokenData($tokenLocation, $tokenData);
79 yield new SsoToken(
80 $tokenData['accessToken'],
81 $tokenData['expiresAt'],
82 isset($tokenData['refreshToken']) ? $tokenData['refreshToken'] : null,
83 isset($tokenData['clientId']) ? $tokenData['clientId'] : null,
84 isset($tokenData['clientSecret']) ? $tokenData['clientSecret'] : null,
85 isset($tokenData['registrationExpiresAt']) ? $tokenData['registrationExpiresAt'] : null,
86 isset($tokenData['region']) ? $tokenData['region'] : null,
87 isset($tokenData['startUrl']) ? $tokenData['startUrl'] : null
88 );
89 });
90 }
91
92 /**
93 * Refreshes the token
94 * @return mixed|null
95 */
96 public function refresh() {
97 try {
98 //try to reload from disk
99 $token = $this();
100 if (
101 $token instanceof SsoToken
102 && !$token->shouldAttemptRefresh()
103 ) {
104 return $token;
105 }
106 } finally {
107 //if reload from disk fails, try refreshing
108 $tokenLocation = self::getTokenLocation($this->ssoProfileName);
109 $tokenData = $this->getTokenData($tokenLocation);
110 if (
111 empty($this->ssoOidcClient)
112 || empty($tokenData['startUrl'])
113 ) {
114 throw new TokenException(
115 "Cannot refresh this token without an 'ssooidcClient' "
116 . "and a 'start_url'"
117 );
118 }
119 $response = $this->ssoOidcClient->createToken([
120 'clientId' => $tokenData['clientId'],
121 'clientSecret' => $tokenData['clientSecret'],
122 'grantType' => 'refresh_token', // REQUIRED
123 'refreshToken' => $tokenData['refreshToken'],
124 ]);
125 if ($response['@metadata']['statusCode'] == 200) {
126 $tokenData['accessToken'] = $response['accessToken'];
127 $tokenData['expiresAt'] = time () + $response['expiresIn'];
128 $tokenData['refreshToken'] = $response['refreshToken'];
129 $token = new SsoToken(
130 $tokenData['accessToken'],
131 $tokenData['expiresAt'],
132 $tokenData['refreshToken'],
133 isset($tokenData['clientId']) ? $tokenData['clientId'] : null,
134 isset($tokenData['clientSecret']) ? $tokenData['clientSecret'] : null,
135 isset($tokenData['registrationExpiresAt']) ? $tokenData['registrationExpiresAt'] : null,
136 isset($tokenData['region']) ? $tokenData['region'] : null,
137 isset($tokenData['startUrl']) ? $tokenData['startUrl'] : null );
138
139 $this->writeNewTokenDataToDisk($tokenData, $tokenLocation);
140
141 return $token;
142 }
143 }
144 }
145
146 public function shouldAttemptRefresh()
147 {
148 $tokenLocation = self::getTokenLocation($this->ssoProfileName);
149 $tokenData = $this->getTokenData($tokenLocation);
150 return strtotime("-10 minutes") >= strtotime($tokenData['expiresAt']);
151 }
152
153 /**
154 * @param $sso_session
155 * @return string
156 */
157 public static function getTokenLocation($sso_session)
158 {
159 return self::getHomeDir()
160 . '/.aws/sso/cache/'
161 . mb_convert_encoding(sha1($sso_session), "UTF-8")
162 . ".json";
163 }
164
165 /**
166 * @param $tokenLocation
167 * @return array
168 */
169 function getTokenData($tokenLocation)
170 {
171 return json_decode(file_get_contents($tokenLocation), true);
172 }
173
174 /**
175 * @param $tokenData
176 * @param $tokenLocation
177 * @return mixed
178 */
179 private function validateTokenData($tokenLocation, $tokenData)
180 {
181 if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) {
182 throw new TokenException(
183 "Token file at {$tokenLocation} must contain an access token and an expiration"
184 );
185 }
186
187 $expiration = strtotime($tokenData['expiresAt']);
188 if ($expiration === false) {
189 throw new TokenException("Cached SSO token returned an invalid expiration");
190 } elseif ($expiration < time()) {
191 throw new TokenException("Cached SSO token returned an expired token");
192 }
193 return $tokenData;
194 }
195
196 /**
197 * @param array $tokenData
198 * @param string $tokenLocation
199 * @return void
200 */
201 private function writeNewTokenDataToDisk(array $tokenData, $tokenLocation)
202 {
203 $tokenData['expiresAt'] = gmdate(
204 'Y-m-d\TH:i:s\Z',
205 $tokenData['expiresAt']
206 );
207 file_put_contents($tokenLocation, json_encode(array_filter($tokenData)));
208 }
209 }
210