protected $rgwS3AccessKey;
/** @var string S3 authentication key (RADOS Gateway) */
protected $rgwS3SecretKey;
+ /** @var array Additional users (account:user) to open read permissions for */
+ protected $readUsers;
+ /** @var array Additional users (account:user) to open write permissions for */
+ protected $writeUsers;
/** @var BagOStuff */
protected $srvCache;
* This is used for generating expiring pre-authenticated URLs.
* Only use this when using rgw and to work around
* http://tracker.newdream.net/issues/3454.
+ * - readUsers : Swift users that should have read access (account:username)
+ * - writeUsers : Swift users that should have write access (account:username)
*/
public function __construct( array $config ) {
parent::__construct( $config );
} else {
$this->srvCache = new EmptyBagOStuff();
}
+ $this->readUsers = isset( $config['readUsers'] )
+ ? $config['readUsers']
+ : [];
+ $this->writeUsers = isset( $config['writeUsers'] )
+ ? $config['writeUsers']
+ : [];
}
public function getFeatures() {
protected function resolveContainerPath( $container, $relStoragePath ) {
if ( !mb_check_encoding( $relStoragePath, 'UTF-8' ) ) {
return null; // not UTF-8, makes it hard to use CF and the swift HTTP API
- } elseif ( strlen( urlencode( $relStoragePath ) ) > 1024 ) {
+ } elseif ( strlen( rawurlencode( $relStoragePath ) ) > 1024 ) {
return null; // too long for Swift
}
* @param array $params
* @return array Sanitized value of 'headers' field in $params
*/
+ protected function sanitizeHdrsStrict( array $params ) {
+ if ( !isset( $params['headers'] ) ) {
+ return [];
+ }
+
+ $headers = $this->getCustomHeaders( $params['headers'] );
+ unset( $headers[ 'content-type' ] );
+
+ return $headers;
+ }
+
+ /**
+ * Sanitize and filter the custom headers from a $params array.
+ * Only allows certain "standard" Content- and X-Content- headers.
+ *
+ * When POSTing data, libcurl adds Content-Type: application/x-www-form-urlencoded
+ * if Content-Type is not set, which overwrites the stored Content-Type header
+ * in Swift - therefore for POSTing data do not strip the Content-Type header (the
+ * previously-stored header that has been already read back from swift is sent)
+ *
+ * @param array $params
+ * @return array Sanitized value of 'headers' field in $params
+ */
protected function sanitizeHdrs( array $params ) {
return isset( $params['headers'] )
? $this->getCustomHeaders( $params['headers'] )
// Normalize casing, and strip out illegal headers
foreach ( $rawHeaders as $name => $value ) {
$name = strtolower( $name );
- if ( preg_match( '/^content-(type|length)$/', $name ) ) {
+ if ( preg_match( '/^content-length$/', $name ) ) {
continue; // blacklisted
} elseif ( preg_match( '/^(x-)?content-/', $name ) ) {
$headers[$name] = $value; // allowed
'etag' => md5( $params['content'] ),
'content-type' => $contentType,
'x-object-meta-sha1base36' => $sha1Hash
- ] + $this->sanitizeHdrs( $params ),
+ ] + $this->sanitizeHdrsStrict( $params ),
'body' => $params['content']
] ];
'etag' => md5_file( $params['src'] ),
'content-type' => $contentType,
'x-object-meta-sha1base36' => $sha1Hash
- ] + $this->sanitizeHdrs( $params ),
+ ] + $this->sanitizeHdrsStrict( $params ),
'body' => $handle // resource
] ];
'headers' => [
'x-copy-from' => '/' . rawurlencode( $srcCont ) .
'/' . str_replace( "%2F", "/", rawurlencode( $srcRel ) )
- ] + $this->sanitizeHdrs( $params ), // extra headers merged into object
+ ] + $this->sanitizeHdrsStrict( $params ), // extra headers merged into object
] ];
$method = __METHOD__;
'headers' => [
'x-copy-from' => '/' . rawurlencode( $srcCont ) .
'/' . str_replace( "%2F", "/", rawurlencode( $srcRel ) )
- ] + $this->sanitizeHdrs( $params ) // extra headers merged into object
+ ] + $this->sanitizeHdrsStrict( $params ) // extra headers merged into object
]
];
if ( "{$srcCont}/{$srcRel}" !== "{$dstCont}/{$dstRel}" ) {
$stat = $this->getContainerStat( $fullCont );
if ( is_array( $stat ) ) {
+ $readUsers = array_merge( $this->readUsers, [ $this->swiftUser ] );
+ $writeUsers = array_merge( $this->writeUsers, [ $this->swiftUser ] );
// Make container private to end-users...
$status->merge( $this->setContainerAccess(
$fullCont,
- [ $this->swiftUser ], // read
- [ $this->swiftUser ] // write
+ $readUsers,
+ $writeUsers
) );
} elseif ( $stat === false ) {
$status->fatal( 'backend-fail-usable', $params['dir'] );
$stat = $this->getContainerStat( $fullCont );
if ( is_array( $stat ) ) {
+ $readUsers = array_merge( $this->readUsers, [ $this->swiftUser, '.r:*' ] );
+ $writeUsers = array_merge( $this->writeUsers, [ $this->swiftUser ] );
+
// Make container public to end-users...
$status->merge( $this->setContainerAccess(
$fullCont,
- [ $this->swiftUser, '.r:*' ], // read
- [ $this->swiftUser ] // write
+ $readUsers,
+ $writeUsers
) );
} elseif ( $stat === false ) {
$status->fatal( 'backend-fail-usable', $params['dir'] );
/** @noinspection PhpUnusedLocalVariableInspection */
$ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
- $this->logger->error( __METHOD__ . ": $path was not stored with SHA-1 metadata." );
+ $this->logger->error( __METHOD__ . ": {path} was not stored with SHA-1 metadata.",
+ [ 'path' => $path ] );
$objHdrs['x-object-meta-sha1base36'] = false;
}
}
- $this->logger->error( __METHOD__ . ": unable to set SHA-1 metadata for $path" );
+ $this->logger->error( __METHOD__ . ': unable to set SHA-1 metadata for {path}',
+ [ 'path' => $path ] );
return $objHdrs; // failed
}
* (lists are truncated to 10000 item with no way to page), and is just a performance risk.
*
* @param string $container Resolved Swift container
- * @param array $readGrps List of the possible criteria for a request to have
+ * @param array $readUsers List of the possible criteria for a request to have
* access to read a container. Each item is one of the following formats:
* - account:user : Grants access if the request is by the given user
* - ".r:<regex>" : Grants access if the request is from a referrer host that
* Setting this to '*' effectively makes a container public.
* -".rlistings:<regex>" : Grants access if the request is from a referrer host that
* matches the expression and the request is for a listing.
- * @param array $writeGrps A list of the possible criteria for a request to have
+ * @param array $writeUsers A list of the possible criteria for a request to have
* access to write to a container. Each item is of the following format:
* - account:user : Grants access if the request is by the given user
* @return StatusValue
*/
- protected function setContainerAccess( $container, array $readGrps, array $writeGrps ) {
+ protected function setContainerAccess( $container, array $readUsers, array $writeUsers ) {
$status = $this->newStatus();
$auth = $this->getAuthentication();
'method' => 'POST',
'url' => $this->storageUrl( $auth, $container ),
'headers' => $this->authTokenHeaders( $auth ) + [
- 'x-container-read' => implode( ',', $readGrps ),
- 'x-container-write' => implode( ',', $writeGrps )
+ 'x-container-read' => implode( ',', $readUsers ),
+ 'x-container-write' => implode( ',', $writeUsers )
]
] );
if ( $rcode != 204 && $rcode !== 202 ) {
$status->fatal( 'backend-fail-internal', $this->name );
- $this->logger->error( __METHOD__ . ': unexpected rcode value (' . $rcode . ')' );
+ $this->logger->error( __METHOD__ . ': unexpected rcode value ({rcode})',
+ [ 'rcode' => $rcode ] );
}
return $status;
// @see SwiftFileBackend::setContainerAccess()
if ( empty( $params['noAccess'] ) ) {
- $readGrps = [ '.r:*', $this->swiftUser ]; // public
+ $readUsers = array_merge( $this->readUsers, [ '.r:*', $this->swiftUser ] ); // public
} else {
- $readGrps = [ $this->swiftUser ]; // private
+ $readUsers = array_merge( $this->readUsers, [ $this->swiftUser ] ); // private
}
- $writeGrps = [ $this->swiftUser ]; // sanity
+
+ $writeUsers = array_merge( $this->writeUsers, [ $this->swiftUser ] ); // sanity
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [
'method' => 'PUT',
'url' => $this->storageUrl( $auth, $container ),
'headers' => $this->authTokenHeaders( $auth ) + [
- 'x-container-read' => implode( ',', $readGrps ),
- 'x-container-write' => implode( ',', $writeGrps )
+ 'x-container-read' => implode( ',', $readUsers ),
+ 'x-container-write' => implode( ',', $writeUsers )
]
] );
if ( $code == 401 ) { // possibly a stale token
$this->srvCache->delete( $this->getCredsCacheKey( $this->swiftUser ) );
}
- $this->logger->error(
- "HTTP $code ($desc) in '{$func}' (given '" . FormatJson::encode( $params ) . "')" .
- ( $err ? ": $err" : "" )
- );
+ $msg = "HTTP {code} ({desc}) in '{func}' (given '{params}')";
+ $msgParams = [
+ 'code' => $code,
+ 'desc' => $desc,
+ 'func' => $func,
+ 'req_params' => FormatJson::encode( $params ),
+ ];
+ if ( $err ) {
+ $msg .= ': {err}';
+ $msgParams['err'] = $err;
+ }
+ $this->logger->error( $msg, $msgParams );
}
}