Allow updating AppUser name via auth/me
Some checks failed
build-and-deploy / build-deploy (push) Failing after 22s

This commit is contained in:
androidlover5842
2026-01-26 22:30:48 +05:30
parent 63c7479c9f
commit bf87d329d4

View File

@@ -3,8 +3,6 @@ package com.android.trisolarisserver.controller
import com.android.trisolarisserver.controller.dto.PropertyUserResponse import com.android.trisolarisserver.controller.dto.PropertyUserResponse
import com.android.trisolarisserver.controller.dto.UserResponse import com.android.trisolarisserver.controller.dto.UserResponse
import com.android.trisolarisserver.repo.AppUserRepo 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.repo.PropertyUserRepo
import com.android.trisolarisserver.security.MyPrincipal import com.android.trisolarisserver.security.MyPrincipal
import com.google.firebase.auth.FirebaseAuth import com.google.firebase.auth.FirebaseAuth
@@ -13,7 +11,9 @@ import org.slf4j.LoggerFactory
import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException import org.springframework.web.server.ResponseStatusException
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
@@ -24,9 +24,7 @@ import java.util.UUID
@RequestMapping("/auth") @RequestMapping("/auth")
class Auth( class Auth(
private val appUserRepo: AppUserRepo, private val appUserRepo: AppUserRepo,
private val propertyUserRepo: PropertyUserRepo, private val propertyUserRepo: PropertyUserRepo
private val organizationRepo: OrganizationRepo,
private val pendingUserRepo: PendingUserRepo
) { ) {
private val logger = LoggerFactory.getLogger(Auth::class.java) private val logger = LoggerFactory.getLogger(Auth::class.java)
@@ -36,7 +34,7 @@ class Auth(
request: HttpServletRequest request: HttpServletRequest
): ResponseEntity<AuthResponse> { ): ResponseEntity<AuthResponse> {
logger.info("Auth verify hit, principalPresent={}", principal != null) logger.info("Auth verify hit, principalPresent={}", principal != null)
val resolved = principal?.let { ResolveResult(it, null) } ?: resolvePrincipalFromHeader(request) val resolved = principal?.let { ResolveResult(it) } ?: resolvePrincipalFromHeader(request)
return resolved.toResponseEntity() return resolved.toResponseEntity()
} }
@@ -45,10 +43,31 @@ class Auth(
@AuthenticationPrincipal principal: MyPrincipal?, @AuthenticationPrincipal principal: MyPrincipal?,
request: HttpServletRequest request: HttpServletRequest
): ResponseEntity<AuthResponse> { ): ResponseEntity<AuthResponse> {
val resolved = principal?.let { ResolveResult(it, null) } ?: resolvePrincipalFromHeader(request) val resolved = principal?.let { ResolveResult(it) } ?: resolvePrincipalFromHeader(request)
return resolved.toResponseEntity() return resolved.toResponseEntity()
} }
@PutMapping("/me")
fun updateMe(
@AuthenticationPrincipal principal: MyPrincipal?,
request: HttpServletRequest,
@RequestBody body: UpdateMeRequest
): ResponseEntity<AuthResponse> {
val resolved = principal?.let { ResolveResult(it) } ?: resolvePrincipalFromHeader(request)
if (resolved.principal == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(AuthResponse(status = "UNAUTHORIZED"))
}
val user = appUserRepo.findById(resolved.principal.userId).orElseThrow {
ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not found")
}
if (!body.name.isNullOrBlank()) {
user.name = body.name.trim()
}
appUserRepo.save(user)
return ResponseEntity.ok(buildAuthResponse(resolved.principal))
}
private fun buildAuthResponse(principal: MyPrincipal): AuthResponse { private fun buildAuthResponse(principal: MyPrincipal): AuthResponse {
val user = appUserRepo.findById(principal.userId).orElseThrow { val user = appUserRepo.findById(principal.userId).orElseThrow {
ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not found") ResponseStatusException(HttpStatus.UNAUTHORIZED, "User not found")
@@ -60,15 +79,20 @@ class Auth(
roles = it.roles.map { role -> role.name }.toSet() roles = it.roles.map { role -> role.name }.toSet()
) )
} }
val status = when {
user.superAdmin -> "SUPER_ADMIN"
memberships.isEmpty() -> "NO_PROPERTIES"
else -> "OK"
}
return AuthResponse( return AuthResponse(
status = "OK", status = status,
user = UserResponse( user = UserResponse(
id = user.id!!, id = user.id!!,
orgId = user.org.id!!,
firebaseUid = user.firebaseUid, firebaseUid = user.firebaseUid,
phoneE164 = user.phoneE164, phoneE164 = user.phoneE164,
name = user.name, name = user.name,
disabled = user.disabled disabled = user.disabled,
superAdmin = user.superAdmin
), ),
properties = memberships properties = memberships
) )
@@ -90,62 +114,34 @@ class Auth(
logger.warn("Auth verify failed: {}", ex.message) logger.warn("Auth verify failed: {}", ex.message)
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid token") throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid token")
} }
val user = appUserRepo.findByFirebaseUid(decoded.uid) val user = appUserRepo.findByFirebaseUid(decoded.uid) ?: run {
if (user == null) {
val orgs = organizationRepo.findAll()
if (orgs.isEmpty()) {
logger.warn("Auth verify user not found for uid={}, orgCount=0", decoded.uid)
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()
val phone = decoded.claims["phone_number"] as? String val phone = decoded.claims["phone_number"] as? String
val name = decoded.claims["name"] as? String val name = decoded.claims["name"] as? String
val makeSuperAdmin = appUserRepo.count() == 0L
val created = appUserRepo.save( val created = appUserRepo.save(
com.android.trisolarisserver.models.property.AppUser( com.android.trisolarisserver.models.property.AppUser(
org = org,
firebaseUid = decoded.uid, firebaseUid = decoded.uid,
phoneE164 = phone, phoneE164 = phone,
name = name name = name,
superAdmin = makeSuperAdmin
) )
) )
logger.warn("Auth verify auto-created user uid={}, userId={}, orgId={}", decoded.uid, created.id, org.id) logger.warn("Auth verify auto-created user uid={}, userId={}", decoded.uid, created.id)
return ResolveResult( created
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) logger.warn("Auth verify resolved uid={}, userId={}", decoded.uid, user.id)
return ResolveResult( return ResolveResult(
MyPrincipal( MyPrincipal(
userId = user.id ?: throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "User id missing"), userId = user.id ?: throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "User id missing"),
firebaseUid = decoded.uid firebaseUid = decoded.uid
), )
null
) )
} }
private fun ResolveResult.toResponseEntity(): ResponseEntity<AuthResponse> { private fun ResolveResult.toResponseEntity(): ResponseEntity<AuthResponse> {
return if (principal == null) { return if (principal == null) {
ResponseEntity.status(HttpStatus.ACCEPTED) ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(AuthResponse(status = "NEEDS_ORG", pendingUserId = pendingUserId)) .body(AuthResponse(status = "UNAUTHORIZED"))
} else { } else {
ResponseEntity.ok(buildAuthResponse(principal)) ResponseEntity.ok(buildAuthResponse(principal))
} }
@@ -155,11 +151,13 @@ class Auth(
data class AuthResponse( data class AuthResponse(
val status: String, val status: String,
val user: UserResponse? = null, val user: UserResponse? = null,
val properties: List<PropertyUserResponse> = emptyList(), val properties: List<PropertyUserResponse> = emptyList()
val pendingUserId: UUID? = null )
data class UpdateMeRequest(
val name: String? = null
) )
private data class ResolveResult( private data class ResolveResult(
val principal: MyPrincipal?, val principal: MyPrincipal?
val pendingUserId: UUID?
) )