MediaWiki master
SwiftAuthProvider.php
Go to the documentation of this file.
1<?php
2
4
5use Psr\Log\LoggerInterface;
9
17 private const DEFAULT_HTTP_OPTIONS = [ 'httpVersion' => 'v1.1' ];
18 public const AUTH_FAILURE_ERROR = 'Could not connect due to prior authentication failure';
20 private $authTTL;
22 private $swiftAuthUrl;
24 private $swiftStorageUrl;
26 private BagOStuff $credentialCache;
28 private $swiftUser;
30 private $swiftKey;
31
33 private $authCreds;
35 private $authErrorTimestamp = null;
36 private MultiHttpClient $http;
37 private BagOStuff $srvCache;
38 private LoggerInterface $logger;
39
40 public function __construct( array $config, MultiHttpClient $http, LoggerInterface $logger ) {
41 // Required settings
42 $this->swiftAuthUrl = $config['swiftAuthUrl'];
43 $this->swiftUser = $config['swiftUser'];
44 $this->swiftKey = $config['swiftKey'];
45 $this->swiftStorageUrl = $config['swiftStorageUrl'] ?? null;
46 // Optional settings
47 $this->authTTL = $config['swiftAuthTTL'] ?? 15 * 60; // some sensible number
48
49 $this->srvCache = $config['srvCache'] ?? new EmptyBagOStuff();
50
51 // Cache auth token information to avoid RTTs
52 if ( !empty( $config['cacheAuthInfo'] ) ) {
53 $this->credentialCache = $this->srvCache;
54 } else {
55 $this->credentialCache = new EmptyBagOStuff();
56 }
57 $this->http = $http;
58 $this->setLogger( $logger );
59 }
60
61 public function setLogger( LoggerInterface $logger ): void {
62 $this->logger = $logger;
63 $this->http->setLogger( $logger );
64 }
65
71 public function getAuthentication() {
72 if ( $this->authErrorTimestamp !== null ) {
73 $interval = time() - $this->authErrorTimestamp;
74 if ( $interval < 60 ) {
75 $this->logger->debug(
76 'rejecting request since auth failure occurred {interval} seconds ago',
77 [ 'interval' => $interval ]
78 );
79 return null;
80 } else { // actually retry this time
81 $this->authErrorTimestamp = null;
82 }
83 }
84 // Authenticate with proxy and get a session key...
85 if ( !$this->authCreds ) {
86 $cacheKey = $this->getCredsCacheKey( $this->swiftUser );
87 $creds = $this->credentialCache->get( $cacheKey );
88 if (
89 isset( $creds['auth_token'] ) &&
90 isset( $creds['storage_url'] ) &&
91 isset( $creds['expiry_time'] ) &&
92 $creds['expiry_time'] > time()
93 ) {
94 // Cache hit; reuse the cached credentials cache
95 $this->setAuthCreds( $creds );
96 } else {
97 // Cache miss; re-authenticate to get the credentials
98 $this->refreshAuthentication();
99 }
100 }
101
102 return $this->authCreds;
103 }
104
110 private function setAuthCreds( ?array $creds ) {
111 $this->logger->debug( 'Using auth token with expiry_time={expiry_time}',
112 [
113 'expiry_time' => isset( $creds['expiry_time'] )
114 ? gmdate( 'c', $creds['expiry_time'] ) : 'null'
115 ]
116 );
117 $this->authCreds = $creds;
118 }
119
125 public function refreshAuthentication() {
126 [ $rcode, , $rhdrs, $rbody, ] = $this->http->run( [
127 'method' => 'GET',
128 'url' => "{$this->swiftAuthUrl}/v1.0",
129 'headers' => [
130 'x-auth-user' => $this->swiftUser,
131 'x-auth-key' => $this->swiftKey
132 ]
133 ], self::DEFAULT_HTTP_OPTIONS );
134
135 if ( $rcode >= 200 && $rcode <= 299 ) { // OK
136 if ( isset( $rhdrs['x-auth-token-expires'] ) ) {
137 $ttl = intval( $rhdrs['x-auth-token-expires'] );
138 } else {
139 $ttl = $this->authTTL;
140 }
141 $expiryTime = time() + $ttl;
142 $creds = [
143 'auth_token' => $rhdrs['x-auth-token'],
144 'storage_url' => $this->swiftStorageUrl ?? $rhdrs['x-storage-url'],
145 'expiry_time' => $expiryTime,
146 ];
147 $this->credentialCache->set(
148 $this->getCredsCacheKey( $this->swiftUser ),
149 $creds,
150 $expiryTime
151 );
152 } elseif ( $rcode === 401 ) {
153 $msg = "HTTP {code} ({desc}) in '{func}': {err}";
154 $msgParams = [
155 'code' => $rcode,
156 'err' => "Authentication failed.",
157 'func' => __METHOD__,
158 ];
159 $this->logger->error( $msg, $msgParams );
160 $this->authErrorTimestamp = time();
161 $creds = null;
162 } else {
163 $msg = "HTTP {code} ({desc}) in '{func}': {err}";
164 $msgParams = [
165 'code' => $rcode,
166 'err' => "HTTP return code: $rcode",
167 'func' => __METHOD__,
168 ];
169 $this->logger->error( $msg, $msgParams );
170 $this->authErrorTimestamp = time();
171 $creds = null;
172 }
173 $this->setAuthCreds( $creds );
174 return $creds;
175 }
176
180 public function authTokenHeaders( array $creds ) {
181 return [ 'x-auth-token' => $creds['auth_token'] ];
182 }
183
190 private function getCredsCacheKey( $username ) {
191 return 'swiftcredentials:' . md5( $username . ':' . $this->swiftAuthUrl );
192 }
193
202 public function getAuthFailureResponse() {
203 return [
204 'code' => 0,
205 0 => 0,
206 'reason' => '',
207 1 => '',
208 'headers' => [],
209 2 => [],
210 'body' => '',
211 3 => '',
212 'error' => self::AUTH_FAILURE_ERROR,
213 4 => self::AUTH_FAILURE_ERROR
214 ];
215 }
216}
Provides authentication to swift file backend.
refreshAuthentication()
Fetch the auth token from the server, without caching.
__construct(array $config, MultiHttpClient $http, LoggerInterface $logger)
getAuthentication()
Get the cached auth token.
getAuthFailureResponse()
Get a synthetic response to return from requestWithAuth() or requestMultiWithAuth() if the request co...
Class to handle multiple HTTP requests.
Abstract class for any ephemeral data store.
Definition BagOStuff.php:73
No-op implementation that stores nothing.