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
12 changes: 12 additions & 0 deletions Storage/src/Bucket.php
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,18 @@ 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 Google
* managed encryption enforcement configuration.
* @type string $encryption.googleManagedEncryptionEnforcementConfig.restrictionMode
* Restriction mode for Google managed encryption.
* @type array $encryption.customerManagedEncryptionEnforcementConfig Customer
* managed encryption enforcement configuration.
* @type string $encryption.customerManagedEncryptionEnforcementConfig.restrictionMode
* Restriction mode for customer managed encryption.
* @type array $encryption.customerSuppliedEncryptionEnforcementConfig Customer
* supplied encryption enforcement configuration.
* @type string $encryption.customerSuppliedEncryptionEnforcementConfig.restrictionMode
* Restriction mode for customer supplied encryption.
* @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
69 changes: 69 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,75 @@
"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": "If set, the new objects created in this bucket must comply with this enforcement config. Changing this has no effect on existing objects; it applies to new objects only. If omitted, the new objects are allowed to be encrypted with Google Managed Encryption type by default.",
"properties": {
"restrictionMode": {
"type": "string",
"description": "Restriction mode for Google-Managed Encryption Keys. Defaults to NotRestricted.",
"enum": [
"NotRestricted",
"FullyRestricted"
],
"enumDescriptions": [
"Creation of new objects with Google Managed Encryption is not restricted.",
"Creation of new objects with Google Managed Encryption is fully restricted."
]
},
"effectiveTime": {
"type": "string",
"description": "Server-determined value that indicates the time from which configuration was enforced and effective. This value is in RFC 3339 format.",
"format": "date-time"
}
}
},
"customerManagedEncryptionEnforcementConfig": {
"type": "object",
"description": "If set, the new objects created in this bucket must comply with this enforcement config. Changing this has no effect on existing objects; it applies to new objects only. If omitted, the new objects are allowed to be encrypted with Customer Managed Encryption type by default.",
"properties": {
"restrictionMode": {
"type": "string",
"description": "Restriction mode for Customer-Managed Encryption Keys. Defaults to NotRestricted.",
"enum": [
"NotRestricted",
"FullyRestricted"
],
"enumDescriptions": [
"Creation of new objects with Customer-Managed Encryption is not restricted.",
"Creation of new objects with Customer-Managed Encryption is fully restricted."
]
},
"effectiveTime": {
"type": "string",
"description": "Server-determined value that indicates the time from which configuration was enforced and effective. This value is in RFC 3339 format.",
"format": "date-time"
}
}
},
"customerSuppliedEncryptionEnforcementConfig": {
"type": "object",
"description": "If set, the new objects created in this bucket must comply with this enforcement config. Changing this has no effect on existing objects; it applies to new objects only. If omitted, the new objects are allowed to be encrypted with Customer Supplied Encryption type by default.",
"properties": {
"restrictionMode": {
"type": "string",
"description": "Restriction mode for Customer-Supplied Encryption Keys. Defaults to NotRestricted.",
"enum": [
"NotRestricted",
"FullyRestricted"
],
"enumDescriptions": [
"Creation of new objects with Customer-Supplied Encryption is not restricted.",
"Creation of new objects with Customer-Supplied Encryption is fully restricted."
]
},
"effectiveTime": {
"type": "string",
"description": "Server-determined value that indicates the time from which configuration was enforced and effective. This value is in RFC 3339 format.",
"format": "date-time"
}
}
}
}
},
Expand Down
12 changes: 12 additions & 0 deletions Storage/src/StorageClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,18 @@ 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 Google
* managed encryption enforcement configuration.
* @type string $encryption.googleManagedEncryptionEnforcementConfig.restrictionMode
* Restriction mode for Google managed encryption.
* @type array $encryption.customerManagedEncryptionEnforcementConfig Customer
* managed encryption enforcement configuration.
* @type string $encryption.customerManagedEncryptionEnforcementConfig.restrictionMode
* Restriction mode for customer managed encryption.
* @type array $encryption.customerSuppliedEncryptionEnforcementConfig Customer
* supplied encryption enforcement configuration.
* @type string $encryption.customerSuppliedEncryptionEnforcementConfig.restrictionMode
* Restriction mode for customer supplied encryption.
* @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
44 changes: 44 additions & 0 deletions Storage/tests/System/KmsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,50 @@ public function testRotatesKmsToCustomerSuppliedEncrpytion()
$this->assertEquals(self::DATA, $rewrittenObject->downloadAsString());
}

public function testEncryptionEnforcementConfig()
{
self::$bucket->update([
'encryption' => [
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'NotRestricted'
],
'customerManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'NotRestricted'
],
'customerSuppliedEncryptionEnforcementConfig' => [
'restrictionMode' => 'NotRestricted'
],
]
]);

$info = self::$bucket->info();
$this->assertArrayHasKey('encryption', $info);
$this->assertArrayHasKey('googleManagedEncryptionEnforcementConfig', $info['encryption']);
$this->assertArrayHasKey('customerManagedEncryptionEnforcementConfig', $info['encryption']);
$this->assertArrayHasKey('customerSuppliedEncryptionEnforcementConfig', $info['encryption']);
$this->assertEquals(
'NotRestricted',
$info['encryption']['googleManagedEncryptionEnforcementConfig']['restrictionMode']
);
$this->assertEquals(
'NotRestricted',
$info['encryption']['customerManagedEncryptionEnforcementConfig']['restrictionMode']
);
$this->assertEquals(
'NotRestricted',
$info['encryption']['customerSuppliedEncryptionEnforcementConfig']['restrictionMode']
);

