Skip to content

Commit

Permalink
Refactor db structure and firestore security rules (#18)
Browse files Browse the repository at this point in the history
* Setup firestore

* Minor map marker fix

* Minor login fixes

* WIP - firebase rule

* Change member and space collection

* Refactor auth service

* Update firestore.indexes.json

* Refactor user location collection

* Add security rule

* Add security rules

* Fixes for security rules
  • Loading branch information
cp-radhika-s authored Feb 8, 2024
1 parent 541330f commit a169455
Show file tree
Hide file tree
Showing 25 changed files with 469 additions and 153 deletions.
5 changes: 5 additions & 0 deletions .firebaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projects": {
"default": "catchme-dev-3a803"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ class SignInMethodViewModel @Inject constructor(
_state.emit(_state.value.copy(showGoogleLoading = true))
try {
val firebaseToken = firebaseAuth.signInWithGoogleAuthCredential(account.idToken)
val isNewUSer = authService.verifiedGoogleLogin(firebaseToken, account)
val isNewUSer = authService.verifiedGoogleLogin(
firebaseAuth.currentUserUid,
firebaseToken,
account
)
onSignUp(isNewUSer)
_state.emit(_state.value.copy(showGoogleLoading = false))
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ class SignInWithPhoneViewModel @Inject constructor(
val firebaseIdToken =
fbAuthService.signInWithPhoneAuthCredential(result.credential)
val isNewUser =
authService.verifiedPhoneLogin(firebaseIdToken, _state.value.phone)
authService.verifiedPhoneLogin(
fbAuthService.currentUserUid,
firebaseIdToken,
_state.value.phone
)
appNavigator.navigateBack(
route = AppDestinations.signIn.path,
result = mapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ class PhoneVerificationViewModel @Inject constructor(
_state.value.otp
)

val isNewUser = authService.verifiedPhoneLogin(firebaseIdToken, _state.value.phone)
val isNewUser = authService.verifiedPhoneLogin(
firebaseAuth.currentUserUid,
firebaseIdToken,
_state.value.phone
)
appNavigator.navigateBack(
route = AppDestinations.signIn.path,
result = mapOf(KEY_RESULT to RESULT_OKAY, EXTRA_RESULT_IS_NEW_USER to isNewUser)
Expand All @@ -87,7 +91,11 @@ class PhoneVerificationViewModel @Inject constructor(
val firebaseIdToken =
firebaseAuth.signInWithPhoneAuthCredential(result.credential)
val isNewUser =
authService.verifiedPhoneLogin(firebaseIdToken, _state.value.phone)
authService.verifiedPhoneLogin(
firebaseAuth.currentUserUid,
firebaseIdToken,
_state.value.phone
)
appNavigator.navigateBack(
route = AppDestinations.signIn.path,
result = mapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
Expand Down Expand Up @@ -40,6 +39,7 @@ fun MapMarker(
keys = arrayOf(user.id, isSelected),
state = markerState,
title = user.fullName,
zIndex = if (isSelected) 1f else 0f,
anchor = Offset(0.0f, 1f),
onClick = {
onClick()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import com.canopas.catchme.ui.component.AppBanner
import com.canopas.catchme.ui.component.CreateSpace
import com.canopas.catchme.ui.theme.AppTheme

Expand All @@ -22,6 +23,13 @@ import com.canopas.catchme.ui.theme.AppTheme
fun CreateSpaceHomeScreen() {
val viewModel = hiltViewModel<CreateSpaceHomeViewModel>()
val state by viewModel.state.collectAsState()

if (state.error != null) {
AppBanner(msg = state.error!!) {
viewModel.resetErrorState()
}
}

Scaffold(
topBar = {
TopAppBar(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,22 @@ class CreateSpaceHomeViewModel @Inject constructor(
_state.emit(_state.value.copy(creatingSpace = true))
val invitationCode = spaceRepository.createSpaceAndGetInviteCode(_state.value.spaceName)
appNavigator.navigateTo(
AppDestinations.SpaceInvitation.spaceInvitation(invitationCode, _state.value.spaceName).path,
AppDestinations.SpaceInvitation.spaceInvitation(
invitationCode,
_state.value.spaceName
).path,
AppDestinations.createSpace.path,
inclusive = true
)
} catch (e: Exception) {
Timber.e(e, "Unable to create space")
_state.emit(_state.value.copy(error = e.localizedMessage))
_state.emit(_state.value.copy(creatingSpace = false, error = e.localizedMessage))
}
}

fun resetErrorState() {
_state.value = _state.value.copy(error = null)
}
}

data class CreateSpaceHomeState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class OnboardViewModel @Inject constructor(
)
} catch (e: Exception) {
Timber.e(e, "Unable to create space")
_state.emit(_state.value.copy(error = e.localizedMessage))
_state.emit(_state.value.copy(creatingSpace = false, error = e.localizedMessage))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import com.canopas.catchme.R
import com.canopas.catchme.ui.component.AppBanner
import com.canopas.catchme.ui.component.CreateSpace
import com.canopas.catchme.ui.flow.onboard.OnboardItems
import com.canopas.catchme.ui.flow.onboard.OnboardViewModel
Expand Down Expand Up @@ -71,4 +72,10 @@ fun CreateSpaceOnboard() {
viewModel.createSpace(spaceName)
}
}

if (state.error != null) {
AppBanner(msg = state.error!!) {
viewModel.resetErrorState()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.lifecycle.viewModelScope
import com.canopas.catchme.data.models.user.ApiUser
import com.canopas.catchme.data.repository.SpaceRepository
import com.canopas.catchme.data.service.auth.AuthService
import com.canopas.catchme.data.service.user.ApiUserService
import com.canopas.catchme.data.utils.AppDispatcher
import com.canopas.catchme.ui.navigation.AppDestinations
import com.canopas.catchme.ui.navigation.HomeNavigator
Expand All @@ -20,7 +19,6 @@ import javax.inject.Inject
class SettingsViewModel @Inject constructor(
private val navigator: HomeNavigator,
private val appNavigator: MainNavigator,
private val userService: ApiUserService,
private val authService: AuthService,
private val appDispatcher: AppDispatcher,
private val spaceRepository: SpaceRepository
Expand All @@ -37,7 +35,7 @@ class SettingsViewModel @Inject constructor(
val user = authService.currentUser
_state.emit(_state.value.copy(user = user))
user?.let {
val updatedUser = userService.getUser(it.id)
val updatedUser = authService.getUser()
_state.emit(_state.value.copy(user = updatedUser))
updatedUser?.let {
authService.saveUser(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ class SignInMethodViewModelTest {
fun `proceedGoogleSignIn should invoke verifiedGoogleLogin`() = runTest {
val account = mock<GoogleSignInAccount>()
whenever(account.idToken).thenReturn("token")
whenever(firebaseAuth.currentUserUid).thenReturn("uid")
whenever(firebaseAuth.signInWithGoogleAuthCredential("token"))
.thenReturn("firebaseToken")
viewModel.proceedGoogleSignIn(account)
verify(authService).verifiedGoogleLogin("firebaseToken", account)
verify(authService).verifiedGoogleLogin("uid", "firebaseToken", account)
}

@Test
Expand All @@ -82,7 +83,9 @@ class SignInMethodViewModelTest {
whenever(account.idToken).thenReturn("token")
whenever(firebaseAuth.signInWithGoogleAuthCredential("token"))
.thenReturn("firebaseToken")
whenever(authService.verifiedGoogleLogin("firebaseToken", account))
whenever(firebaseAuth.currentUserUid).thenReturn("uid")

whenever(authService.verifiedGoogleLogin("uid", "firebaseToken", account))
.thenReturn(false)
viewModel.proceedGoogleSignIn(account)
verify(navigator).navigateTo("home", "sign-in", true)
Expand All @@ -92,9 +95,11 @@ class SignInMethodViewModelTest {
fun `proceedGoogleSignIn should navigate to onboard screen`() = runTest {
val account = mock<GoogleSignInAccount>()
whenever(account.idToken).thenReturn("token")
whenever(firebaseAuth.currentUserUid).thenReturn("uid")
whenever(firebaseAuth.signInWithGoogleAuthCredential("token"))
.thenReturn("firebaseToken")
whenever(authService.verifiedGoogleLogin("firebaseToken", account))

whenever(authService.verifiedGoogleLogin("uid", "firebaseToken", account))
.thenReturn(true)
viewModel.proceedGoogleSignIn(account)
verify(navigator).navigateTo("onboard", "sign-in", true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ class SignInWithPhoneViewModelTest {
val context = mock<Context>()
val credential = mock<PhoneAuthCredential>()
viewModel.onPhoneChange("1234567890")
whenever(firebaseAuth.currentUserUid).thenReturn("uid")
whenever(firebaseAuth.verifyPhoneNumber(context, "1234567890"))
.thenReturn(flowOf(PhoneAuthState.VerificationCompleted(credential)))
whenever(firebaseAuth.signInWithPhoneAuthCredential(credential)).thenReturn("firebaseIdToken")
whenever(authService.verifiedPhoneLogin("firebaseIdToken", "1234567890")).thenReturn(true)
whenever(authService.verifiedPhoneLogin("uid", "firebaseIdToken", "1234567890")).thenReturn(
true
)
viewModel.verifyPhoneNumber(context)
assert(!viewModel.state.value.verifying)
}
Expand All @@ -74,10 +77,13 @@ class SignInWithPhoneViewModelTest {
val context = mock<Context>()
val credential = mock<PhoneAuthCredential>()
viewModel.onPhoneChange("1234567890")
whenever(firebaseAuth.currentUserUid).thenReturn("uid")
whenever(firebaseAuth.verifyPhoneNumber(context, "1234567890"))
.thenReturn(flowOf(PhoneAuthState.VerificationCompleted(credential)))
whenever(firebaseAuth.signInWithPhoneAuthCredential(credential)).thenReturn("firebaseIdToken")
whenever(authService.verifiedPhoneLogin("firebaseIdToken", "1234567890")).thenReturn(true)
whenever(authService.verifiedPhoneLogin("uid", "firebaseIdToken", "1234567890")).thenReturn(
true
)

viewModel.verifyPhoneNumber(context)
verify(navigator).navigateBack(
Expand All @@ -91,12 +97,15 @@ class SignInWithPhoneViewModelTest {
val context = mock<Context>()
val credential = mock<PhoneAuthCredential>()
viewModel.onPhoneChange("1234567890")
whenever(firebaseAuth.currentUserUid).thenReturn("uid")
whenever(firebaseAuth.verifyPhoneNumber(context, "1234567890"))
.thenReturn(flowOf(PhoneAuthState.VerificationCompleted(credential)))
whenever(firebaseAuth.signInWithPhoneAuthCredential(credential)).thenReturn("firebaseIdToken")
whenever(authService.verifiedPhoneLogin("firebaseIdToken", "1234567890")).thenReturn(true)
whenever(authService.verifiedPhoneLogin("uid", "firebaseIdToken", "1234567890")).thenReturn(
true
)
viewModel.verifyPhoneNumber(context)
verify(authService).verifiedPhoneLogin("firebaseIdToken", "1234567890")
verify(authService).verifiedPhoneLogin("uid", "firebaseIdToken", "1234567890")
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import org.junit.Test
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.doSuspendableAnswer
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.whenever
Expand Down Expand Up @@ -126,11 +125,12 @@ class PhoneVerificationViewModelTest {
"12356"
)
).thenReturn("firebaseIdToken")
whenever(firebaseAuth.currentUserUid).thenReturn("uid")

viewModel.updateOTP("12356")
viewModel.verifyOTP()

verify(authService).verifiedPhoneLogin("firebaseIdToken", "1234567890")
verify(authService).verifiedPhoneLogin("uid", "firebaseIdToken", "1234567890")
}

@Test
Expand All @@ -141,7 +141,10 @@ class PhoneVerificationViewModelTest {
"12356"
)
).thenReturn("firebaseIdToken")
whenever(authService.verifiedPhoneLogin("firebaseIdToken", "1234567890")).thenReturn(false)
whenever(firebaseAuth.currentUserUid).thenReturn("uid")
whenever(authService.verifiedPhoneLogin("uid", "firebaseIdToken", "1234567890")).thenReturn(
false
)

viewModel.updateOTP("12356")
viewModel.verifyOTP()
Expand Down Expand Up @@ -189,7 +192,8 @@ class PhoneVerificationViewModelTest {
val credential = mock<PhoneAuthCredential>()
whenever(firebaseAuth.verifyPhoneNumber(context, "1234567890"))
.thenReturn(flowOf(PhoneAuthState.VerificationCompleted(credential)))
whenever(authService.verifiedPhoneLogin("firebaseIdToken", "1234567890")).thenReturn(false)
whenever(firebaseAuth.currentUserUid).thenReturn("uid")
whenever(authService.verifiedPhoneLogin("uid", "firebaseIdToken", "1234567890")).thenReturn(false)

whenever(firebaseAuth.signInWithPhoneAuthCredential(credential)).thenReturn("firebaseIdToken")
viewModel.resendCode(context)
Expand All @@ -205,11 +209,12 @@ class PhoneVerificationViewModelTest {
val credential = mock<PhoneAuthCredential>()
whenever(firebaseAuth.verifyPhoneNumber(context, "1234567890"))
.thenReturn(flowOf(PhoneAuthState.VerificationCompleted(credential)))
whenever(firebaseAuth.currentUserUid).thenReturn("uid")
whenever(firebaseAuth.signInWithPhoneAuthCredential(credential)).thenReturn("firebaseIdToken")
whenever(authService.verifiedPhoneLogin("firebaseIdToken", "1234567890")).thenReturn(true)
whenever(authService.verifiedPhoneLogin("uid", "firebaseIdToken", "1234567890")).thenReturn(true)

viewModel.resendCode(context)
verify(authService).verifiedPhoneLogin("firebaseIdToken", "1234567890")
verify(authService).verifiedPhoneLogin("uid", "firebaseIdToken", "1234567890")
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.canopas.catchme.data.models.space

import androidx.annotation.Keep
import com.google.firebase.firestore.Exclude
import java.util.UUID
import java.util.concurrent.TimeUnit

Expand Down Expand Up @@ -32,6 +33,7 @@ data class ApiSpaceInvitation(
val code: String = "",
val created_at: Long? = System.currentTimeMillis()
) {
@get:Exclude
val isExpired: Boolean
get() {
if (created_at == null) return true
Expand Down
19 changes: 11 additions & 8 deletions data/src/main/java/com/canopas/catchme/data/models/user/ApiUser.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.canopas.catchme.data.models.user

import androidx.annotation.Keep
import com.google.firebase.firestore.Exclude
import java.util.UUID

const val LOGIN_TYPE_GOOGLE = 1
Expand All @@ -13,26 +14,28 @@ data class ApiUser(
val phone: String? = null,
val email: String? = null,
val auth_type: Int? = null,
val first_name: String? = null,
val last_name: String? = null,
val profile_image: String? = null,
val first_name: String? = "",
val last_name: String? = "",
val profile_image: String? = "",
val location_enabled: Boolean = true,
val space_ids: List<String>? = emptyList(),
val provider_firebase_id_token: String? = null,
val created_at: Long? = System.currentTimeMillis()
) {
@get:Exclude
val fullName: String get() = "$first_name $last_name"
}

@Keep
data class ApiUserSession(
val id: String = UUID.randomUUID().toString(),
val user_id: String,
val device_id: String? = null,
val fcm_token: String? = null,
val device_name: String? = null,
val device_id: String? = "",
val fcm_token: String? = "",
val device_name: String? = "",
val platform: Int = LOGIN_DEVICE_TYPE_ANDROID,
val session_active: Boolean = true,
val app_version: Long? = null,
val battery_status: String? = null,
val app_version: Long? = 0,
val battery_status: String? = "",
val created_at: Long? = System.currentTimeMillis()
)
Loading

0 comments on commit a169455

Please sign in to comment.