diff --git a/app/src/main/java/app/closer/ui/onboarding/CreateProfileScreen.kt b/app/src/main/java/app/closer/ui/onboarding/CreateProfileScreen.kt index 128d2642..91a1ffce 100644 --- a/app/src/main/java/app/closer/ui/onboarding/CreateProfileScreen.kt +++ b/app/src/main/java/app/closer/ui/onboarding/CreateProfileScreen.kt @@ -105,7 +105,9 @@ fun CreateProfileScreen( uri?.let { viewModel.setPhotoUri(it.toString()) } } - val cameraFile = remember(context) { File(context.filesDir, "profile_temp.jpg") } + val cameraFile = remember(context) { + File(context.filesDir, "photos/profile_temp.jpg").also { it.parentFile?.mkdirs() } + } val cameraUri = remember(cameraFile) { FileProvider.getUriForFile( context, diff --git a/app/src/main/java/app/closer/ui/settings/EditProfileScreen.kt b/app/src/main/java/app/closer/ui/settings/EditProfileScreen.kt index 6e512684..986221d5 100644 --- a/app/src/main/java/app/closer/ui/settings/EditProfileScreen.kt +++ b/app/src/main/java/app/closer/ui/settings/EditProfileScreen.kt @@ -143,7 +143,9 @@ fun EditProfileContent( uri?.let { viewModel.setPhotoUri(it.toString()) } } - val cameraFile = remember(context) { File(context.filesDir, "profile_edit_temp.jpg") } + val cameraFile = remember(context) { + File(context.filesDir, "photos/profile_edit_temp.jpg").also { it.parentFile?.mkdirs() } + } val cameraUri = remember(cameraFile) { FileProvider.getUriForFile( context, diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml index 600817c7..90464ea2 100644 --- a/app/src/main/res/xml/file_paths.xml +++ b/app/src/main/res/xml/file_paths.xml @@ -1,4 +1,4 @@ - + diff --git a/firestore.rules b/firestore.rules index a55cce9c..c470efc0 100644 --- a/firestore.rules +++ b/firestore.rules @@ -179,16 +179,18 @@ service cloud.firestore { 'wrappedCoupleKey', 'kdfSalt', 'kdfParams', 'encryptionVersion']); // Update: field-level restrictions - // - user IDs are immutable (cannot change who is in the couple) - // - invite code is immutable (cannot change the code) - // - createdAt is immutable (cannot change when the couple was formed) + // - user IDs, invite code, and createdAt are immutable // - encryptionVersion is monotonically non-decreasing (cannot downgrade) - // - wrappedCoupleKey/kdfSalt/kdfParams: mutable by members (passphrase change) - // - All other fields (including streakCount and lastAnsweredAt): both members can update + // - only the explicitly listed mutable fields may change; everything else + // (including currentQuestionId, activePackId, id) is server-only allow update: if isCouplesMember(coupleId) && isImmutable(['userIds', 'inviteCode', 'createdAt']) && (resource.data.encryptionVersion == null - || request.resource.data.encryptionVersion >= resource.data.encryptionVersion); + || request.resource.data.encryptionVersion >= resource.data.encryptionVersion) + && request.resource.data.diff(resource.data).affectedKeys().hasOnly([ + 'streakCount', 'lastAnsweredAt', + 'wrappedCoupleKey', 'kdfSalt', 'kdfParams', 'encryptionVersion' + ]); // Delete: server-only (admin SDK only). Admin SDK bypasses rules. allow delete: if false;