// Reset
self::$bucket->update([
'encryption' => [
'googleManagedEncryptionEnforcementConfig' => null,
'customerManagedEncryptionEnforcementConfig' => null,
'customerSuppliedEncryptionEnforcementConfig' => null,
]
]);
}

/**
* @param array $options
* @return StorageObject
Expand Down
137 changes: 137 additions & 0 deletions Storage/tests/Unit/BucketTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,143 @@ public function testUpdateAutoclassConfig($terminalStorageClass)
$this->assertArrayHasKey('terminalStorageClassUpdateTime', $autoclassInfo);
}

public function testUpdateEncryptionEnforcementConfig()
{
$encryptionConfig = [
'encryption' => [
'defaultKmsKeyName' => 'key',
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
],
'customerManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
],
'customerSuppliedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
],
],
];
$this->connection->patchBucket(Argument::any())->willReturn(
['name' => 'bucket'] +
$encryptionConfig
);
$bucket = $this->getBucket([
'name' => 'bucket',
]);

$bucket->update($encryptionConfig);

$this->assertArrayHasKey('encryption', $bucket->info());
$encryptionInfo = $bucket->info()['encryption'];
$this->assertEquals('key', $encryptionInfo['defaultKmsKeyName']);
$this->assertEquals(
'FullyRestricted',
$encryptionInfo['googleManagedEncryptionEnforcementConfig']['restrictionMode']
);
$this->assertEquals(
'FullyRestricted',
$encryptionInfo['customerManagedEncryptionEnforcementConfig']['restrictionMode']
);
$this->assertEquals(
'FullyRestricted',
$encryptionInfo['customerSuppliedEncryptionEnforcementConfig']['restrictionMode']
);
}

public function testUpdatePartialEncryptionEnforcementConfig()
{
$encryptionConfig = [
'encryption' => [
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
],
],
];
$this->connection->patchBucket(Argument::any())->willReturn(
['name' => 'bucket'] +
$encryptionConfig
);
$bucket = $this->getBucket([
'name' => 'bucket',
]);

$bucket->update($encryptionConfig);

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

public function testReloadWithEncryptionEnforcementConfig()
{
$encryptionConfig = [
'encryption' => [
'defaultKmsKeyName' => 'key',
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted',
'effectiveTime' => '2025-12-18T18:13:15Z'
],
'customerManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'NotRestricted',
'effectiveTime' => '2025-12-18T18:13:15Z'
],
'customerSuppliedEncryptionEnforcementConfig' => [
'restrictionMode' => 'NotRestricted',
'effectiveTime' => '2025-12-18T18:13:15Z'
],
],
];
$this->connection->getBucket(Argument::any())->willReturn(
['name' => self::BUCKET_NAME] +
$encryptionConfig
);
$bucket = $this->getBucket();

$info = $bucket->reload();

$this->assertArrayHasKey('encryption', $info);
$encryptionInfo = $info['encryption'];
$this->assertEquals('key', $encryptionInfo['defaultKmsKeyName']);
$this->assertEquals(
'FullyRestricted',
$encryptionInfo['googleManagedEncryptionEnforcementConfig']['restrictionMode']
);
$this->assertEquals(
'2025-12-18T18:13:15Z',
$encryptionInfo['googleManagedEncryptionEnforcementConfig']['effectiveTime']
);
}

public function testReloadWithUnknownEncryptionEnforcementRestrictionMode()
{
$encryptionConfig = [
'encryption' => [
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'NOT_YET_DEFINED'
],
],
];
$this->connection->getBucket(Argument::any())->willReturn(
['name' => self::BUCKET_NAME] +
$encryptionConfig
);
$bucket = $this->getBucket();

$info = $bucket->reload();

$this->assertArrayHasKey('encryption', $info);
$encryptionInfo = $info['encryption'];
$this->assertEquals(
'NOT_YET_DEFINED',
$encryptionInfo['googleManagedEncryptionEnforcementConfig']['restrictionMode']
);
}

public function testUpdatesDataWithLifecycleBuilder()
{
$lifecycleArr = ['test' => 'test'];
Expand Down
33 changes: 33 additions & 0 deletions Storage/tests/Unit/StorageClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,39 @@ public function testCreatesDualRegionBucket()
$this->assertInstanceOf(Bucket::class, $createdBucket);
}

public function testCreateBucketWithEncryptionEnforcementConfig()
{
$bucket = 'bucket';
$encryptionConfig = [
'encryption' => [
'defaultKmsKeyName' => 'key',
'googleManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
],
'customerManagedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
],
'customerSuppliedEncryptionEnforcementConfig' => [
'restrictionMode' => 'FullyRestricted'
],
],
];
$this->connection->projectId()
->willReturn(self::PROJECT);
$this->connection
->insertBucket([
'project' => self::PROJECT,
'encryption' => $encryptionConfig['encryption'],
'name' => $bucket
])
->willReturn(['name' => $bucket] + $encryptionConfig);
$this->client->___setProperty('connection', $this->connection->reveal());

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

public function testCreatesBucketWithLifecycleBuilder()
{
$bucket = 'bucket';
Expand Down
Loading