Persist pending users when no org exists
All checks were successful
build-and-deploy / build-deploy (push) Successful in 27s
All checks were successful
build-and-deploy / build-deploy (push) Successful in 27s
This commit is contained in:
@@ -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?
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
@@ -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?
|
||||
}
|
||||
Reference in New Issue
Block a user