Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Storage/src/Bucket.php
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,17 @@ public function delete(array $options = [])
* `projects/my-project/locations/kr-location/keyRings/my-kr/cryptoKeys/my-key`.
* Please note the KMS key ring must use the same location as the
* bucket.
* @type array $encryption.googleManagedEncryptionEnforcementConfig
* Enforcement configuration for Google-managed encryption.
* @type array $encryption.customerManagedEncryptionEnforcementConfig
* Enforcement configuration for Cloud KMS (customer-managed) encryption.
* @type array $encryption.customerSuppliedEncryptionEnforcementConfig
* Enforcement configuration for customer-supplied encryption keys (CSEK).
* @type string $encryption.*.restrictionMode The restriction state of
* the encryption policy. Acceptable values are `"NotRestricted"`
* and `"FullyRestricted"`.
* @type string $encryption.*.effectiveTime [readonly] The time from which
* the policy was effective in RFC 3339 format.
* @type bool $defaultEventBasedHold When `true`, newly created objects
* in this bucket will be retained indefinitely until an event
* occurs, signified by the hold's release.
Expand Down
72 changes: 72 additions & 0 deletions Storage/src/Connection/ServiceDefinition/storage-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,78 @@
"defaultKmsKeyName": {
"type": "string",
"description": "A Cloud KMS key that will be used to encrypt objects inserted into this bucket, if no encryption method is specified."
},
"googleManagedEncryptionEnforcementConfig": {
"type": "object",
"description": "Enforcement configuration for Google-managed encryption.",
"properties": {
"restrictionMode": {
"type": "string",
"description": "The restriction state of the encryption policy.",
"enum": [
"NotRestricted",
"FullyRestricted"
],
"enumDescriptions": [
"No restrictions are applied to this encryption type.",
"Specific restrictions are enforced for this encryption type."
]
},
"effectiveTime": {
"type": "string",
"description": "Server-determined value indicating when this configuration became effective. In RFC 3339 format.",
"format": "date-time",
"readOnly": true
}
}
},
"customerManagedEncryptionEnforcementConfig": {
"type": "object",
"description": "Enforcement configuration for Cloud KMS (customer-managed) encryption.",
"properties": {
"restrictionMode": {
"type": "string",
"description": "The restriction state of the encryption policy.",
"enum": [
"NotRestricted",
"FullyRestricted"
],
"enumDescriptions": [
"No restrictions are applied to this encryption type.",
"Specific restrictions are enforced for this encryption type."
]
},
"effectiveTime": {
"type": "string",
"description": "Server-determined value indicating when this configuration became effective. In RFC 3339 format.",
"format": "date-time",
"readOnly": true
}
}
},
"customerSuppliedEncryptionEnforcementConfig": {
"type": "object",
"description": "Enforcement configuration for customer-supplied encryption keys (CSEK).",
"properties": {
"restrictionMode": {
"type": "string",
"description": "The restriction state of the encryption policy.",
"enum": [
"NotRestricted",
"FullyRestricted"
],
"enumDescriptions": [
"No restrictions are applied to this encryption type.",
"Specific restrictions are enforced for this encryption type."
]
},
"effectiveTime": {
"type": "string",
"description": "Server-determined value indicating when this configuration became effective. In RFC 3339 format.",
"format": "date-time",
"readOnly": true
}
}
}
}
},
Expand Down
11 changes: 11 additions & 0 deletions Storage/src/StorageClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,17 @@ public function restore(string $name, string $generation, array $options = [])
* `projects/my-project/locations/kr-location/keyRings/my-kr/cryptoKeys/my-key`.
* Please note the KMS key ring must use the same location as the
* bucket.
* @type array $encryption.googleManagedEncryptionEnforcementConfig
* Enforcement configuration for Google-managed encryption.
* @type array $encryption.customerManagedEncryptionEnforcementConfig
* Enforcement configuration for Cloud KMS (customer-managed) encryption.
* @type array $encryption.customerSuppliedEncryptionEnforcementConfig
* Enforcement configuration for customer-supplied encryption keys (CSEK).
* @type string $encryption.*.restrictionMode The restriction state of
* the encryption policy. Acceptable values are `"NotRestricted"`
* and `"FullyRestricted"`.
* @type string $encryption.*.effectiveTime [readonly] The time from which
* the policy was effective in RFC 3339 format.
* @type bool $defaultEventBasedHold When `true`, newly created objects
* in this bucket will be retained indefinitely until an event
* occurs, signified by the hold's release.
Expand Down
48 changes: 48 additions & 0 deletions Storage/tests/System/KmsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

use Google\Cloud\Core\Testing\System\KeyManager;
use Google\Cloud\Storage\StorageObject;
use Google\Cloud\Core\Exception\ServiceException;

