MediaWiki  master
SwiftVirtualRESTService.php
Go to the documentation of this file.
1 <?php
31  protected $authCreds;
33  protected $authSessionTimestamp = 0;
35  protected $authErrorTimestamp = null;
37  protected $authCachedStatus = null;
39  protected $authCachedReason = null;
40 
48  public function __construct( array $params ) {
49  wfDeprecated( __METHOD__, '1.41' );
50  // set up defaults and merge them with the given params
51  $mparams = array_merge( [
52  'name' => 'swift'
53  ], $params );
54  parent::__construct( $mparams );
55  }
56 
60  protected function needsAuthRequest() {
61  if ( !$this->authCreds ) {
62  return true;
63  }
64  if ( $this->authErrorTimestamp !== null ) {
65  if ( ( time() - $this->authErrorTimestamp ) < 60 ) {
66  return $this->authCachedStatus; // failed last attempt; don't bother
67  } else { // actually retry this time
68  $this->authErrorTimestamp = null;
69  }
70  }
71  // Session keys expire after a while, so we renew them periodically
72  return ( ( time() - $this->authSessionTimestamp ) > $this->params['swiftAuthTTL'] );
73  }
74 
75  protected function applyAuthResponse( array $req ) {
76  $this->authSessionTimestamp = 0;
77  [ $rcode, $rdesc, $rhdrs, , ] = $req['response'];
78  if ( $rcode >= 200 && $rcode <= 299 ) { // OK
79  $this->authCreds = [
80  'auth_token' => $rhdrs['x-auth-token'],
81  'storage_url' => $rhdrs['x-storage-url']
82  ];
83  $this->authSessionTimestamp = time();
84  return true;
85  } elseif ( $rcode === 403 ) {
86  $this->authCachedStatus = 401;
87  $this->authCachedReason = 'Authorization Required';
88  $this->authErrorTimestamp = time();
89  return false;
90  } else {
91  $this->authCachedStatus = $rcode;
92  $this->authCachedReason = $rdesc;
93  $this->authErrorTimestamp = time();
94  return null;
95  }
96  }
97 
102  public function onRequests( array $reqs, Closure $idGeneratorFunc ) {
103  $result = [];
104  $firstReq = reset( $reqs );
105  if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) {
106  // This was an authentication request for work requests...
107  $result = $reqs; // no change
108  } else {
109  // These are actual work requests...
110  $needsAuth = $this->needsAuthRequest();
111  if ( $needsAuth === true ) {
112  // These are work requests and we don't have any token to use.
113  // Replace the work requests with an authentication request.
114  $result = [
115  $idGeneratorFunc() => [
116  'method' => 'GET',
117  'url' => $this->params['swiftAuthUrl'] . "/v1.0",
118  'headers' => [
119  'x-auth-user' => $this->params['swiftUser'],
120  'x-auth-key' => $this->params['swiftKey'] ],
121  'isAuth' => true,
122  'chain' => $reqs
123  ]
124  ];
125  } elseif ( $needsAuth !== false ) {
126  // These are work requests and authentication has previously failed.
127  // It is most efficient to just give failed pseudo responses back for
128  // the original work requests.
129  foreach ( $reqs as $key => $req ) {
130  $req['response'] = [
131  'code' => $this->authCachedStatus,
132  'reason' => $this->authCachedReason,
133  'headers' => [],
134  'body' => '',
135  'error' => ''
136  ];
137  $result[$key] = $req;
138  }
139  } else {
140  // These are work requests and we have a token already.
141  // Go through and mangle each request to include a token.
142  foreach ( $reqs as $key => $req ) {
143  // The default encoding treats the URL as a REST style path that uses
144  // forward slash as a hierarchical delimiter (and never otherwise).
145  // Subclasses can override this, and should be documented in any case.
146  $parts = array_map( 'rawurlencode', explode( '/', $req['url'] ) );
147  $req['url'] = $this->authCreds['storage_url'] . '/' . implode( '/', $parts );
148  $req['headers']['x-auth-token'] = $this->authCreds['auth_token'];
149  $result[$key] = $req;
150  // @TODO: add ETag/Content-Length and such as needed
151  }
152  }
153  }
154  return $result;
155  }
156 
157  public function onResponses( array $reqs, Closure $idGeneratorFunc ) {
158  $firstReq = reset( $reqs );
159  if ( $firstReq && count( $reqs ) == 1 && isset( $firstReq['isAuth'] ) ) {
160  $result = [];
161  // This was an authentication request for work requests...
162  if ( $this->applyAuthResponse( $firstReq ) ) {
163  // If it succeeded, we can substitute the work requests back.
164  // Call this recursively in order to munge and add headers.
165  $result = $this->onRequests( $firstReq['chain'], $idGeneratorFunc );
166  } else {
167  // If it failed, it is most efficient to just give failing
168  // pseudo-responses back for the actual work requests.
169  foreach ( $firstReq['chain'] as $key => $req ) {
170  $req['response'] = [
171  'code' => $this->authCachedStatus,
172  'reason' => $this->authCachedReason,
173  'headers' => [],
174  'body' => '',
175  'error' => ''
176  ];
177  $result[$key] = $req;
178  }
179  }
180  } else {
181  $result = $reqs; // no change
182  }
183  return $result;
184  }
185 }
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
Example virtual rest service for OpenStack Swift.
onResponses(array $reqs, Closure $idGeneratorFunc)
Mangle or replace virtual HTTP(S) requests which have been responded to.
int $authSessionTimestamp
UNIX timestamp.
onRequests(array $reqs, Closure $idGeneratorFunc)
Prepare virtual HTTP(S) requests (for this service) for execution.This method should mangle any of th...
int null $authErrorTimestamp
UNIX timestamp.
Virtual HTTP service instance that can be mounted on to a VirtualRESTService.
array $params
Key/value map.