Skip to content

Commit

Permalink
Fjerne abac-legacy og containerlogin, Legg til JaxRs-auth-filter (#1252)
Browse files Browse the repository at this point in the history
* Fjerne abac-legacy og containerlogin, Legg til JaxRs-auth-filter

* Søtte azure-pip
  • Loading branch information
jolarsen authored Feb 24, 2023
1 parent b819340 commit 98b5c40
Show file tree
Hide file tree
Showing 18 changed files with 643 additions and 180 deletions.

This file was deleted.

4 changes: 2 additions & 2 deletions felles/abac/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Det vil mangle en implementasjon av TokenProvider og det er mulig å implementer
```
<dependency>
<groupId>no.nav.foreldrepenger.felles</groupId>
<artifactId>felles-abac-legacy</artifactId>
<artifactId>felles-abac-kontekst</artifactId>
</dependency>
```
Denne kommer med en avhengighet til felles-sikkerhet og SubjectHandler.
Denne kommer med en avhengighet til felles-kontekst og KontekstHolder.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public Tilgangsbeslutning vurderTilgang(BeskyttetRessursAttributter beskyttetRes
if (PIP.equals(beskyttetRessursAttributter.getResourceType())) {
return vurderTilgangTilPipTjeneste(beskyttetRessursAttributter, appRessurser);
}
if (skalForetaLokalTilgangsbeslutning(beskyttetRessursAttributter)) {
if (kanForetaLokalTilgangsbeslutning(beskyttetRessursAttributter)) {
return new Tilgangsbeslutning(harTilgang(beskyttetRessursAttributter) ? GODKJENT : AVSLÅTT_ANNEN_ÅRSAK, beskyttetRessursAttributter, appRessurser);
}
return pdpKlient.forespørTilgang(beskyttetRessursAttributter, builder.abacDomene(), appRessurser);
Expand All @@ -70,7 +70,7 @@ public Tilgangsbeslutning vurderTilgang(BeskyttetRessursAttributter beskyttetRes
// AzureAD CC kommer med sub som ikke ikke en bruker med vanlige AD-grupper og roller
// Token kan utvides med roles og groups - men oppsettet er langt fra det som er kjent fra STS mv.
// Kan legge inn filter på claims/roles intern og/eller ekstern.
private boolean skalForetaLokalTilgangsbeslutning(BeskyttetRessursAttributter attributter) {
private boolean kanForetaLokalTilgangsbeslutning(BeskyttetRessursAttributter attributter) {
var identType = attributter.getToken().getIdentType();
var consumer = attributter.getToken().getBrukerId();
return OpenIDProvider.AZUREAD.equals(attributter.getToken().getOpenIDProvider())
Expand All @@ -92,6 +92,8 @@ protected Tilgangsbeslutning vurderTilgangTilPipTjeneste(BeskyttetRessursAttribu
String uid = tokenProvider.getUid();
if (pipUsers.contains(uid.toLowerCase())) {
return new Tilgangsbeslutning(GODKJENT, beskyttetRessursAttributter, appRessursData);
} else if (kanForetaLokalTilgangsbeslutning(beskyttetRessursAttributter) && harTilgang(beskyttetRessursAttributter)) {
return new Tilgangsbeslutning(GODKJENT, beskyttetRessursAttributter, appRessursData);
} else {
return new Tilgangsbeslutning(AVSLÅTT_ANNEN_ÅRSAK, beskyttetRessursAttributter, appRessursData);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package no.nav.vedtak.sikkerhet.abac;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.interceptor.InterceptorBinding;
import javax.ws.rs.NameBinding;

@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@NameBinding
public @interface ÅpenRessurs {
}
27 changes: 22 additions & 5 deletions felles/abac-legacy/pom.xml → felles/auth-filter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,41 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<artifactId>felles</artifactId>
<groupId>no.nav.foreldrepenger.felles</groupId>
<version>0.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>felles-abac-legacy</artifactId>
<name>Felles :: ABAC</name>

<artifactId>felles-auth-filter</artifactId>
<name>Felles :: Auth-filter</name>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>no.nav.foreldrepenger.felles.sikkerhet</groupId>
<artifactId>felles-sikkerhet</artifactId>
<groupId>no.nav.foreldrepenger.felles</groupId>
<artifactId>felles-log</artifactId>
</dependency>
<dependency>
<groupId>no.nav.foreldrepenger.felles</groupId>
<artifactId>felles-oidc</artifactId>
</dependency>
<dependency>
<groupId>no.nav.foreldrepenger.felles</groupId>
<artifactId>felles-abac</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package no.nav.vedtak.sikkerhet.jaxrs;

import java.lang.reflect.Method;
import java.time.Instant;
import java.util.Optional;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;

import org.jose4j.jwt.MalformedClaimException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import no.nav.vedtak.log.mdc.MDCOperations;
import no.nav.vedtak.sikkerhet.abac.BeskyttetRessurs;
import no.nav.vedtak.sikkerhet.abac.ÅpenRessurs;
import no.nav.vedtak.sikkerhet.kontekst.BasisKontekst;
import no.nav.vedtak.sikkerhet.kontekst.KontekstHolder;
import no.nav.vedtak.sikkerhet.kontekst.RequestKontekst;
import no.nav.vedtak.sikkerhet.oidc.config.ConfigProvider;
import no.nav.vedtak.sikkerhet.oidc.token.OpenIDToken;
import no.nav.vedtak.sikkerhet.oidc.token.TokenString;
import no.nav.vedtak.sikkerhet.oidc.validator.JwtUtil;
import no.nav.vedtak.sikkerhet.oidc.validator.OidcTokenValidator;
import no.nav.vedtak.sikkerhet.oidc.validator.OidcTokenValidatorConfig;
import no.nav.vedtak.sikkerhet.oidc.validator.OidcTokenValidatorResult;

/**
* Bruksanvisning inntil alle er over og det evt samles her:
* - App må lage et filter som implementerer ContainerRequestFilter
* - Filter må annotert med Provider og legges inn i Application/getClasses()
* - Legg på @Context private ResourceInfo resourceinfo
* - Kall AuthenticationFilterDelegate . validerSettKontekst og la evt exceptions passere ut til Jersey
*
* App må også ha et ContainerResponseFilter som kaller AuthenticationFilterDelegate . fjernKontekst
*/
public class AuthenticationFilterDelegate {

private static final Logger LOG = LoggerFactory.getLogger(AuthenticationFilterDelegate.class);

private static final String ID_TOKEN_COOKIE_NAME = "ID_token";
private static final String AUTHORIZATION_HEADER = HttpHeaders.AUTHORIZATION;

private AuthenticationFilterDelegate() {
}


public static void validerSettKontekst(ResourceInfo resourceInfo, ContainerRequestContext ctx) {
validerSettKontekst(resourceInfo, ctx, null);
}

public static void validerSettKontekst(ResourceInfo resourceInfo, ContainerRequestContext ctx, String cookiePath) {
try {
Method method = resourceInfo.getResourceMethod();
var beskyttetRessurs = method.getAnnotation(BeskyttetRessurs.class);
var åpenRessurs = method.getAnnotation(ÅpenRessurs.class);
var metodenavn = method.getName();
LOG.trace("{} i klasse {}", metodenavn, method.getDeclaringClass());
setCallAndConsumerId(ctx);
if (beskyttetRessurs == null && (åpenRessurs != null || method.getDeclaringClass().getName().startsWith("io.swagger"))) {
KontekstHolder.setKontekst(BasisKontekst.ikkeAutentisertRequest(MDCOperations.getConsumerId()));
LOG.trace("{} er whitelisted", metodenavn);
} else if (beskyttetRessurs == null) {
throw new WebApplicationException(metodenavn + " mangler annotering", Response.Status.INTERNAL_SERVER_ERROR);
} else {
var tokenString = getTokenFromHeader(ctx)
.or(() -> getCookie(ctx, cookiePath))
.orElseThrow(() -> new TokenFeil("Mangler token"));
validerToken(tokenString);
}
} catch (MalformedClaimException|TokenFeil e) {
throw new WebApplicationException(e, Response.Status.FORBIDDEN);
} catch (WebApplicationException e) {
throw e;
} catch (Exception e) {
throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);
}
}

public static void fjernKontekst() {
if (KontekstHolder.harKontekst()) {
KontekstHolder.fjernKontekst();
}
}

private static void setCallAndConsumerId(ContainerRequestContext request) {
String callId = Optional.ofNullable(request.getHeaderString(MDCOperations.HTTP_HEADER_CALL_ID)) // NOSONAR Akseptertet headere
.or(() -> Optional.ofNullable(request.getHeaderString(MDCOperations.HTTP_HEADER_ALT_CALL_ID)))
.orElseGet(MDCOperations::generateCallId);
MDCOperations.putCallId(callId);

Optional.ofNullable(request.getHeaderString(MDCOperations.HTTP_HEADER_CONSUMER_ID))
.ifPresent(MDCOperations::putConsumerId);
}

private static Optional<TokenString> getTokenFromHeader(ContainerRequestContext request) {
String headerValue = request.getHeaderString(AUTHORIZATION_HEADER);
return headerValue != null && headerValue.startsWith(OpenIDToken.OIDC_DEFAULT_TOKEN_TYPE)
? Optional.of(new TokenString(headerValue.substring(OpenIDToken.OIDC_DEFAULT_TOKEN_TYPE.length())))
: Optional.empty();
}

private static Optional<TokenString> getCookie(ContainerRequestContext request, String cookiePath) {
if (cookiePath == null || request.getCookies() == null) {
return Optional.empty();
}
return request.getCookies().values().stream()
.filter(c -> c.getValue() != null)
.filter(c -> ID_TOKEN_COOKIE_NAME.equalsIgnoreCase(c.getName()))
.filter(c -> cookiePath.equalsIgnoreCase(c.getPath()))
.findFirst()
.or(() -> request.getCookies().values().stream()
.filter(c -> c.getValue() != null)
.filter(c -> ID_TOKEN_COOKIE_NAME.equalsIgnoreCase(c.getName()))
.findFirst())
.map(Cookie::getValue)
.map(TokenString::new);
}

public static void validerToken(TokenString tokenString) throws MalformedClaimException {
// Sett opp OpenIDToken
var claims = JwtUtil.getClaims(tokenString.token());
var configuration = ConfigProvider.getOpenIDConfiguration(claims.getIssuer())
.orElseThrow(() -> new TokenFeil("Token mangler issuer claim"));
var expiresAt = Optional.ofNullable(JwtUtil.getExpirationTime(claims)).orElseGet(() -> Instant.now().plusSeconds(300));
var token = new OpenIDToken(configuration.type(), OpenIDToken.OIDC_DEFAULT_TOKEN_TYPE, tokenString, null, expiresAt.toEpochMilli());

// Valider
var tokenValidator = OidcTokenValidatorConfig.instance().getValidator(token.provider());
var validateResult = tokenValidator.validate(token.primary());

// Håndter valideringsresultat
if (needToRefreshToken(token, validateResult, tokenValidator)) {
throw new ValideringsFeil("Token expired");
}
if (validateResult.isValid()) {
KontekstHolder.setKontekst(RequestKontekst.forRequest(validateResult.subject(), validateResult.compactSubject(), validateResult.identType(), token));
LOG.trace("token validert");
} else {
throw new ValideringsFeil("Ugyldig token");
}
}

private static boolean needToRefreshToken(OpenIDToken token, OidcTokenValidatorResult validateResult, OidcTokenValidator tokenValidator) {
return !validateResult.isValid() && tokenValidator.validateWithoutExpirationTime(token.primary()).isValid();
}

private static class TokenFeil extends RuntimeException {
TokenFeil(String message) {
super(message);
}
}

private static class ValideringsFeil extends RuntimeException {
ValideringsFeil(String message) {
super(message);
}
}

}
Loading

0 comments on commit 98b5c40

Please sign in to comment.