/**
* @group storage
Expand Down Expand Up @@ -155,6 +156,53 @@ public function testRotatesKmsToCustomerSuppliedEncrpytion()
$this->assertEquals(self::DATA, $rewrittenObject->downloadAsString());
}

public function testUploadFailsWhenCsekViolatesCmekEnforcement()
{
self::$bucket->update([
'encryption' => [
'customerSuppliedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
]
]
]);

$this->expectException(ServiceException::class);
$this->expectExceptionCode(412);

try {
$key = base64_encode(openssl_random_pseudo_bytes(32));
self::$bucket->upload('data', [
'name' => uniqid(self::TESTING_PREFIX),
'encryptionKey' => $key
]);
} finally {
self::$bucket->update(['encryption' => null]);
}
}

public function testUploadSucceedsWhenNotRestricted()
{
self::$bucket->update([
'encryption' => [
'defaultKmsKeyName' => self::$keyName1,
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'NotRestricted'
]
]
]);
$object = null;
try {
$object = self::$bucket->upload('data', ['name' => uniqid(self::TESTING_PREFIX)]);

$this->assertTrue($object->exists());
} finally {
if ($object) {
$object->delete();
}
self::$bucket->update(['encryption' => null]);
}
}

/**
* @param array $options
* @return StorageObject
Expand Down
53 changes: 53 additions & 0 deletions Storage/tests/System/ManageBucketsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -524,4 +524,57 @@ public function testSoftDeleteBucket()
self::$client->restore($name, $generation);
$this->assertTrue(self::$client->bucket($name)->exists());
}

/**
* @dataProvider encryptionEnforcementConfigs
*/
public function testCreateAndUpdateBucketWithEncryptionEnforcement($config)
{
$name = uniqid(self::TESTING_PREFIX);
$options = ['encryption' => $config];

// Test Creation
$bucket = self::createBucket(self::$client, $name, $options);
$this->assertArrayHasKey('encryption', $bucket->info());

$encryption = $bucket->info()['encryption'];
foreach ($config as $key => $val) {
$this->assertEquals($val['restrictionMode'], $encryption[$key]['restrictionMode']);
$this->assertArrayHasKey('effectiveTime', $encryption[$key]);
}

// Test Update (Changing restrictionMode)
$updatedConfig = $config;
$firstKey = array_key_first($updatedConfig);
$updatedConfig[$firstKey]['restrictionMode'] = 'NotRestricted';

$info = $bucket->update(['encryption' => $updatedConfig]);
$this->assertEquals(
'NotRestricted',
$info['encryption'][$firstKey]['restrictionMode']
);
}

public function encryptionEnforcementConfigs()
{
return [
[
[
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
]
]
],
[
[
'customerManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
],
'customerSuppliedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
]
]
]
];
}
}
26 changes: 26 additions & 0 deletions Storage/tests/Unit/BucketTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,32 @@ public function testUpdatesDataWithLifecycleBuilder()
);
}


public function testUpdatesEncryptionEnforcementConfig()
{
$encryptionConfig = [
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
]
];
$this->connection->patchBucket(Argument::withEntry('encryption', $encryptionConfig))
->shouldBeCalled()
->willReturn([
'name' => self::BUCKET_NAME,
'encryption' => $encryptionConfig
]);

$bucket = $this->getBucket(['name' => self::BUCKET_NAME]);

$bucket->update(['encryption' => $encryptionConfig]);

$this->assertArrayHasKey('encryption', $bucket->info());
$this->assertEquals(
'FullyRestricted',
$bucket->info()['encryption']['googleManagedEncryptionEnforcementConfig']['restrictionMode']
);
}

public function testGetsInfo()
{
$bucketInfo = [
Expand Down
31 changes: 31 additions & 0 deletions Storage/tests/Unit/StorageClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,37 @@ public function testCreatesBucketWithLifecycleBuilder()
);
}

public function testCreatesBucketWithEncryptionEnforcement()
{
$bucketName = 'encrypted-bucket';
$encryptionConfig = [
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
],
'customerManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'NotRestricted'
]
];
$this->connection->projectId()
->willReturn(self::PROJECT);
$this->connection
->insertBucket(Argument::allOf(
Argument::withEntry('name', $bucketName),
Argument::withEntry('project', self::PROJECT),
Argument::withEntry('encryption', $encryptionConfig)
))
->willReturn(['name' => $bucketName]);
$this->client->___setProperty('connection', $this->connection->reveal());

$this->assertInstanceOf(
Bucket::class,
$this->client->createBucket(
$bucketName,
['encryption' => $encryptionConfig]
)
);
}

public function testRegisteringStreamWrapper()
{
$this->assertTrue($this->client->registerStreamWrapper());
Expand Down
Loading