Skip to content

Commit

Permalink
Merge pull request #4461 from navikt/flytt-altinn-acl
Browse files Browse the repository at this point in the history
Flytt altinn acl
  • Loading branch information
fredrikpe authored Oct 11, 2024
2 parents d052660 + 9a2fa36 commit 87c61e2
Show file tree
Hide file tree
Showing 28 changed files with 739 additions and 140 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package no.nav.mulighetsrommet.tokenprovider

import com.nimbusds.jose.JWSSigner
import com.nimbusds.jose.crypto.RSASSASigner
import com.nimbusds.jose.jwk.RSAKey
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.oauth2.sdk.JWTBearerGrant
import com.nimbusds.oauth2.sdk.Scope
import com.nimbusds.oauth2.sdk.TokenRequest
import com.nimbusds.oauth2.sdk.TokenResponse
import no.nav.common.token_client.utils.TokenClientUtils
import org.slf4j.LoggerFactory
import java.net.URI
import java.util.*

class MaskinPortenTokenProvider(
private val clientId: String,
private val issuer: String,
tokenEndpointUrl: String,
privateJwk: String,
) {
private val log = LoggerFactory.getLogger(javaClass)
private val tokenEndpoint: URI
private val privateJwkKeyId: String
private val assertionSigner: JWSSigner

data class Config(
val clientId: String,
val issuer: String,
val tokenEndpointUrl: String,
val privateJwk: String,
)

init {
val rsaKey = RSAKey.parse(privateJwk)

tokenEndpoint = URI.create(tokenEndpointUrl)
privateJwkKeyId = rsaKey.keyID
assertionSigner = RSASSASigner(rsaKey)
}

private fun createToken(scope: String, targetAudience: String): String {
val signedJwt = TokenClientUtils.signedClientAssertion(
TokenClientUtils.clientAssertionHeader(privateJwkKeyId),
clientAssertionClaims(targetAudience = targetAudience, scope = scope),
assertionSigner,
)

val request =
TokenRequest(
tokenEndpoint,
JWTBearerGrant(signedJwt.clientAssertion),
Scope(*(scope.split(" ")).toTypedArray()),
)

val response = TokenResponse.parse(request.toHTTPRequest().send())

if (!response.indicatesSuccess()) {
val tokenErrorResponse = response.toErrorResponse()
log.error(
"Failed to fetch Maskinporten M2M token for scope={}. Error: {}",
scope,
tokenErrorResponse.toJSONObject().toString(),
)
throw RuntimeException("Failed to fetch Maskinporten M2M token for scope=$scope")
}

return response
.toSuccessResponse()
.tokens
.accessToken
.value
}

fun withScope(scope: String, targetAudience: String): M2MTokenProvider {
return M2MTokenProvider exchange@{ accessType ->
createToken(scope, targetAudience)
}
}

fun clientAssertionClaims(
targetAudience: String,
scope: String,
): JWTClaimsSet {
val now = Date()
val expiration = Date(now.toInstant().plusSeconds(30).toEpochMilli())

return JWTClaimsSet.Builder()
.subject(clientId)
.issuer(clientId)
.audience(issuer)
.jwtID(UUID.randomUUID().toString())
.issueTime(now)
.notBeforeTime(now)
.expirationTime(expiration)
.claim("resource", targetAudience)
.claim("scope", scope)
.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ fun interface TokenProvider {
suspend fun exchange(accessType: AccessType): String
}

fun interface M2MTokenProvider {
suspend fun exchange(accessType: AccessType.M2M): String
}

/**
* Denne wrapper kall til login.microsoft i `CoroutineScope(Dispatchers.IO)`
* som gjør at man kan gjøre token exchanges i parallel (dvs. med coroutines uten
Expand Down Expand Up @@ -111,6 +115,23 @@ private fun createM2mTokenClient(clientId: String, tokenEndpointUrl: String): Ma
else -> AzureAdTokenClientBuilder.builder().withNaisDefaults().buildMachineToMachineTokenClient()
}

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
70 changes: 0 additions & 70 deletions frontend/arrangor-flate/app/auth/altinn.server.ts

This file was deleted.

14 changes: 0 additions & 14 deletions frontend/arrangor-flate/app/mocks/altinnMocks.ts

This file was deleted.

3 changes: 1 addition & 2 deletions frontend/arrangor-flate/app/mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { mulighetsrommetAltinnAclHandlers } from "./altinnMocks";
import { refusjonHandlers } from "./refusjonMocks";

export const handlers = [...mulighetsrommetAltinnAclHandlers, ...refusjonHandlers];
export const handlers = [...refusjonHandlers];
17 changes: 2 additions & 15 deletions frontend/arrangor-flate/app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { RefusjonKravAft, RefusjonskravService } from "@mr/api-client";
import { Heading, VStack } from "@navikt/ds-react";
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { RefusjonskravTable } from "~/components/refusjonskrav/RefusjonskravTable";
import { getTilganger } from "../auth/altinn.server";
import { setupOpenApi } from "../auth/auth.server";
import { PageHeader } from "../components/PageHeader";

Expand All @@ -16,20 +15,8 @@ export const meta: MetaFunction = () => {
};

export async function loader({ request }: LoaderFunctionArgs) {
const tilganger = await getTilganger(request);

if (tilganger.roller.length === 0) {
throw redirect("/ingen-tilgang");
}

await setupOpenApi(request);
// TODO: Vi trenger en måte å velge orgrn på
// F. eks med bedriftsvelger (eller hva det heter) som min-side-arbeidsgiver bruker
const krav = await RefusjonskravService.getRefusjonskrav({
requestBody: {
orgnr: tilganger.roller.map((rolle) => rolle.organisasjonsnummer),
},
});
const krav = await RefusjonskravService.getRefusjonskrav();

return json({ krav });
}
Expand Down
10 changes: 9 additions & 1 deletion mulighetsrommet-api/.nais/nais-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,15 @@ spec:
- host: axsys.dev-fss-pub.nais.io
- host: pdl-api.dev-fss-pub.nais.io
- host: api.utdanning.no

- host: platform.tt02.altinn.no
- host: test.maskinporten.no
envFrom:
- secret: mulighetsrommet-api
- secret: mr-admin-flate-unleash-api-token
- secret: altinn-api-key
maskinporten:
enabled: true
scopes:
consumes:
- name: "altinn:authorization/authorize"
- name: "altinn:accessmanagement/authorizedparties.resourceowner"
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package no.nav.mulighetsrommet.api

import io.ktor.client.engine.*
import io.ktor.client.engine.cio.*
import no.nav.mulighetsrommet.api.clients.altinn.AltinnClient
import no.nav.mulighetsrommet.api.clients.brreg.BrregClient
import no.nav.mulighetsrommet.api.clients.sanity.SanityClient
import no.nav.mulighetsrommet.api.clients.utdanning.UtdanningClient
Expand Down Expand Up @@ -50,11 +51,13 @@ data class AppConfig(
val pdl: ServiceClientConfig,
val engine: HttpClientEngine = CIO.create(),
val utdanning: UtdanningClient.Config,
val altinn: AltinnClient.Config,
)

data class AuthConfig(
val azure: AuthProvider,
val tokenx: AuthProvider,
val maskinporten: AuthProvider,
val roles: List<AdGruppeNavAnsattRolleMapping>,
)

Expand Down
Loading

0 comments on commit 87c61e2

Please sign in to comment.