@striae-org/striae 5.3.2 → 5.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/auth/auth.module.css +531 -0
- package/app/components/auth/mfa-enrollment.tsx +132 -79
- package/app/components/auth/mfa-totp-enrollment.tsx +231 -0
- package/app/components/auth/mfa-verification.tsx +162 -33
- package/app/components/{sidebar/cases/cases-modal.tsx → navbar/case-modals/all-cases-modal.tsx} +4 -4
- package/app/components/navbar/case-modals/archive-case-modal.tsx +9 -10
- package/app/components/navbar/case-modals/case-modal-shared.module.css +88 -0
- package/app/components/navbar/case-modals/delete-case-modal.tsx +9 -10
- package/app/components/navbar/case-modals/export-case-modal.tsx +9 -10
- package/app/components/navbar/case-modals/export-confirmations-modal.tsx +9 -10
- package/app/components/navbar/case-modals/open-case-modal.tsx +4 -4
- package/app/components/navbar/case-modals/rename-case-modal.tsx +9 -10
- package/app/components/navbar/navbar.tsx +1 -1
- package/app/components/sidebar/files/delete-files-modal.tsx +3 -3
- package/app/components/sidebar/files/files-modal.module.css +29 -0
- package/app/components/sidebar/notes/{class-details-fields.tsx → class-details/class-details-fields.tsx} +1 -1
- package/app/components/sidebar/notes/{class-details-modal.tsx → class-details/class-details-modal.tsx} +1 -1
- package/app/components/sidebar/notes/{class-details-sections.tsx → class-details/class-details-sections.tsx} +1 -1
- package/app/components/sidebar/notes/notes-editor-form.tsx +2 -2
- package/app/components/sidebar/notes/notes-editor-modal.tsx +6 -6
- package/app/components/sidebar/notes/notes.module.css +52 -0
- package/app/components/toolbar/toolbar-color-selector.tsx +8 -8
- package/app/components/toolbar/toolbar.module.css +181 -2
- package/app/components/user/delete-account.tsx +7 -7
- package/app/components/user/inactivity-warning.tsx +6 -6
- package/app/components/user/manage-profile.tsx +18 -1
- package/app/components/user/mfa-enrolled-factors.tsx +117 -0
- package/app/components/user/mfa-phone-update.tsx +8 -4
- package/app/components/user/mfa-totp-section.tsx +446 -0
- package/app/components/user/user.module.css +665 -0
- package/app/routes/striae/striae.tsx +1 -1
- package/app/services/audit/audit.service.ts +1 -1
- package/app/services/audit/builders/audit-event-builders-user-security.ts +4 -2
- package/app/services/firebase/errors.ts +2 -0
- package/app/utils/auth/mfa.ts +35 -1
- package/package.json +23 -28
- package/scripts/deploy-all.sh +166 -0
- package/scripts/deploy-config/modules/env-utils.sh +322 -0
- package/scripts/deploy-config/modules/keys.sh +404 -0
- package/scripts/deploy-config/modules/prompt.sh +375 -0
- package/scripts/deploy-config/modules/scaffolding.sh +310 -0
- package/scripts/deploy-config/modules/validation.sh +354 -0
- package/scripts/deploy-config.sh +236 -0
- package/scripts/deploy-pages-secrets.sh +231 -0
- package/scripts/deploy-pages.sh +34 -0
- package/scripts/deploy-primershear-emails.sh +167 -0
- package/scripts/deploy-worker-secrets.sh +385 -0
- package/scripts/dev.cjs +23 -0
- package/scripts/enable-totp-mfa.mjs +57 -0
- package/scripts/install-workers.sh +87 -0
- package/scripts/run-eslint.cjs +43 -0
- package/scripts/unenroll-totp-mfa.mjs +82 -0
- package/scripts/update-compatibility-dates.cjs +124 -0
- package/scripts/update-markdown-versions.cjs +43 -0
- package/workers/audit-worker/.editorconfig +12 -0
- package/workers/audit-worker/.prettierrc +6 -0
- package/workers/audit-worker/package.json +1 -1
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/.editorconfig +12 -0
- package/workers/data-worker/.prettierrc +6 -0
- package/workers/data-worker/package.json +1 -1
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/.editorconfig +12 -0
- package/workers/image-worker/.prettierrc +6 -0
- package/workers/image-worker/package.json +1 -1
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/.editorconfig +12 -0
- package/workers/pdf-worker/.prettierrc +6 -0
- package/workers/pdf-worker/package.json +1 -1
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/.editorconfig +12 -0
- package/workers/user-worker/.prettierrc +6 -0
- package/workers/user-worker/package.json +1 -1
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
- package/app/components/auth/mfa-enrollment.module.css +0 -276
- package/app/components/auth/mfa-verification.module.css +0 -259
- package/app/components/navbar/case-modals/archive-case-modal.module.css +0 -34
- package/app/components/navbar/case-modals/delete-case-modal.module.css +0 -9
- package/app/components/navbar/case-modals/export-case-modal.module.css +0 -27
- package/app/components/navbar/case-modals/export-confirmations-modal.module.css +0 -24
- package/app/components/navbar/case-modals/open-case-modal.module.css +0 -82
- package/app/components/navbar/case-modals/rename-case-modal.module.css +0 -9
- package/app/components/sidebar/files/delete-files-modal.module.css +0 -26
- package/app/components/sidebar/notes/notes-editor-modal.module.css +0 -49
- package/app/components/toolbar/toolbar-color-selector.module.css +0 -171
- package/app/components/user/delete-account.module.css +0 -277
- package/app/components/user/inactivity-warning.module.css +0 -148
- package/app/components/user/manage-profile.module.css +0 -192
- package/app/routes/auth/login.module.css +0 -523
- package/app/routes/auth/login.tsx +0 -705
- package/workers/audit-worker/worker-configuration.d.ts +0 -7448
- package/workers/data-worker/worker-configuration.d.ts +0 -7448
- package/workers/image-worker/worker-configuration.d.ts +0 -7448
- package/workers/pdf-worker/worker-configuration.d.ts +0 -7447
- package/workers/user-worker/worker-configuration.d.ts +0 -7450
- /package/app/components/{sidebar → navbar}/case-import/case-import.module.css +0 -0
- /package/app/components/{sidebar → navbar}/case-import/case-import.tsx +0 -0
- /package/app/components/{sidebar → navbar}/case-import/components/CasePreviewSection.tsx +0 -0
- /package/app/components/{sidebar → navbar}/case-import/components/ConfirmationDialog.tsx +0 -0
- /package/app/components/{sidebar → navbar}/case-import/components/ConfirmationPreviewSection.tsx +0 -0
- /package/app/components/{sidebar → navbar}/case-import/components/ExistingCaseSection.tsx +0 -0
- /package/app/components/{sidebar → navbar}/case-import/components/FileSelector.tsx +0 -0
- /package/app/components/{sidebar → navbar}/case-import/components/ProgressSection.tsx +0 -0
- /package/app/components/{sidebar → navbar}/case-import/hooks/useFilePreview.ts +0 -0
- /package/app/components/{sidebar → navbar}/case-import/hooks/useImportExecution.ts +0 -0
- /package/app/components/{sidebar → navbar}/case-import/hooks/useImportState.ts +0 -0
- /package/app/components/{sidebar → navbar}/case-import/index.ts +0 -0
- /package/app/components/{sidebar → navbar}/case-import/utils/file-validation.ts +0 -0
- /package/app/components/{sidebar/cases/cases-modal.module.css → navbar/case-modals/all-cases-modal.module.css} +0 -0
- /package/app/components/sidebar/notes/{class-details-shared.ts → class-details/class-details-shared.ts} +0 -0
- /package/app/components/sidebar/notes/{use-class-details-state.ts → class-details/use-class-details-state.ts} +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
is_admin_service_placeholder() {
|
|
4
|
+
local value="$1"
|
|
5
|
+
local normalized=$(echo "$value" | tr '[:upper:]' '[:lower:]')
|
|
6
|
+
|
|
7
|
+
[[ -z "$normalized" || "$normalized" == your-* || "$normalized" == *"your_private_key"* ]]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
load_admin_service_credentials() {
|
|
11
|
+
local admin_service_path="app/config/admin-service.json"
|
|
12
|
+
|
|
13
|
+
if [ ! -f "$admin_service_path" ]; then
|
|
14
|
+
echo -e "${RED}❌ Error: Required Firebase admin service file not found: $admin_service_path${NC}"
|
|
15
|
+
echo -e "${YELLOW} Create app/config/admin-service.json with service account credentials.${NC}"
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
local service_project_id
|
|
20
|
+
local service_client_email
|
|
21
|
+
local service_private_key
|
|
22
|
+
|
|
23
|
+
if ! service_project_id=$(node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); process.stdout.write(data.project_id || '');" "$admin_service_path"); then
|
|
24
|
+
echo -e "${RED}❌ Error: Could not parse project_id from $admin_service_path${NC}"
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if ! service_client_email=$(node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); process.stdout.write(data.client_email || '');" "$admin_service_path"); then
|
|
29
|
+
echo -e "${RED}❌ Error: Could not parse client_email from $admin_service_path${NC}"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
if ! service_private_key=$(node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); process.stdout.write(data.private_key || '');" "$admin_service_path"); then
|
|
34
|
+
echo -e "${RED}❌ Error: Could not parse private_key from $admin_service_path${NC}"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
local normalized_private_key="${service_private_key//$'\r'/}"
|
|
39
|
+
normalized_private_key="${normalized_private_key//$'\n'/\\n}"
|
|
40
|
+
|
|
41
|
+
if is_admin_service_placeholder "$service_project_id"; then
|
|
42
|
+
echo -e "${RED}❌ Error: project_id in $admin_service_path is missing or placeholder${NC}"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if is_admin_service_placeholder "$service_client_email" || [[ "$service_client_email" != *".gserviceaccount.com"* ]]; then
|
|
47
|
+
echo -e "${RED}❌ Error: client_email in $admin_service_path is invalid${NC}"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if is_admin_service_placeholder "$normalized_private_key" || [[ "$normalized_private_key" != *"-----BEGIN PRIVATE KEY-----"* ]] || [[ "$normalized_private_key" != *"-----END PRIVATE KEY-----"* ]]; then
|
|
52
|
+
echo -e "${RED}❌ Error: private_key in $admin_service_path is invalid${NC}"
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
PROJECT_ID="$service_project_id"
|
|
57
|
+
export PROJECT_ID
|
|
58
|
+
write_env_var "PROJECT_ID" "$PROJECT_ID"
|
|
59
|
+
|
|
60
|
+
FIREBASE_SERVICE_ACCOUNT_EMAIL="$service_client_email"
|
|
61
|
+
export FIREBASE_SERVICE_ACCOUNT_EMAIL
|
|
62
|
+
write_env_var "FIREBASE_SERVICE_ACCOUNT_EMAIL" "$FIREBASE_SERVICE_ACCOUNT_EMAIL"
|
|
63
|
+
|
|
64
|
+
FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY="$normalized_private_key"
|
|
65
|
+
export FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY
|
|
66
|
+
write_env_var "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" "$FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY"
|
|
67
|
+
|
|
68
|
+
echo -e "${GREEN}✅ Imported Firebase service account credentials from $admin_service_path${NC}"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
generate_manifest_signing_key_pair() {
|
|
72
|
+
local private_key_file
|
|
73
|
+
local public_key_file
|
|
74
|
+
private_key_file=$(mktemp)
|
|
75
|
+
public_key_file=$(mktemp)
|
|
76
|
+
|
|
77
|
+
if ! node -e "const { generateKeyPairSync } = require('crypto'); const fs = require('fs'); const pair = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); fs.writeFileSync(process.argv[1], pair.privateKey, 'utf8'); fs.writeFileSync(process.argv[2], pair.publicKey, 'utf8');" "$private_key_file" "$public_key_file"; then
|
|
78
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
79
|
+
return 1
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
local private_key_pem
|
|
83
|
+
local public_key_pem
|
|
84
|
+
private_key_pem=$(cat "$private_key_file")
|
|
85
|
+
public_key_pem=$(cat "$public_key_file")
|
|
86
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
87
|
+
|
|
88
|
+
private_key_pem="${private_key_pem//$'\r'/}"
|
|
89
|
+
public_key_pem="${public_key_pem//$'\r'/}"
|
|
90
|
+
|
|
91
|
+
MANIFEST_SIGNING_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
|
|
92
|
+
MANIFEST_SIGNING_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
|
|
93
|
+
|
|
94
|
+
export MANIFEST_SIGNING_PRIVATE_KEY
|
|
95
|
+
export MANIFEST_SIGNING_PUBLIC_KEY
|
|
96
|
+
|
|
97
|
+
write_env_var "MANIFEST_SIGNING_PRIVATE_KEY" "$MANIFEST_SIGNING_PRIVATE_KEY"
|
|
98
|
+
write_env_var "MANIFEST_SIGNING_PUBLIC_KEY" "$MANIFEST_SIGNING_PUBLIC_KEY"
|
|
99
|
+
|
|
100
|
+
return 0
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
configure_manifest_signing_credentials() {
|
|
104
|
+
echo -e "${BLUE}🛡️ MANIFEST SIGNING CONFIGURATION${NC}"
|
|
105
|
+
echo "================================="
|
|
106
|
+
|
|
107
|
+
local should_generate="false"
|
|
108
|
+
restore_env_var_from_backup_if_missing "MANIFEST_SIGNING_PRIVATE_KEY"
|
|
109
|
+
restore_env_var_from_backup_if_missing "MANIFEST_SIGNING_PUBLIC_KEY"
|
|
110
|
+
restore_env_var_from_backup_if_missing "MANIFEST_SIGNING_KEY_ID"
|
|
111
|
+
|
|
112
|
+
if [ -z "$MANIFEST_SIGNING_PRIVATE_KEY" ] || is_placeholder "$MANIFEST_SIGNING_PRIVATE_KEY" || [ -z "$MANIFEST_SIGNING_PUBLIC_KEY" ] || is_placeholder "$MANIFEST_SIGNING_PUBLIC_KEY"; then
|
|
113
|
+
should_generate="true"
|
|
114
|
+
else
|
|
115
|
+
if confirm_key_pair_regeneration "manifest signing" "Regenerating this key pair can invalidate verification for signatures tied to the previous key ID."; then
|
|
116
|
+
should_generate="true"
|
|
117
|
+
fi
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
if [ "$should_generate" = "true" ]; then
|
|
121
|
+
echo -e "${YELLOW}Generating manifest signing RSA key pair...${NC}"
|
|
122
|
+
if generate_manifest_signing_key_pair; then
|
|
123
|
+
echo -e "${GREEN}✅ Manifest signing key pair generated${NC}"
|
|
124
|
+
else
|
|
125
|
+
echo -e "${RED}❌ Error: Failed to generate manifest signing key pair${NC}"
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
else
|
|
129
|
+
echo -e "${GREEN}✅ Keeping current manifest signing key pair${NC}"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
if [ -z "$MANIFEST_SIGNING_KEY_ID" ] || is_placeholder "$MANIFEST_SIGNING_KEY_ID" || [ "$should_generate" = "true" ]; then
|
|
133
|
+
local generated_key_id
|
|
134
|
+
generated_key_id=$(generate_worker_subdomain_label)
|
|
135
|
+
if [ -z "$generated_key_id" ] || [ ${#generated_key_id} -ne 10 ]; then
|
|
136
|
+
echo -e "${RED}❌ Error: Failed to generate MANIFEST_SIGNING_KEY_ID${NC}"
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
139
|
+
MANIFEST_SIGNING_KEY_ID="$generated_key_id"
|
|
140
|
+
export MANIFEST_SIGNING_KEY_ID
|
|
141
|
+
write_env_var "MANIFEST_SIGNING_KEY_ID" "$MANIFEST_SIGNING_KEY_ID"
|
|
142
|
+
echo -e "${GREEN}✅ MANIFEST_SIGNING_KEY_ID generated: $MANIFEST_SIGNING_KEY_ID${NC}"
|
|
143
|
+
else
|
|
144
|
+
echo -e "${GREEN}✅ MANIFEST_SIGNING_KEY_ID: $MANIFEST_SIGNING_KEY_ID${NC}"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
echo ""
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
generate_export_encryption_key_pair() {
|
|
151
|
+
local private_key_file
|
|
152
|
+
local public_key_file
|
|
153
|
+
private_key_file=$(mktemp)
|
|
154
|
+
public_key_file=$(mktemp)
|
|
155
|
+
|
|
156
|
+
if ! node -e "const { generateKeyPairSync } = require('crypto'); const fs = require('fs'); const pair = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); fs.writeFileSync(process.argv[1], pair.privateKey, 'utf8'); fs.writeFileSync(process.argv[2], pair.publicKey, 'utf8');" "$private_key_file" "$public_key_file"; then
|
|
157
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
158
|
+
return 1
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
local private_key_pem
|
|
162
|
+
local public_key_pem
|
|
163
|
+
private_key_pem=$(cat "$private_key_file")
|
|
164
|
+
public_key_pem=$(cat "$public_key_file")
|
|
165
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
166
|
+
|
|
167
|
+
private_key_pem="${private_key_pem//$'\r'/}"
|
|
168
|
+
public_key_pem="${public_key_pem//$'\r'/}"
|
|
169
|
+
|
|
170
|
+
EXPORT_ENCRYPTION_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
|
|
171
|
+
EXPORT_ENCRYPTION_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
|
|
172
|
+
|
|
173
|
+
export EXPORT_ENCRYPTION_PRIVATE_KEY
|
|
174
|
+
export EXPORT_ENCRYPTION_PUBLIC_KEY
|
|
175
|
+
|
|
176
|
+
write_env_var "EXPORT_ENCRYPTION_PRIVATE_KEY" "$EXPORT_ENCRYPTION_PRIVATE_KEY"
|
|
177
|
+
write_env_var "EXPORT_ENCRYPTION_PUBLIC_KEY" "$EXPORT_ENCRYPTION_PUBLIC_KEY"
|
|
178
|
+
|
|
179
|
+
return 0
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
configure_export_encryption_credentials() {
|
|
183
|
+
echo -e "${BLUE}🔐 EXPORT ENCRYPTION CONFIGURATION${NC}"
|
|
184
|
+
echo "================================="
|
|
185
|
+
|
|
186
|
+
local should_generate="false"
|
|
187
|
+
restore_env_var_from_backup_if_missing "EXPORT_ENCRYPTION_PRIVATE_KEY"
|
|
188
|
+
restore_env_var_from_backup_if_missing "EXPORT_ENCRYPTION_PUBLIC_KEY"
|
|
189
|
+
restore_env_var_from_backup_if_missing "EXPORT_ENCRYPTION_KEY_ID"
|
|
190
|
+
restore_env_var_from_backup_if_missing "EXPORT_ENCRYPTION_KEYS_JSON"
|
|
191
|
+
restore_env_var_from_backup_if_missing "EXPORT_ENCRYPTION_ACTIVE_KEY_ID"
|
|
192
|
+
|
|
193
|
+
if [ -z "$EXPORT_ENCRYPTION_PRIVATE_KEY" ] || is_placeholder "$EXPORT_ENCRYPTION_PRIVATE_KEY" || [ -z "$EXPORT_ENCRYPTION_PUBLIC_KEY" ] || is_placeholder "$EXPORT_ENCRYPTION_PUBLIC_KEY"; then
|
|
194
|
+
should_generate="true"
|
|
195
|
+
else
|
|
196
|
+
if confirm_key_pair_regeneration "export encryption" "Regenerating this key pair can make prior encrypted exports undecryptable without migration."; then
|
|
197
|
+
should_generate="true"
|
|
198
|
+
fi
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
if [ "$should_generate" = "true" ]; then
|
|
202
|
+
echo -e "${YELLOW}Generating export encryption RSA key pair...${NC}"
|
|
203
|
+
if generate_export_encryption_key_pair; then
|
|
204
|
+
echo -e "${GREEN}✅ Export encryption key pair generated${NC}"
|
|
205
|
+
else
|
|
206
|
+
echo -e "${RED}❌ Error: Failed to generate export encryption key pair${NC}"
|
|
207
|
+
exit 1
|
|
208
|
+
fi
|
|
209
|
+
else
|
|
210
|
+
echo -e "${GREEN}✅ Keeping current export encryption key pair${NC}"
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
if [ -z "$EXPORT_ENCRYPTION_KEY_ID" ] || is_placeholder "$EXPORT_ENCRYPTION_KEY_ID" || [ "$should_generate" = "true" ]; then
|
|
214
|
+
local generated_key_id
|
|
215
|
+
generated_key_id=$(generate_worker_subdomain_label)
|
|
216
|
+
if [ -z "$generated_key_id" ] || [ ${#generated_key_id} -ne 10 ]; then
|
|
217
|
+
echo -e "${RED}❌ Error: Failed to generate EXPORT_ENCRYPTION_KEY_ID${NC}"
|
|
218
|
+
exit 1
|
|
219
|
+
fi
|
|
220
|
+
EXPORT_ENCRYPTION_KEY_ID="$generated_key_id"
|
|
221
|
+
export EXPORT_ENCRYPTION_KEY_ID
|
|
222
|
+
write_env_var "EXPORT_ENCRYPTION_KEY_ID" "$EXPORT_ENCRYPTION_KEY_ID"
|
|
223
|
+
echo -e "${GREEN}✅ EXPORT_ENCRYPTION_KEY_ID generated: $EXPORT_ENCRYPTION_KEY_ID${NC}"
|
|
224
|
+
else
|
|
225
|
+
echo -e "${GREEN}✅ EXPORT_ENCRYPTION_KEY_ID: $EXPORT_ENCRYPTION_KEY_ID${NC}"
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
update_private_key_registry "EXPORT_ENCRYPTION_KEYS_JSON" "EXPORT_ENCRYPTION_ACTIVE_KEY_ID" "$EXPORT_ENCRYPTION_KEY_ID" "$EXPORT_ENCRYPTION_PRIVATE_KEY" "export encryption"
|
|
229
|
+
|
|
230
|
+
echo ""
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
generate_data_at_rest_encryption_key_pair() {
|
|
234
|
+
local private_key_file
|
|
235
|
+
local public_key_file
|
|
236
|
+
private_key_file=$(mktemp)
|
|
237
|
+
public_key_file=$(mktemp)
|
|
238
|
+
|
|
239
|
+
if ! node -e "const { generateKeyPairSync } = require('crypto'); const fs = require('fs'); const pair = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); fs.writeFileSync(process.argv[1], pair.privateKey, 'utf8'); fs.writeFileSync(process.argv[2], pair.publicKey, 'utf8');" "$private_key_file" "$public_key_file"; then
|
|
240
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
241
|
+
return 1
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
local private_key_pem
|
|
245
|
+
local public_key_pem
|
|
246
|
+
private_key_pem=$(cat "$private_key_file")
|
|
247
|
+
public_key_pem=$(cat "$public_key_file")
|
|
248
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
249
|
+
|
|
250
|
+
private_key_pem="${private_key_pem//$'\r'/}"
|
|
251
|
+
public_key_pem="${public_key_pem//$'\r'/}"
|
|
252
|
+
|
|
253
|
+
DATA_AT_REST_ENCRYPTION_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
|
|
254
|
+
DATA_AT_REST_ENCRYPTION_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
|
|
255
|
+
|
|
256
|
+
export DATA_AT_REST_ENCRYPTION_PRIVATE_KEY
|
|
257
|
+
export DATA_AT_REST_ENCRYPTION_PUBLIC_KEY
|
|
258
|
+
|
|
259
|
+
write_env_var "DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY"
|
|
260
|
+
write_env_var "DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY"
|
|
261
|
+
|
|
262
|
+
return 0
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
generate_user_kv_encryption_key_pair() {
|
|
266
|
+
local private_key_file
|
|
267
|
+
local public_key_file
|
|
268
|
+
private_key_file=$(mktemp)
|
|
269
|
+
public_key_file=$(mktemp)
|
|
270
|
+
|
|
271
|
+
if ! node -e "const { generateKeyPairSync } = require('crypto'); const fs = require('fs'); const pair = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); fs.writeFileSync(process.argv[1], pair.privateKey, 'utf8'); fs.writeFileSync(process.argv[2], pair.publicKey, 'utf8');" "$private_key_file" "$public_key_file"; then
|
|
272
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
273
|
+
return 1
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
local private_key_pem
|
|
277
|
+
local public_key_pem
|
|
278
|
+
private_key_pem=$(cat "$private_key_file")
|
|
279
|
+
public_key_pem=$(cat "$public_key_file")
|
|
280
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
281
|
+
|
|
282
|
+
private_key_pem="${private_key_pem//$'\r'/}"
|
|
283
|
+
public_key_pem="${public_key_pem//$'\r'/}"
|
|
284
|
+
|
|
285
|
+
USER_KV_ENCRYPTION_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
|
|
286
|
+
USER_KV_ENCRYPTION_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
|
|
287
|
+
|
|
288
|
+
export USER_KV_ENCRYPTION_PRIVATE_KEY
|
|
289
|
+
export USER_KV_ENCRYPTION_PUBLIC_KEY
|
|
290
|
+
|
|
291
|
+
write_env_var "USER_KV_ENCRYPTION_PRIVATE_KEY" "$USER_KV_ENCRYPTION_PRIVATE_KEY"
|
|
292
|
+
write_env_var "USER_KV_ENCRYPTION_PUBLIC_KEY" "$USER_KV_ENCRYPTION_PUBLIC_KEY"
|
|
293
|
+
|
|
294
|
+
return 0
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
configure_user_kv_encryption_credentials() {
|
|
298
|
+
echo -e "${BLUE}🧑 USER KV ENCRYPTION CONFIGURATION${NC}"
|
|
299
|
+
echo "=================================="
|
|
300
|
+
|
|
301
|
+
local should_generate="false"
|
|
302
|
+
restore_env_var_from_backup_if_missing "USER_KV_ENCRYPTION_PRIVATE_KEY"
|
|
303
|
+
restore_env_var_from_backup_if_missing "USER_KV_ENCRYPTION_PUBLIC_KEY"
|
|
304
|
+
restore_env_var_from_backup_if_missing "USER_KV_ENCRYPTION_KEY_ID"
|
|
305
|
+
restore_env_var_from_backup_if_missing "USER_KV_ENCRYPTION_KEYS_JSON"
|
|
306
|
+
restore_env_var_from_backup_if_missing "USER_KV_ENCRYPTION_ACTIVE_KEY_ID"
|
|
307
|
+
|
|
308
|
+
if [ -z "$USER_KV_ENCRYPTION_PRIVATE_KEY" ] || is_placeholder "$USER_KV_ENCRYPTION_PRIVATE_KEY" || [ -z "$USER_KV_ENCRYPTION_PUBLIC_KEY" ] || is_placeholder "$USER_KV_ENCRYPTION_PUBLIC_KEY"; then
|
|
309
|
+
should_generate="true"
|
|
310
|
+
else
|
|
311
|
+
if confirm_key_pair_regeneration "user KV encryption" "Regenerating this key pair or key ID without re-encryption migration can make existing user KV records undecryptable."; then
|
|
312
|
+
should_generate="true"
|
|
313
|
+
fi
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
if [ "$should_generate" = "true" ]; then
|
|
317
|
+
echo -e "${YELLOW}Generating user KV encryption RSA key pair...${NC}"
|
|
318
|
+
if generate_user_kv_encryption_key_pair; then
|
|
319
|
+
echo -e "${GREEN}✅ User KV encryption key pair generated${NC}"
|
|
320
|
+
else
|
|
321
|
+
echo -e "${RED}❌ Error: Failed to generate user KV encryption key pair${NC}"
|
|
322
|
+
exit 1
|
|
323
|
+
fi
|
|
324
|
+
else
|
|
325
|
+
echo -e "${GREEN}✅ Keeping current user KV encryption key pair${NC}"
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
if [ -z "$USER_KV_ENCRYPTION_KEY_ID" ] || is_placeholder "$USER_KV_ENCRYPTION_KEY_ID" || [ "$should_generate" = "true" ]; then
|
|
329
|
+
local generated_key_id
|
|
330
|
+
generated_key_id=$(generate_worker_subdomain_label)
|
|
331
|
+
if [ -z "$generated_key_id" ] || [ ${#generated_key_id} -ne 10 ]; then
|
|
332
|
+
echo -e "${RED}❌ Error: Failed to generate USER_KV_ENCRYPTION_KEY_ID${NC}"
|
|
333
|
+
exit 1
|
|
334
|
+
fi
|
|
335
|
+
USER_KV_ENCRYPTION_KEY_ID="$generated_key_id"
|
|
336
|
+
export USER_KV_ENCRYPTION_KEY_ID
|
|
337
|
+
write_env_var "USER_KV_ENCRYPTION_KEY_ID" "$USER_KV_ENCRYPTION_KEY_ID"
|
|
338
|
+
echo -e "${GREEN}✅ USER_KV_ENCRYPTION_KEY_ID generated: $USER_KV_ENCRYPTION_KEY_ID${NC}"
|
|
339
|
+
else
|
|
340
|
+
echo -e "${GREEN}✅ USER_KV_ENCRYPTION_KEY_ID: $USER_KV_ENCRYPTION_KEY_ID${NC}"
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
update_private_key_registry "USER_KV_ENCRYPTION_KEYS_JSON" "USER_KV_ENCRYPTION_ACTIVE_KEY_ID" "$USER_KV_ENCRYPTION_KEY_ID" "$USER_KV_ENCRYPTION_PRIVATE_KEY" "user KV encryption"
|
|
344
|
+
|
|
345
|
+
echo ""
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
configure_data_at_rest_encryption_credentials() {
|
|
349
|
+
echo -e "${BLUE}🗃️ DATA-AT-REST ENCRYPTION CONFIGURATION${NC}"
|
|
350
|
+
echo "========================================"
|
|
351
|
+
|
|
352
|
+
# Data-at-rest encryption is mandatory for all environments.
|
|
353
|
+
DATA_AT_REST_ENCRYPTION_ENABLED="true"
|
|
354
|
+
|
|
355
|
+
export DATA_AT_REST_ENCRYPTION_ENABLED
|
|
356
|
+
write_env_var "DATA_AT_REST_ENCRYPTION_ENABLED" "$DATA_AT_REST_ENCRYPTION_ENABLED"
|
|
357
|
+
echo -e "${GREEN}✅ DATA_AT_REST_ENCRYPTION_ENABLED: $DATA_AT_REST_ENCRYPTION_ENABLED${NC}"
|
|
358
|
+
|
|
359
|
+
local should_generate="false"
|
|
360
|
+
restore_env_var_from_backup_if_missing "DATA_AT_REST_ENCRYPTION_PRIVATE_KEY"
|
|
361
|
+
restore_env_var_from_backup_if_missing "DATA_AT_REST_ENCRYPTION_PUBLIC_KEY"
|
|
362
|
+
restore_env_var_from_backup_if_missing "DATA_AT_REST_ENCRYPTION_KEY_ID"
|
|
363
|
+
restore_env_var_from_backup_if_missing "DATA_AT_REST_ENCRYPTION_KEYS_JSON"
|
|
364
|
+
restore_env_var_from_backup_if_missing "DATA_AT_REST_ENCRYPTION_ACTIVE_KEY_ID"
|
|
365
|
+
|
|
366
|
+
if [ -z "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" || [ -z "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY"; then
|
|
367
|
+
should_generate="true"
|
|
368
|
+
else
|
|
369
|
+
if confirm_key_pair_regeneration "data-at-rest encryption" "Regenerating this key pair can make previously encrypted data unreadable without migration."; then
|
|
370
|
+
should_generate="true"
|
|
371
|
+
fi
|
|
372
|
+
fi
|
|
373
|
+
|
|
374
|
+
if [ "$should_generate" = "true" ]; then
|
|
375
|
+
echo -e "${YELLOW}Generating data-at-rest encryption RSA key pair...${NC}"
|
|
376
|
+
if generate_data_at_rest_encryption_key_pair; then
|
|
377
|
+
echo -e "${GREEN}✅ Data-at-rest encryption key pair generated${NC}"
|
|
378
|
+
else
|
|
379
|
+
echo -e "${RED}❌ Error: Failed to generate data-at-rest encryption key pair${NC}"
|
|
380
|
+
exit 1
|
|
381
|
+
fi
|
|
382
|
+
else
|
|
383
|
+
echo -e "${GREEN}✅ Keeping current data-at-rest encryption key pair${NC}"
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
if [ -z "$DATA_AT_REST_ENCRYPTION_KEY_ID" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_KEY_ID" || [ "$should_generate" = "true" ]; then
|
|
387
|
+
local generated_key_id
|
|
388
|
+
generated_key_id=$(generate_worker_subdomain_label)
|
|
389
|
+
if [ -z "$generated_key_id" ] || [ ${#generated_key_id} -ne 10 ]; then
|
|
390
|
+
echo -e "${RED}❌ Error: Failed to generate DATA_AT_REST_ENCRYPTION_KEY_ID${NC}"
|
|
391
|
+
exit 1
|
|
392
|
+
fi
|
|
393
|
+
DATA_AT_REST_ENCRYPTION_KEY_ID="$generated_key_id"
|
|
394
|
+
export DATA_AT_REST_ENCRYPTION_KEY_ID
|
|
395
|
+
write_env_var "DATA_AT_REST_ENCRYPTION_KEY_ID" "$DATA_AT_REST_ENCRYPTION_KEY_ID"
|
|
396
|
+
echo -e "${GREEN}✅ DATA_AT_REST_ENCRYPTION_KEY_ID generated: $DATA_AT_REST_ENCRYPTION_KEY_ID${NC}"
|
|
397
|
+
else
|
|
398
|
+
echo -e "${GREEN}✅ DATA_AT_REST_ENCRYPTION_KEY_ID: $DATA_AT_REST_ENCRYPTION_KEY_ID${NC}"
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
update_private_key_registry "DATA_AT_REST_ENCRYPTION_KEYS_JSON" "DATA_AT_REST_ENCRYPTION_ACTIVE_KEY_ID" "$DATA_AT_REST_ENCRYPTION_KEY_ID" "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" "data-at-rest encryption"
|
|
402
|
+
|
|
403
|
+
echo ""
|
|
404
|
+
}
|