Skip to content

Commit f8fa7bd

Browse files
committed
Add separate existing external user id check when updating the user token
1 parent a9e0ad1 commit f8fa7bd

File tree

2 files changed

+29
-32
lines changed

2 files changed

+29
-32
lines changed

authorizer-app-backend/src/main/java/org/radarbase/authorizer/resources/RegistrationResource.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class RegistrationResource(
154154
) = asyncService.runAsCoroutine(asyncResponse) {
155155
val registration = registrationService.ensureRegistration(token)
156156
val accessToken = authorizationService.requestAccessToken(payload, registration.user.sourceType)
157-
val user = userRepository.updateToken(accessToken, registration.user)
157+
val user = restSourceUserService.updateUserToken(accessToken, registration.user)
158158
val project = registration.user.projectId?.let {
159159
projectService.project(it).toProject()
160160
}

authorizer-app-backend/src/main/java/org/radarbase/authorizer/service/RestSourceUserService.kt

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import jakarta.ws.rs.core.Context
55
import jakarta.ws.rs.core.Response
66
import org.radarbase.auth.authorization.EntityDetails
77
import org.radarbase.auth.authorization.Permission
8+
import org.radarbase.authorizer.api.RestOauth2AccessToken
89
import org.radarbase.authorizer.api.RestSourceUserDTO
910
import org.radarbase.authorizer.api.RestSourceUserMapper
1011
import org.radarbase.authorizer.api.TokenDTO
@@ -13,6 +14,7 @@ import org.radarbase.authorizer.doa.entity.RestSourceUser
1314
import org.radarbase.jersey.auth.AuthService
1415
import org.radarbase.jersey.exception.HttpApplicationException
1516
import org.radarbase.jersey.exception.HttpBadRequestException
17+
import org.radarbase.jersey.exception.HttpConflictException
1618
import org.radarbase.jersey.exception.HttpNotFoundException
1719
import kotlin.time.Duration.Companion.seconds
1820

@@ -41,21 +43,6 @@ class RestSourceUserService(
4143
suspend fun create(userDto: RestSourceUserDTO): RestSourceUserDTO {
4244
userDto.ensure()
4345

44-
// Check if the service user ID is already in use for the same source type.
45-
userDto.serviceUserId?.let { serviceUserId ->
46-
val existingExternalUser = userRepository.findByExternalId(
47-
externalId = serviceUserId,
48-
sourceType = userDto.sourceType,
49-
)
50-
if (existingExternalUser != null) {
51-
val response = Response.status(Response.Status.CONFLICT)
52-
.entity(mapOf("status" to 409, "message" to "Account with this service user ID $serviceUserId already exists for source type ${userDto.sourceType}.", "user" to userMapper.fromEntity(existingExternalUser)))
53-
.build()
54-
55-
throw WebApplicationException(response)
56-
}
57-
}
58-
5946
val existingUser = userRepository.findByUserIdProjectIdSourceType(
6047
userId = userDto.userId!!,
6148
projectId = userDto.projectId!!,
@@ -87,21 +74,6 @@ class RestSourceUserService(
8774
suspend fun update(userId: Long, user: RestSourceUserDTO): RestSourceUserDTO {
8875
user.ensure()
8976

90-
// Check if the service user ID is already in use for the same source type but different user.
91-
user.serviceUserId?.let { serviceUserId ->
92-
val existingExternalUser = userRepository.findByExternalId(
93-
externalId = serviceUserId,
94-
sourceType = user.sourceType,
95-
)
96-
if (existingExternalUser != null && existingExternalUser.id != userId) {
97-
val response = Response.status(Response.Status.CONFLICT)
98-
.entity(mapOf("status" to 409, "message" to "Account with this service user ID $serviceUserId already exists for source type ${user.sourceType}.", "user" to userMapper.fromEntity(existingExternalUser)))
99-
.build()
100-
101-
throw WebApplicationException(response)
102-
}
103-
}
104-
10577
return userMapper.fromEntity(
10678
runLocked(userId) {
10779
userRepository.update(userId, user)
@@ -139,6 +111,31 @@ class RestSourceUserService(
139111
)
140112
}
141113

114+
/**
115+
* Validates that an external user ID is not already in use by another user.
116+
* Should be called before updating a user's token with an external user ID.
117+
*/
118+
private suspend fun validateExternalUserId(token: RestOauth2AccessToken?, user: RestSourceUser) {
119+
if (token?.externalUserId != null) {
120+
val existingUser = userRepository.findByExternalId(token.externalUserId, user.sourceType)
121+
if (existingUser != null && existingUser.id != user.id) {
122+
throw HttpConflictException(
123+
"external_user_id_already_exists",
124+
"External user ID ${token.externalUserId} is already registered for another user of source type ${user.sourceType}",
125+
)
126+
}
127+
}
128+
}
129+
130+
/**
131+
* Updates a user's token after validating the external user ID.
132+
* This method ensures no duplicate external user IDs exist in the system.
133+
*/
134+
suspend fun updateUserToken(token: RestOauth2AccessToken?, user: RestSourceUser): RestSourceUser {
135+
validateExternalUserId(token, user)
136+
return userRepository.updateToken(token, user)
137+
}
138+
142139
suspend fun ensureToken(userId: Long): TokenDTO {
143140
ensureUser(userId, Permission.MEASUREMENT_CREATE)
144141
return runLocked(userId) { user ->
@@ -184,7 +181,7 @@ class RestSourceUserService(
184181
}
185182

186183
val token = authorizationService.refreshToken(user)
187-
val updatedUser = userRepository.updateToken(token, user)
184+
val updatedUser = updateUserToken(token, user)
188185

189186
if (!updatedUser.authorized) {
190187
throw HttpApplicationException(

0 commit comments

Comments
 (0)