Skip to content

Commit

Permalink
Gå mot arbeidsgiver-altinn-tilganger
Browse files Browse the repository at this point in the history
  • Loading branch information
sndrem committed Oct 24, 2024
1 parent 57d4f39 commit e3f1785
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 234 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ package no.nav.mulighetsrommet.tokenprovider
sealed class AccessType {
data class OBO(val token: String) : AccessType()
data object M2M : AccessType()
data class TOKENX(val token: String) : AccessType()
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import no.nav.common.token_client.builder.AzureAdTokenClientBuilder
import no.nav.common.token_client.builder.TokenXTokenClientBuilder
import no.nav.common.token_client.client.MachineToMachineTokenClient
import no.nav.common.token_client.client.OnBehalfOfTokenClient
import no.nav.common.token_client.client.TokenXOnBehalfOfTokenClient
import no.nav.mulighetsrommet.env.NaisEnv
import java.security.KeyPairGenerator
import java.security.interfaces.RSAPrivateKey
Expand All @@ -39,7 +37,6 @@ fun interface M2MTokenProvider {
* spør igjen for å hente det cachede tokenet.
*/
class CachedTokenProvider(
private val tokenXTokenProvider: TokenXOnBehalfOfTokenClient,
private val oboTokenProvider: OnBehalfOfTokenClient,
private val m2MTokenProvider: MachineToMachineTokenClient,
) {
Expand All @@ -52,7 +49,6 @@ class CachedTokenProvider(
companion object {
fun init(clientId: String, tokenEndpointUrl: String): CachedTokenProvider {
return CachedTokenProvider(
tokenXTokenProvider = createTokenXTokenClient(clientId),
oboTokenProvider = createOboTokenClient(clientId, tokenEndpointUrl),
m2MTokenProvider = createM2mTokenClient(clientId, tokenEndpointUrl),
)
Expand All @@ -75,7 +71,6 @@ class CachedTokenProvider(
private fun exchangeAsync(scope: String, accessType: AccessType): Deferred<String> {
return CoroutineScope(Dispatchers.IO).async {
when (accessType) {
is AccessType.TOKENX -> tokenXTokenProvider.exchangeOnBehalfOfToken(scope, accessType.token)
AccessType.M2M -> m2MTokenProvider.createMachineToMachineToken(scope)
is AccessType.OBO -> oboTokenProvider.exchangeOnBehalfOfToken(scope, accessType.token)
}
Expand All @@ -96,17 +91,6 @@ private fun AccessType.subject(): String =
throw IllegalArgumentException("Unable to get subject, access token is invalid")
}
}

is AccessType.TOKENX -> {
try {
val token = JWTParser.parse(this.token)
val subject = token.jwtClaimsSet.subject
?: throw IllegalArgumentException("Unable to get subject, access token is missing subject")
subject
} catch (e: ParseException) {
throw IllegalArgumentException("Unable to get subject, access token is invalid")
}
}
}

private fun createOboTokenClient(clientId: String, tokenEndpointUrl: String): OnBehalfOfTokenClient =
Expand All @@ -131,38 +115,6 @@ private fun createM2mTokenClient(clientId: String, tokenEndpointUrl: String): Ma
else -> AzureAdTokenClientBuilder.builder().withNaisDefaults().buildMachineToMachineTokenClient()
}

fun createTokenXTokenClient(clientId: String): TokenXOnBehalfOfTokenClient =
when (NaisEnv.current()) {
NaisEnv.Local -> TokenXTokenClientBuilder.builder()
.withClientId(clientId)
.withPrivateJwk(createMockRSAKey("azure").toJSONString())
.buildOnBehalfOfTokenClient()

else -> TokenXTokenClientBuilder.builder().withNaisDefaults().buildOnBehalfOfTokenClient()
}

fun createMaskinportenM2mTokenClient(
clientId: String,
tokenEndpointUrl: String,
issuer: String,
): MaskinPortenTokenProvider? =
when (NaisEnv.current()) {
NaisEnv.Local -> MaskinPortenTokenProvider(
clientId = clientId,
tokenEndpointUrl = tokenEndpointUrl,
privateJwk = createMockRSAKey("maskinporten").toJSONString(),
issuer = issuer,
)

NaisEnv.ProdGCP -> null // TODO: Remove when prod
else -> MaskinPortenTokenProvider(
clientId = clientId,
tokenEndpointUrl = tokenEndpointUrl,
privateJwk = System.getenv("MASKINPORTEN_CLIENT_JWK"),
issuer = issuer,
)
}

private fun createMockRSAKey(keyID: String): RSAKey = KeyPairGenerator
.getInstance("RSA").let {
it.initialize(2048)
Expand Down
7 changes: 7 additions & 0 deletions mulighetsrommet-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ dependencies {
implementation(libs.nav.common.auditLog)
implementation(libs.nav.common.client)

implementation(libs.nav.common.tokenClient)
constraints {
implementation("net.minidev:json-smart:2.5.1") {
because("sikkerhetshull i transitiv avhengighet rapportert via snyk")
}
}

// Dependency injection
implementation(libs.koin.ktor)
implementation(libs.koin.logger.slf4j)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,13 @@ import io.ktor.http.*
import kotlinx.serialization.Serializable
import no.nav.mulighetsrommet.altinn.models.AltinnRessurs
import no.nav.mulighetsrommet.altinn.models.BedriftRettigheter
import no.nav.mulighetsrommet.domain.dto.NorskIdent
import no.nav.mulighetsrommet.domain.dto.Organisasjonsnummer
import no.nav.mulighetsrommet.ktor.clients.httpJsonClient
import no.nav.mulighetsrommet.tokenprovider.AccessType
import no.nav.mulighetsrommet.tokenprovider.TokenProvider
import org.slf4j.LoggerFactory

class AltinnClient(
private val baseUrl: String,
private val tokenProvider: TokenProvider,
private val tokenProvider: (token: String) -> String,
clientEngine: HttpClientEngine = CIO.create(),
) {
private val log = LoggerFactory.getLogger(javaClass)
Expand All @@ -32,30 +29,29 @@ class AltinnClient(
val scope: String,
)

suspend fun hentRettigheter(): List<BedriftRettigheter> {
log.info("Henter organisasjoner fra Altinn")
val tilganger = hentTilganger()
return findAltinnRoller(tilganger)
suspend fun hentRettigheter(token: String): List<BedriftRettigheter> {
log.info("Henter rettigheter fra Altinn for bruker via Team Fager")
val tilganger = hentTilganger(token)
return sjekkTilganger(tilganger)
}

private fun findAltinnRoller(
bedriftsrettigheter: List<BedriftRettigheter>,
): List<BedriftRettigheter> =
bedriftsrettigheter
.flatMap { rettighet ->
findAltinnRoller(rettighet.) +
BedriftRettigheter(
organisasjonsnummer = Organisasjonsnummer(rettighet.organizationNumber),
rettigheter = AltinnRessurs
.entries
.filter { it.ressursId in rettighet.authorizedResources },
)
fun sjekkTilganger(tilganger: Tilgangshierarki): List<BedriftRettigheter> {
val result = mutableListOf<BedriftRettigheter>()

fun checkTilganger(org: TilgangForOrganisasjon) {
if (AltinnRessurs.TILTAK_ARRANGOR_REFUSJON.ressursId in org.altinn3Tilganger) {
result.add(BedriftRettigheter(Organisasjonsnummer(org.orgnr), listOf(AltinnRessurs.TILTAK_ARRANGOR_REFUSJON)))
}
.filter { it.rettigheter.isNotEmpty() }
org.underenheter.forEach { checkTilganger(it) }
}

tilganger.hierarki.forEach { checkTilganger(it) }
return result
}

private suspend fun hentTilganger(): Tilgangshierarki {
private suspend fun hentTilganger(token: String): Tilgangshierarki {
val response = client.post("$baseUrl/altinn-tilganger") {
bearerAuth(tokenProvider.exchange(AccessType.M2M))
bearerAuth(tokenProvider.invoke(token))
header(HttpHeaders.ContentType, ContentType.Application.Json)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ class AltinnRettigheterService(
) {
private val rolleExpiryDuration = Duration.ofDays(1)

suspend fun getRettigheter(norskIdent: NorskIdent): List<BedriftRettigheter> {
suspend fun getRettigheter(token: String, norskIdent: NorskIdent): List<BedriftRettigheter> {
val bedriftRettigheter = altinnRettigheterRepository.getRettigheter(norskIdent)
return if (bedriftRettigheter.isEmpty() || bedriftRettigheter.any { it.rettigheter.any { it.expiry.isBefore(LocalDateTime.now()) } }) {
syncRettigheter(norskIdent)
syncRettigheter(token, norskIdent)
} else {
bedriftRettigheter.map { it.toBedriftRettigheter() }
}
}

private suspend fun syncRettigheter(norskIdent: NorskIdent): List<BedriftRettigheter> {
val rettigheter = altinnClient.hentRettigheter()
private suspend fun syncRettigheter(token: String, norskIdent: NorskIdent): List<BedriftRettigheter> {
val rettigheter = altinnClient.hentRettigheter(token)
altinnRettigheterRepository.upsertRettighet(
PersonBedriftRettigheter(
norskIdent = norskIdent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ data class AuthProvider(
val issuer: String,
val jwksUri: String,
val audience: String,
val tokenEndpointUrl: String,
val tokenEndpointUrl: String? = null,
val wellKnownUrl: String? = null,
)

data class ServiceClientConfig(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import no.nav.mulighetsrommet.domain.dto.NavIdent
import no.nav.mulighetsrommet.domain.dto.NorskIdent
import no.nav.mulighetsrommet.domain.dto.Organisasjonsnummer
import no.nav.mulighetsrommet.ktor.exception.StatusException
import no.nav.mulighetsrommet.ktor.extensions.getAccessToken
import org.koin.ktor.ext.inject
import java.net.URI
import java.util.*
Expand Down Expand Up @@ -262,11 +263,12 @@ fun Application.configureAuthentication(
}
validate { credentials ->
credentials["pid"] ?: return@validate null
val token = getAccessToken()
val norskIdent = credentials["pid"]?.let {
runCatching { NorskIdent(it) }.getOrNull()
} ?: return@validate null

val organisasjonsnummer = altinnRettigheterService.getRettigheter(norskIdent)
val organisasjonsnummer = altinnRettigheterService.getRettigheter(token, norskIdent)
.filter {
it.rettigheter.contains(AltinnRessurs.TILTAK_ARRANGOR_REFUSJON)
}
Expand Down
Loading

0 comments on commit e3f1785

Please sign in to comment.