Persist pending users when no org exists
All checks were successful
build-and-deploy / build-deploy (push) Successful in 27s

This commit is contained in:
androidlover5842
2026-01-26 21:57:52 +05:30
parent 650e7c7354
commit 721580ffd7
3 changed files with 90 additions and 22 deletions

View File

@@ -4,6 +4,7 @@ import com.android.trisolarisserver.controller.dto.PropertyUserResponse
import com.android.trisolarisserver.controller.dto.UserResponse
import com.android.trisolarisserver.repo.AppUserRepo
import com.android.trisolarisserver.repo.OrganizationRepo
import com.android.trisolarisserver.repo.PendingUserRepo
import com.android.trisolarisserver.repo.PropertyUserRepo
import com.android.trisolarisserver.security.MyPrincipal
import com.google.firebase.auth.FirebaseAuth
@@ -16,13 +17,15 @@ import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException
import org.springframework.http.HttpStatus
import java.util.UUID
@RestController
@RequestMapping("/auth")
class Auth(
private val appUserRepo: AppUserRepo,
private val propertyUserRepo: PropertyUserRepo,
private val organizationRepo: OrganizationRepo
private val organizationRepo: OrganizationRepo,
private val pendingUserRepo: PendingUserRepo
) {
private val logger = LoggerFactory.getLogger(Auth::class.java)
@@ -32,12 +35,8 @@ class Auth(
request: HttpServletRequest
): AuthResponse {
logger.info("Auth verify hit, principalPresent={}", principal != null)
val resolved = principal ?: resolvePrincipalFromHeader(request)
return if (resolved == null) {
AuthResponse(status = "NEEDS_ORG")
} else {
buildAuthResponse(resolved)
}
val resolved = principal?.let { ResolveResult(it, null) } ?: resolvePrincipalFromHeader(request)
return resolved.toResponse()
}
@GetMapping("/me")
@@ -45,12 +44,8 @@ class Auth(
@AuthenticationPrincipal principal: MyPrincipal?,
request: HttpServletRequest
): AuthResponse {
val resolved = principal ?: resolvePrincipalFromHeader(request)
return if (resolved == null) {
AuthResponse(status = "NEEDS_ORG")
} else {
buildAuthResponse(resolved)
}
val resolved = principal?.let { ResolveResult(it, null) } ?: resolvePrincipalFromHeader(request)
return resolved.toResponse()
}
private fun buildAuthResponse(principal: MyPrincipal): AuthResponse {
@@ -78,7 +73,7 @@ class Auth(
)
}
private fun resolvePrincipalFromHeader(request: HttpServletRequest): MyPrincipal? {
private fun resolvePrincipalFromHeader(request: HttpServletRequest): ResolveResult {
val header = request.getHeader("Authorization") ?: throw ResponseStatusException(
HttpStatus.UNAUTHORIZED,
"Missing Authorization token"
@@ -99,7 +94,17 @@ class Auth(
val orgs = organizationRepo.findAll()
if (orgs.isEmpty()) {
logger.warn("Auth verify user not found for uid={}, orgCount=0", decoded.uid)
return null
val phone = decoded.claims["phone_number"] as? String
val name = decoded.claims["name"] as? String
val existing = pendingUserRepo.findByFirebaseUid(decoded.uid)
val pending = existing ?: pendingUserRepo.save(
com.android.trisolarisserver.models.property.PendingUser(
firebaseUid = decoded.uid,
phoneE164 = phone,
name = name
)
)
return ResolveResult(null, pending.id)
}
if (orgs.size == 1) {
val org = orgs.first()
@@ -114,24 +119,45 @@ class Auth(
)
)
logger.warn("Auth verify auto-created user uid={}, userId={}, orgId={}", decoded.uid, created.id, org.id)
return MyPrincipal(
userId = created.id ?: throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "User id missing"),
return ResolveResult(
MyPrincipal(
userId = created.id
?: throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "User id missing"),
firebaseUid = decoded.uid
),
null
)
}
logger.warn("Auth verify user not found for uid={}, orgCount={}", decoded.uid, orgs.size)
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not found")
}
logger.warn("Auth verify resolved uid={}, userId={}", decoded.uid, user.id)
return MyPrincipal(
return ResolveResult(
MyPrincipal(
userId = user.id ?: throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "User id missing"),
firebaseUid = decoded.uid
),
null
)
}
private fun ResolveResult.toResponse(): AuthResponse {
return if (principal == null) {
AuthResponse(status = "NEEDS_ORG", pendingUserId = pendingUserId)
} else {
buildAuthResponse(principal)
}
}
}
data class AuthResponse(
val status: String,
val user: UserResponse? = null,
val properties: List<PropertyUserResponse> = emptyList()
val properties: List<PropertyUserResponse> = emptyList(),
val pendingUserId: UUID? = null
)
private data class ResolveResult(
val principal: MyPrincipal?,
val pendingUserId: UUID?
)

View File

@@ -0,0 +1,33 @@
package com.android.trisolarisserver.models.property
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.Id
import jakarta.persistence.Table
import jakarta.persistence.UniqueConstraint
import java.time.OffsetDateTime
import java.util.UUID
@Entity
@Table(
name = "pending_user",
uniqueConstraints = [UniqueConstraint(columnNames = ["firebase_uid"])]
)
class PendingUser(
@Id
@GeneratedValue
@Column(columnDefinition = "uuid")
val id: UUID? = null,
@Column(name = "firebase_uid", nullable = false)
var firebaseUid: String,
@Column(name = "phone_e164")
var phoneE164: String? = null,
var name: String? = null,
@Column(name = "created_at", nullable = false, columnDefinition = "timestamptz")
val createdAt: OffsetDateTime = OffsetDateTime.now()
)

View File

@@ -0,0 +1,9 @@
package com.android.trisolarisserver.repo
import com.android.trisolarisserver.models.property.PendingUser
import org.springframework.data.jpa.repository.JpaRepository
import java.util.UUID
interface PendingUserRepo : JpaRepository<PendingUser, UUID> {
fun findByFirebaseUid(firebaseUid: String): PendingUser?
}