public static function S3fsCorsFile::processManagedFile in S3 File System CORS Upload 8
Handle a managed file from a form upload field.
Overrides ManagedFile::processManagedFile
File
- src/
Element/ S3fsCorsFile.php, line 150
Class
- S3fsCorsFile
- Provides an S3fs Cors File Element.
Namespace
Drupal\s3fs_cors\ElementCode
public static function processManagedFile(&$element, FormStateInterface $form_state, &$complete_form) {
// Get ManagedFile Process Form Element and Alter it.
$element = parent::processManagedFile($element, $form_state, $complete_form);
// Alter Upload - Input File element.
$element['upload']['#attributes'] = [
'class' => [
's3fs-cors-upload',
],
];
$config = \Drupal::config('s3fs.settings');
$cors_config = \Drupal::config('s3fs_cors.settings');
// Create Configurations needed for AWS S3 CORS Upload.
$acl = $cors_config
->get('s3fs_access_type');
$bucket = $config
->get('bucket');
$upload_parts = explode('://', $element['#upload_location']);
$s3_key = $upload_parts[1];
// If a base folder for public or private uri schemes has been defined,
// prepend it to the $s3 key, else use the same defaults as the s3fs module.
if (method_exists('\\Drupal\\Core\\StreamWrapper\\StreamWrapperManager', 'getScheme')) {
// Drupal 8.8+, inc. Drupal 9.
$uri_scheme = StreamWrapperManager::getScheme($element['#upload_location']);
}
else {
// Drupal < 8.8.
$uri_scheme = \Drupal::service('file_system')
->uriScheme($element['#upload_location']);
}
if ($uri_scheme == 'public' || $uri_scheme == 'private') {
$config_key = $uri_scheme . '_folder';
$folder_key = empty($config
->get($config_key)) ? 's3fs-' . $uri_scheme : $config
->get($config_key);
$s3_key = $folder_key . '/' . $s3_key;
}
// If a root folder has been set, prepend it to the $s3_key at this time.
if (!empty($config
->get('root_folder'))) {
$s3_key = $config
->get('root_folder') . '/' . $s3_key;
}
// Drop the "s3://" stream prefix as it is misleading.
$element['#upload_location'] = $bucket . '::' . $s3_key;
$datenow = new DrupalDateTime('now');
$datenow
->setTimezone(new \DateTimeZone('UTC'));
$expiration = clone $datenow;
$expiration
->add(new \DateInterval('PT6H'));
$region = $config
->get('region') ?: Settings::get('s3fs.region', '');
// Use the memoized default credential provider.
$provider = CredentialProvider::defaultProvider();
// Chain a provider with the local values if they exist.
$credentials = NULL;
$access_key = $config
->get('access_key') ?: Settings::get('s3fs.access_key', '');
$secret_key = $config
->get('secret_key') ?: Settings::get('s3fs.secret_key', '');
if ($access_key && $secret_key) {
$credentials = new Credentials($access_key, $secret_key);
$provider = CredentialProvider::chain(CredentialProvider::fromCredentials($credentials), $provider);
}
// Create an S3 client using the provider. This should use the Instance
// Profile provider if this code is running in an AWS instance.
/** @var \Drupal\s3fs\S3fsServiceInterface $s3fs */
$s3fs = \Drupal::service('s3fs');
$client = $s3fs
->getAmazonS3Client($config
->get());
$creds = $client
->getCredentials()
->wait();
$access_key = $creds
->getAccessKeyId();
$secret_key = $creds
->getSecretKey();
$session_token = $creds
->getSecurityToken();
// If not running on an AWS instance the S3 Client doesn't have session
// token, so create an STS client and use the session token from that.
if (empty($credentials) && empty($session_token)) {
$sts_policy_resource = $cors_config
->get('s3fs_sts_policy_resource') ?: '';
$sts = new StsClient([
'region' => $region,
'version' => 'latest',
]);
$sessionToken = $sts
->getFederationToken([
'Name' => 'User1',
'DurationSeconds' => '3600',
'Policy' => json_encode([
'Statement' => [
'Sid' => 'drupals3fscorsid' . time(),
'Action' => [
"s3:PutObject",
"s3:GetObjectAcl",
"s3:GetObject",
"s3:DeleteObjectVersion",
"s3:PutObjectVersionAcl",
"s3:GetObjectVersionAcl",
"s3:DeleteObject",
"s3:PutObjectAcl",
"s3:GetObjectVersion",
],
'Effect' => 'Allow',
'Resource' => $sts_policy_resource,
],
]),
]);
$access_key = $sessionToken['Credentials']['AccessKeyId'];
$secret_key = $sessionToken['Credentials']['SecretAccessKey'];
$session_token = $sessionToken['Credentials']['SessionToken'];
}
// Specify the S3 upload policy.
$policy = [
'expiration' => $expiration
->format('Y-m-d\\TH:i:s\\Z'),
'conditions' => [
[
'bucket' => $bucket,
],
[
'acl' => $acl,
],
[
'starts-with',
'$key',
$s3_key,
],
[
'starts-with',
'$Content-Type',
'',
],
[
'success_action_status' => '201',
],
[
'x-amz-algorithm' => 'AWS4-HMAC-SHA256',
],
[
'x-amz-credential' => $access_key . '/' . $datenow
->format('Ymd') . '/' . $region . '/s3/aws4_request',
],
[
'x-amz-date' => $datenow
->format('Ymd\\THis\\Z'),
],
[
'x-amz-expires' => '21600',
],
],
];
// Include the session token if it exists.
if ($session_token) {
$policy['conditions'][] = [
'x-amz-security-token' => $session_token,
];
}
// Generate a string to sign from the policy.
$base64Policy = base64_encode(json_encode($policy));
// Generate the v4 signing key.
$date_key = hash_hmac('sha256', $datenow
->format('Ymd'), 'AWS4' . $secret_key, TRUE);
$region_key = hash_hmac('sha256', $region, $date_key, TRUE);
$service_key = hash_hmac('sha256', 's3', $region_key, TRUE);
$signing_key = hash_hmac('sha256', 'aws4_request', $service_key, TRUE);
$signature = hash_hmac('sha256', $base64Policy, $signing_key);
$js_settings = [];
// Add the extension list to the page as JavaScript settings.
if (isset($element['#upload_validators']['file_validate_extensions'][0])) {
$js_settings['extension_list'] = implode(',', array_filter(explode(' ', $element['#upload_validators']['file_validate_extensions'][0])));
}
if (isset($element['#max_filesize'])) {
$max_filesize = Bytes::toInt($element['#max_filesize']);
}
elseif (isset($element['#upload_validators']['file_validate_size'])) {
$max_filesize = $element['#upload_validators']['file_validate_size'][0];
}
else {
$max_filesize = Environment::getUploadMaxSize();
}
$js_settings['max_size'] = $max_filesize;
$js_settings['upload_location'] = $element['#upload_location'];
$js_settings['cors_form_data'] = [
'acl' => $acl,
'success_action_status' => 201,
'x-amz-algorithm' => 'AWS4-HMAC-SHA256',
'x-amz-credential' => $access_key . '/' . $datenow
->format('Ymd') . '/' . $region . '/s3/aws4_request',
'x-amz-date' => $datenow
->format('Ymd\\THis\\Z'),
'policy' => $base64Policy,
'x-amz-signature' => $signature,
'x-amz-expires' => '21600',
];
// Include the session token if it exists.
if ($session_token) {
$js_settings['cors_form_data']['x-amz-security-token'] = $session_token;
}
$element_parents = $element['#array_parents'];
// Remove the delta value from element parents if multiple files allowed.
if ($element['#multiple']) {
array_pop($element_parents);
}
// Pass the element parents through to the javascript function.
$js_settings['element_parents'] = implode('/', $element_parents);
// Use s3fs settings for constructing the form action.
$hostname = $config
->get('use_customhost') ? $config
->get('hostname') : 's3.' . $region . '.amazonaws.com';
$endpoint = $config
->get('use_path_style_endpoint') ? $hostname . '/' . $bucket : $bucket . '.' . $hostname;
$js_settings['cors_form_action'] = $cors_config
->get('s3fs_https') . '://' . $endpoint . '/';
$field_name = $element['#field_name'];
if (!empty($element['#field_parents'])) {
$field_name = sprintf('%s_%s', implode('_', $element['#field_parents']), $field_name);
}
$element['upload']['#attached']['drupalSettings']['s3fs_cors'][$field_name] = $js_settings;
$item = $element['#value'];
$item['fids'] = $element['fids']['#value'];
// Add the description field if enabled.
if ($element['#description_field'] && $item['fids']) {
$config = \Drupal::config('file.settings');
$element['description'] = [
'#type' => $config
->get('description.type'),
'#title' => t('Description'),
'#value' => isset($item['description']) ? $item['description'] : '',
'#maxlength' => $config
->get('description.length'),
'#description' => t('The description may be used as the label of the link to the file.'),
];
}
return $element;
}