Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 93 additions & 64 deletions src/main/kotlin/com/example/autobank/service/AuthenticationService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.example.autobank.service

import com.example.autobank.data.authentication.Auth0User
import com.example.autobank.repository.user.OnlineUserRepository
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.ParameterizedTypeReference
Expand All @@ -14,6 +15,10 @@ import org.springframework.security.oauth2.server.resource.authentication.JwtAut
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
import org.springframework.web.client.exchange
import org.springframework.web.util.UriComponentsBuilder
import java.net.URI
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.time.Duration
import java.time.Instant
import java.time.LocalDateTime
Expand All @@ -24,23 +29,16 @@ class AuthenticationService(
@Value("\${admincommittee}") private val adminCommittee: String,
@Value("\${auth0.domain}") private val domain: String,
@Value("\${environment}") private val environment: String,
@Value("\${api.base.domain}") private val apiBaseDomain: String,
) {

private val restTemplate = RestTemplate()

private val fetchProfileUrl = "https://old.online.ntnu.no/api/v1/profile/"

private val fetchUserCommitteesUrl = "https://old.online.ntnu.no/api/v1/group/online-groups/?members__user="

private val adminRecheckTime = 24 * 60 * 60 * 1000;

@Autowired
lateinit var onlineUserRepository: OnlineUserRepository

fun getAuth0User(token: String): Auth0User {
return Auth0User("sub", "email", "name")
}

fun getSecondsUntilExpiration(): Long {
val expiresAt = getExpiresAt()
return if (expiresAt != null) {
Expand Down Expand Up @@ -75,53 +73,64 @@ class AuthenticationService(
}

fun getUserDetails(): Auth0User {
val headers = HttpHeaders().apply {
set("Authorization", "Bearer ${getAccessToken()}")

if (environment != "prod") {
val sub = getUserSub()
// Return mock/minimal user data for local dev
return Auth0User(sub, "dev@example.com", "Dev User")
}
val entity = HttpEntity<Void>(headers)
val response: ResponseEntity<Map<String, Any>> = restTemplate.exchange(
"${domain}/userinfo",
HttpMethod.GET,
entity,
)

return Auth0User(
sub = response.body?.get("sub").toString(),
email = response.body?.get("email").toString(),
name = response.body?.get("name").toString(),
)
}
val endpoint = UriComponentsBuilder
.fromHttpUrl("${apiBaseDomain}user.getMe")
.encode()
.toUriString()

private fun fetchOnlineuserId(): Int {
val headers = HttpHeaders().apply {
set("Authorization", "Bearer ${getAccessToken()}")
}
val entity = HttpEntity<Void>(headers)
val response: ResponseEntity<Map<String, Any>> = restTemplate.exchange(
fetchProfileUrl,

val response: ResponseEntity<UserResponse> = restTemplate.exchange(
endpoint,
HttpMethod.GET,
entity,
object : ParameterizedTypeReference<UserResponse>() {}
)

if (response.statusCode.isError || response.body == null) {
throw Exception("Error fetching user id")
throw Exception("Error fetching user details")
}

return response.body?.get("id").toString().toInt()
val user = response.body?.result?.data?.json
?: throw Exception("User not found")

return Auth0User(user.id, user.email, user.name)
}

fun fetchUserCommittees(): List<String> {

if (environment != "prod") {
return listOf("Applikasjonskomiteen", "Trivselskomiteen")
return listOf("Applikasjonskomiteen")
}

val userId = fetchOnlineuserId()
val userId = getUserDetails().sub

// tRPC format: {"json":"<value>"}
// The value itself is the user ID string
val input = """{"json":"$userId"}"""
val encodedInput = URLEncoder.encode(input, StandardCharsets.UTF_8.toString())
val urlString = "${apiBaseDomain}group.allByMember?input=$encodedInput"

val headers = HttpHeaders()
println("Final URL: $urlString") // Debug log

val uri = URI(urlString)

val headers = HttpHeaders().apply {
set("Authorization", "Bearer ${getAccessToken()}")
}
val entity = HttpEntity<Void>(headers)

val response: ResponseEntity<UserCommitteeResponse> = restTemplate.exchange(
fetchUserCommitteesUrl + userId,
uri,
HttpMethod.GET,
entity,
object : ParameterizedTypeReference<UserCommitteeResponse>() {}
Expand All @@ -131,41 +140,34 @@ class AuthenticationService(
throw Exception("Error fetching user committees")
}

return response.body?.results?.map { it.name_long } ?: listOf()
return response.body?.result?.data?.json?.map { it.slug } ?: emptyList()
}

fun checkAdmin(): Boolean {
// Use the Auth0 sub to find the user in your local DB
val auth0Sub = getUserSub()
val user = onlineUserRepository.findByOnlineId(auth0Sub) ?: throw Exception("User not found")

val user = onlineUserRepository.findByOnlineId(getUserSub()) ?: throw Exception("User not found");
val currentTime = LocalDateTime.now()
if (Duration.between(user.lastUpdated, currentTime).toMillis() > adminRecheckTime) {
user.lastUpdated = currentTime
val now = LocalDateTime.now()
val hoursSinceUpdate = Duration.between(user.lastUpdated, now).toHours()

val isAdmin = checkBankomMembership()
user.isAdmin = isAdmin
onlineUserRepository.save(user)
// If never updated or 24h passed
if (user.lastUpdated == null || hoursSinceUpdate >= 24) {

return isAdmin;
} else {
return user.isAdmin;
}
}
// This function calls user.getMe to get the internal UUID
// and then calls group.allByMember
val committees = fetchUserCommittees()

private fun checkBankomMembership(): Boolean {
if (environment == "dev") {
return true
}
user.isAdmin = committees.contains(adminCommittee)
user.lastUpdated = now

val userCommittees = fetchUserCommittees()
return userCommittees.contains(adminCommittee)
}
// onlineUserRepository.save(user) persists the isAdmin status
// to your database so you don't have to call the API every time.
onlineUserRepository.save(user)
}

/*
fun checkSuperAdmin(): Boolean {
return superadminEmails.split(",").contains(getUserDetails().email)
return user.isAdmin
}
*/


fun getExpiresAt(): Instant? {
val authentication = SecurityContextHolder.getContext().authentication
Expand All @@ -177,14 +179,41 @@ class AuthenticationService(
}
}

data class Result(
val name_long: String = ""
)
data class UserCommitteeResponse(
val result: Result
)

data class Result(
val data: Data
)

data class UserCommitteeResponse(
val results: List<Result> = listOf()
)
data class Data(
val json: List<Committee>
)

}
data class Committee(
val slug: String,
val abbreviation: String,
val name: String,
val type: String,
val memberVisibility: String,
// Add other fields if needed
)
data class UserResponse(
val result: UserResult
)

data class UserResult(
val data: UserData
)

data class UserData(
val json: User // Single User object, not List<User>
)

data class User(
val id: String,
val email: String,
val name: String
)
}