Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a06bf4b
Add EncryptionConfig API type
TomasMorton Jul 17, 2024
14287a4
Add encryptionType command option for GOOGLE_DEFAULT_ENCRYPTION
TomasMorton Jul 17, 2024
3a664cb
Accept and parse option for GOOGLE_DEFAULT_ENCRYPTION
TomasMorton Jul 17, 2024
685f96f
Set encryption config values in the API request
TomasMorton Jul 17, 2024
56f2895
Add support for USE_BACKUP_ENCRYPTION
TomasMorton Jul 17, 2024
7e66357
Fix casing of helpCommandText constant
TomasMorton Jul 17, 2024
a85be92
Add support for CUSTOMER_MANAGED_ENCRYPTION
TomasMorton Jul 17, 2024
f6ed4d0
Format changes
TomasMorton Jul 17, 2024
3084104
Add missing bracket in encryption type info
TomasMorton Jul 17, 2024
e3d4aeb
Don't allow an empty encryption type
TomasMorton Jul 17, 2024
4feebe0
Validate if kms-key-name is set at the appropriate time
TomasMorton Jul 17, 2024
b397656
Use enum for encryption type
TomasMorton Jul 17, 2024
89d43e6
Throw an error if a key is set without an encryption type
TomasMorton Jul 19, 2024
2869fac
Improve error messages
TomasMorton Jul 19, 2024
b25c476
Specify that USE_BACKUP_ENCRYPTION is the default
TomasMorton Jul 19, 2024
1833a4a
Move undefined option into the switch statement
TomasMorton Jul 19, 2024
2123c60
Improve help text
TomasMorton Jul 26, 2024
4723363
Update --kms-key-name to match new arg in create db
TomasMorton Jul 30, 2024
7261c5a
Remove duplicate `kmsKeyName` declaration.
TomasMorton Jul 30, 2024
6f0ef09
Update help text for --kms-key-name
TomasMorton Jul 30, 2024
8a43d59
Use new API fields
TomasMorton Aug 5, 2024
612a039
Update changelog for firestore restore with encryption config
TomasMorton Aug 27, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- Improve Firebase Data Connect postgres security by granting fine grained SQL privileges to the users the need it. (#7578)
- Remove `dataconnect:sql:migrate` command hard dependency on 'roles/cloudsql.admin'. (#7578)
- Add support for setting the encryption configuration of restored firestore databases (#7483)
54 changes: 53 additions & 1 deletion src/commands/firestore-databases-restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,25 @@ import { logger } from "../logger";
import { requirePermissions } from "../requirePermissions";
import { Emulators } from "../emulator/types";
import { warnEmulatorNotSupported } from "../emulator/commandUtils";
import { FirestoreOptions } from "../firestore/options";
import { EncryptionType, FirestoreOptions } from "../firestore/options";
import { PrettyPrint } from "../firestore/pretty-print";
import { FirebaseError } from "../error";

export const command = new Command("firestore:databases:restore")
.description("Restore a Firestore database in your Firebase project.")
.option("-d, --database <databaseID>", "ID of the database to restore into")
.option("-b, --backup <backup>", "Backup from which to restore")
.option(
"-e, --encryption-type <encryptionType>",
`Encryption method of the restored database; one of ${EncryptionType.USE_SOURCE_ENCRYPTION} (default), ` +
`${EncryptionType.CUSTOMER_MANAGED_ENCRYPTION}, ${EncryptionType.GOOGLE_DEFAULT_ENCRYPTION}`,
)
// TODO(b/356137854): Remove allowlist only message once feature is public GA.
.option(
"-k, --kms-key-name <kmsKeyName>",
"Resource ID of the Cloud KMS key to encrypt the restored database. This " +
"feature is allowlist only in initial launch.",
)
.before(requirePermissions, ["datastore.backups.restoreDatabase"])
.before(warnEmulatorNotSupported, Emulators.FIRESTORE)
.action(async (options: FirestoreOptions) => {
Expand All @@ -32,10 +43,33 @@ export const command = new Command("firestore:databases:restore")
}
const backupName = options.backup;

let encryptionConfig: types.EncryptionConfig | undefined = undefined;
switch (options.encryptionType) {
case EncryptionType.GOOGLE_DEFAULT_ENCRYPTION:
throwIfKmsKeyNameIsSet(options.kmsKeyName);
encryptionConfig = { googleDefaultEncryption: {} };
break;
case EncryptionType.USE_SOURCE_ENCRYPTION:
throwIfKmsKeyNameIsSet(options.kmsKeyName);
encryptionConfig = { useSourceEncryption: {} };
break;
case EncryptionType.CUSTOMER_MANAGED_ENCRYPTION:
encryptionConfig = {
customerManagedEncryption: { kmsKeyName: getKmsKeyOrThrow(options.kmsKeyName) },
};
break;
case undefined:
throwIfKmsKeyNameIsSet(options.kmsKeyName);
break;
default:
throw new FirebaseError(`Invalid value for flag --encryption-type. ${helpCommandText}`);
}

const databaseResp: types.DatabaseResp = await api.restoreDatabase(
options.project,
databaseId,
backupName,
encryptionConfig,
);

if (options.json) {
Expand All @@ -55,4 +89,22 @@ export const command = new Command("firestore:databases:restore")
}

return databaseResp;

function throwIfKmsKeyNameIsSet(kmsKeyName: string | undefined): void {
if (kmsKeyName) {
throw new FirebaseError(
"--kms-key-name can only be set when specifying an --encryption-type " +
`of ${EncryptionType.CUSTOMER_MANAGED_ENCRYPTION}.`,
);
}
}

function getKmsKeyOrThrow(kmsKeyName: string | undefined): string {
if (kmsKeyName) return kmsKeyName;

throw new FirebaseError(
"--kms-key-name must be provided when specifying an --encryption-type " +
`of ${EncryptionType.CUSTOMER_MANAGED_ENCRYPTION}.`,
);
}
});
12 changes: 12 additions & 0 deletions src/firestore/api-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export interface DatabaseResp {
export interface RestoreDatabaseReq {
databaseId: string;
backup: string;
encryptionConfig?: EncryptionConfig;
}

export enum RecurrenceType {
Expand All @@ -171,3 +172,14 @@ export interface CmekConfig {
kmsKeyName: string;
activeKeyVersion?: string[];
}

type UseGoogleDefaultEncryption = { googleDefaultEncryption: Record<string, never> };
type UseSourceEncryption = { useSourceEncryption: Record<string, never> };
type UseCustomerManagedEncryption = { customerManagedEncryption: CustomerManagedEncryptionOptions };
type CustomerManagedEncryptionOptions = {
kmsKeyName: string;
};
export type EncryptionConfig =
| UseCustomerManagedEncryption
| UseSourceEncryption
| UseGoogleDefaultEncryption;
3 changes: 3 additions & 0 deletions src/firestore/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,16 +695,19 @@ export class FirestoreApi {
* @param project the Firebase project id.
* @param databaseId the ID of the Firestore Database to be restored into
* @param backupName Name of the backup from which to restore
* @param encryptionConfig the encryption configuration of the restored database
*/
async restoreDatabase(
project: string,
databaseId: string,
backupName: string,
encryptionConfig?: types.EncryptionConfig,
): Promise<types.DatabaseResp> {
const url = `/projects/${project}/databases:restore`;
const payload: types.RestoreDatabaseReq = {
databaseId,
backup: backupName,
encryptionConfig: encryptionConfig,
};
const options = { queryParams: { databaseId: databaseId } };
const res = await this.apiClient.post<
Expand Down
11 changes: 10 additions & 1 deletion src/firestore/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export interface FirestoreOptions extends Options {
type?: types.DatabaseType;
deleteProtection?: types.DatabaseDeleteProtectionStateOption;
pointInTimeRecoveryEnablement?: types.PointInTimeRecoveryEnablementOption;
kmsKeyName?: string;

// backup schedules
backupSchedule?: string;
Expand All @@ -28,4 +27,14 @@ export interface FirestoreOptions extends Options {

// backups
backup?: string;

// CMEK
encryptionType?: EncryptionType;
kmsKeyName?: string;
}

export enum EncryptionType {
CUSTOMER_MANAGED_ENCRYPTION = "CUSTOMER_MANAGED_ENCRYPTION",
USE_SOURCE_ENCRYPTION = "USE_SOURCE_ENCRYPTION",
GOOGLE_DEFAULT_ENCRYPTION = "GOOGLE_DEFAULT_ENCRYPTION",
}