Skip to content

Commit

Permalink
Accept gog linked accounts for lobby scope
Browse files Browse the repository at this point in the history
Requires faf-db v120+
  • Loading branch information
Brutus5000 committed Sep 26, 2021
1 parent 36e0eaa commit 9ca9042
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/main/kotlin/com/faforever/userservice/domain/User.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ data class User(
val ip: String?,
@Column("steamid")
val steamId: Long?,
val gogId: String?,
) {

override fun toString(): String =
// Do NOT expose personal information here!!
"User(id=$id, username='$username')"

val hasGameOwnershipVerified = steamId != null || gogId != null
}

@Table("group_permission")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class UserService(
}
}
.switchIfEmpty {
if (user.steamId == null && loginRequest.requestedScope.contains(OAuthScope.LOBBY)) {
if (loginRequest.requestedScope.contains(OAuthScope.LOBBY) && !user.hasGameOwnershipVerified) {
LOG.debug("Lobby login blocked for user '$usernameOrEmail' because of missing game ownership verification")
hydraService.rejectLoginRequest(
challenge,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class UserServiceApplicationTests {
private const val hydraRedirectUrl = "someHydraRedirectUrl"
private val revokeRequest = RevokeRefreshTokensRequest("1", null, true)

private val user = User(1, username, password, email, null, 0)
private val user = User(1, username, password, email, null, 0, null)
private val mockServer = ClientAndServer(mockServerPort)

@JvmStatic
Expand Down Expand Up @@ -265,7 +265,7 @@ class UserServiceApplicationTests {

@Test
fun postLoginWithNonLinkedUserWithLobbyScope() {
val unlinkedUser = User(1, username, password, email, null, null)
val unlinkedUser = User(1, username, password, email, null, null, null)
`when`(userRepository.findByUsernameOrEmail(username, username)).thenReturn(Mono.just(unlinkedUser))
`when`(passwordEncoder.matches(password, password)).thenReturn(true)
`when`(loginLogRepository.findFailedAttemptsByIp(anyString()))
Expand Down Expand Up @@ -302,9 +302,87 @@ class UserServiceApplicationTests {
verify(banRepository).findAllByPlayerIdAndLevel(anyLong(), anyOrNull())
}

@Test
fun postLoginWithGogLinkedUserWithLobbyScope() {
val unlinkedUser = User(1, username, password, email, null, null, "someGogId")
`when`(userRepository.findByUsernameOrEmail(username, username)).thenReturn(Mono.just(unlinkedUser))
`when`(passwordEncoder.matches(password, password)).thenReturn(true)
`when`(loginLogRepository.findFailedAttemptsByIp(anyString()))
.thenReturn(Mono.just(FailedAttemptsSummary(null, null, null, null)))
`when`(loginLogRepository.save(anyOrNull()))
.thenAnswer { Mono.just(it.arguments[0]) }
`when`(banRepository.findAllByPlayerIdAndLevel(anyLong(), anyOrNull())).thenReturn(
Flux.empty()
)

mockLoginRequest(scopes = listOf(OAuthScope.LOBBY))
mockLoginAccept()

webTestClient
.mutateWith(csrf())
.post()
.uri("/oauth2/login?login_challenge=$challenge")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.body(
BodyInserters.fromFormData("login_challenge", challenge)
.with("usernameOrEmail", username)
.with("password", password)
)
.exchange()
.expectStatus().is3xxRedirection
.expectHeader()
.location(hydraRedirectUrl)
.expectBody(String::class.java)

verify(userRepository).findByUsernameOrEmail(username, username)
verify(passwordEncoder).matches(password, password)
verify(loginLogRepository).findFailedAttemptsByIp(anyString())
verify(loginLogRepository).save(anyOrNull())
verify(banRepository).findAllByPlayerIdAndLevel(anyLong(), anyOrNull())
}

@Test
fun postLoginWithSteamLinkedUserWithLobbyScope() {
val unlinkedUser = User(1, username, password, email, null, 123456L, null)
`when`(userRepository.findByUsernameOrEmail(username, username)).thenReturn(Mono.just(unlinkedUser))
`when`(passwordEncoder.matches(password, password)).thenReturn(true)
`when`(loginLogRepository.findFailedAttemptsByIp(anyString()))
.thenReturn(Mono.just(FailedAttemptsSummary(null, null, null, null)))
`when`(loginLogRepository.save(anyOrNull()))
.thenAnswer { Mono.just(it.arguments[0]) }
`when`(banRepository.findAllByPlayerIdAndLevel(anyLong(), anyOrNull())).thenReturn(
Flux.empty()
)

mockLoginRequest(scopes = listOf(OAuthScope.LOBBY))
mockLoginAccept()

webTestClient
.mutateWith(csrf())
.post()
.uri("/oauth2/login?login_challenge=$challenge")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.body(
BodyInserters.fromFormData("login_challenge", challenge)
.with("usernameOrEmail", username)
.with("password", password)
)
.exchange()
.expectStatus().is3xxRedirection
.expectHeader()
.location(hydraRedirectUrl)
.expectBody(String::class.java)

verify(userRepository).findByUsernameOrEmail(username, username)
verify(passwordEncoder).matches(password, password)
verify(loginLogRepository).findFailedAttemptsByIp(anyString())
verify(loginLogRepository).save(anyOrNull())
verify(banRepository).findAllByPlayerIdAndLevel(anyLong(), anyOrNull())
}

@Test
fun postLoginWithNonLinkedUserWithoutLobbyScope() {
val unlinkedUser = User(1, username, password, email, null, null)
val unlinkedUser = User(1, username, password, email, null, null, null)
`when`(userRepository.findByUsernameOrEmail(username, username)).thenReturn(Mono.just(unlinkedUser))
`when`(passwordEncoder.matches(password, password)).thenReturn(true)
`when`(loginLogRepository.findFailedAttemptsByIp(anyString()))
Expand Down

0 comments on commit 9ca9042

Please sign in to comment.