Skip to content

Commit 98b5c40

Browse files
authored
Fjerne abac-legacy og containerlogin, Legg til JaxRs-auth-filter (#1252)
* Fjerne abac-legacy og containerlogin, Legg til JaxRs-auth-filter * Søtte azure-pip
1 parent b819340 commit 98b5c40

File tree

18 files changed

+643
-180
lines changed

18 files changed

+643
-180
lines changed

felles/abac-legacy/src/main/java/no/nav/foreldrepenger/sikkerhet/abac/LegacyTokenProvider.java

-32
This file was deleted.

felles/abac/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Det vil mangle en implementasjon av TokenProvider og det er mulig å implementer
2626
```
2727
<dependency>
2828
<groupId>no.nav.foreldrepenger.felles</groupId>
29-
<artifactId>felles-abac-legacy</artifactId>
29+
<artifactId>felles-abac-kontekst</artifactId>
3030
</dependency>
3131
```
32-
Denne kommer med en avhengighet til felles-sikkerhet og SubjectHandler.
32+
Denne kommer med en avhengighet til felles-kontekst og KontekstHolder.

felles/abac/src/main/java/no/nav/vedtak/sikkerhet/abac/PepImpl.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public Tilgangsbeslutning vurderTilgang(BeskyttetRessursAttributter beskyttetRes
6161
if (PIP.equals(beskyttetRessursAttributter.getResourceType())) {
6262
return vurderTilgangTilPipTjeneste(beskyttetRessursAttributter, appRessurser);
6363
}
64-
if (skalForetaLokalTilgangsbeslutning(beskyttetRessursAttributter)) {
64+
if (kanForetaLokalTilgangsbeslutning(beskyttetRessursAttributter)) {
6565
return new Tilgangsbeslutning(harTilgang(beskyttetRessursAttributter) ? GODKJENT : AVSLÅTT_ANNEN_ÅRSAK, beskyttetRessursAttributter, appRessurser);
6666
}
6767
return pdpKlient.forespørTilgang(beskyttetRessursAttributter, builder.abacDomene(), appRessurser);
@@ -70,7 +70,7 @@ public Tilgangsbeslutning vurderTilgang(BeskyttetRessursAttributter beskyttetRes
7070
// AzureAD CC kommer med sub som ikke ikke en bruker med vanlige AD-grupper og roller
7171
// Token kan utvides med roles og groups - men oppsettet er langt fra det som er kjent fra STS mv.
7272
// Kan legge inn filter på claims/roles intern og/eller ekstern.
73-
private boolean skalForetaLokalTilgangsbeslutning(BeskyttetRessursAttributter attributter) {
73+
private boolean kanForetaLokalTilgangsbeslutning(BeskyttetRessursAttributter attributter) {
7474
var identType = attributter.getToken().getIdentType();
7575
var consumer = attributter.getToken().getBrukerId();
7676
return OpenIDProvider.AZUREAD.equals(attributter.getToken().getOpenIDProvider())
@@ -92,6 +92,8 @@ protected Tilgangsbeslutning vurderTilgangTilPipTjeneste(BeskyttetRessursAttribu
9292
String uid = tokenProvider.getUid();
9393
if (pipUsers.contains(uid.toLowerCase())) {
9494
return new Tilgangsbeslutning(GODKJENT, beskyttetRessursAttributter, appRessursData);
95+
} else if (kanForetaLokalTilgangsbeslutning(beskyttetRessursAttributter) && harTilgang(beskyttetRessursAttributter)) {
96+
return new Tilgangsbeslutning(GODKJENT, beskyttetRessursAttributter, appRessursData);
9597
} else {
9698
return new Tilgangsbeslutning(AVSLÅTT_ANNEN_ÅRSAK, beskyttetRessursAttributter, appRessursData);
9799
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package no.nav.vedtak.sikkerhet.abac;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Inherited;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
import javax.interceptor.InterceptorBinding;
10+
import javax.ws.rs.NameBinding;
11+
12+
@Inherited
13+
@InterceptorBinding
14+
@Retention(RetentionPolicy.RUNTIME)
15+
@Target({ ElementType.TYPE, ElementType.METHOD })
16+
@NameBinding
17+
public @interface ÅpenRessurs {
18+
}

felles/abac-legacy/pom.xml felles/auth-filter/pom.xml

+22-5
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,41 @@
22
<project xmlns="http://maven.apache.org/POM/4.0.0"
33
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
44
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
57
<parent>
68
<artifactId>felles</artifactId>
79
<groupId>no.nav.foreldrepenger.felles</groupId>
810
<version>0.0.0-SNAPSHOT</version>
911
</parent>
10-
<modelVersion>4.0.0</modelVersion>
1112

12-
<artifactId>felles-abac-legacy</artifactId>
13-
<name>Felles :: ABAC</name>
13+
14+
<artifactId>felles-auth-filter</artifactId>
15+
<name>Felles :: Auth-filter</name>
16+
<packaging>jar</packaging>
1417

1518
<dependencies>
1619
<dependency>
17-
<groupId>no.nav.foreldrepenger.felles.sikkerhet</groupId>
18-
<artifactId>felles-sikkerhet</artifactId>
20+
<groupId>no.nav.foreldrepenger.felles</groupId>
21+
<artifactId>felles-log</artifactId>
22+
</dependency>
23+
<dependency>
24+
<groupId>no.nav.foreldrepenger.felles</groupId>
25+
<artifactId>felles-oidc</artifactId>
1926
</dependency>
2027
<dependency>
2128
<groupId>no.nav.foreldrepenger.felles</groupId>
2229
<artifactId>felles-abac</artifactId>
2330
</dependency>
31+
<dependency>
32+
<groupId>jakarta.ws.rs</groupId>
33+
<artifactId>jakarta.ws.rs-api</artifactId>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.glassfish.jersey.core</groupId>
37+
<artifactId>jersey-server</artifactId>
38+
<scope>test</scope>
39+
</dependency>
2440
</dependencies>
41+
2542
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package no.nav.vedtak.sikkerhet.jaxrs;
2+
3+
import java.lang.reflect.Method;
4+
import java.time.Instant;
5+
import java.util.Optional;
6+
7+
import javax.ws.rs.WebApplicationException;
8+
import javax.ws.rs.container.ContainerRequestContext;
9+
import javax.ws.rs.container.ResourceInfo;
10+
import javax.ws.rs.core.Cookie;
11+
import javax.ws.rs.core.HttpHeaders;
12+
import javax.ws.rs.core.Response;
13+
14+
import org.jose4j.jwt.MalformedClaimException;
15+
import org.slf4j.Logger;
16+
import org.slf4j.LoggerFactory;
17+
18+
import no.nav.vedtak.log.mdc.MDCOperations;
19+
import no.nav.vedtak.sikkerhet.abac.BeskyttetRessurs;
20+
import no.nav.vedtak.sikkerhet.abac.ÅpenRessurs;
21+
import no.nav.vedtak.sikkerhet.kontekst.BasisKontekst;
22+
import no.nav.vedtak.sikkerhet.kontekst.KontekstHolder;
23+
import no.nav.vedtak.sikkerhet.kontekst.RequestKontekst;
24+
import no.nav.vedtak.sikkerhet.oidc.config.ConfigProvider;
25+
import no.nav.vedtak.sikkerhet.oidc.token.OpenIDToken;
26+
import no.nav.vedtak.sikkerhet.oidc.token.TokenString;
27+
import no.nav.vedtak.sikkerhet.oidc.validator.JwtUtil;
28+
import no.nav.vedtak.sikkerhet.oidc.validator.OidcTokenValidator;
29+
import no.nav.vedtak.sikkerhet.oidc.validator.OidcTokenValidatorConfig;
30+
import no.nav.vedtak.sikkerhet.oidc.validator.OidcTokenValidatorResult;
31+
32+
/**
33+
* Bruksanvisning inntil alle er over og det evt samles her:
34+
* - App må lage et filter som implementerer ContainerRequestFilter
35+
* - Filter må annotert med Provider og legges inn i Application/getClasses()
36+
* - Legg på @Context private ResourceInfo resourceinfo
37+
* - Kall AuthenticationFilterDelegate . validerSettKontekst og la evt exceptions passere ut til Jersey
38+
*
39+
* App må også ha et ContainerResponseFilter som kaller AuthenticationFilterDelegate . fjernKontekst
40+
*/
41+
public class AuthenticationFilterDelegate {
42+
43+
private static final Logger LOG = LoggerFactory.getLogger(AuthenticationFilterDelegate.class);
44+
45+
private static final String ID_TOKEN_COOKIE_NAME = "ID_token";
46+
private static final String AUTHORIZATION_HEADER = HttpHeaders.AUTHORIZATION;
47+
48+
private AuthenticationFilterDelegate() {
49+
}
50+
51+
52+
public static void validerSettKontekst(ResourceInfo resourceInfo, ContainerRequestContext ctx) {
53+
validerSettKontekst(resourceInfo, ctx, null);
54+
}
55+
56+
public static void validerSettKontekst(ResourceInfo resourceInfo, ContainerRequestContext ctx, String cookiePath) {
57+
try {
58+
Method method = resourceInfo.getResourceMethod();
59+
var beskyttetRessurs = method.getAnnotation(BeskyttetRessurs.class);
60+
var åpenRessurs = method.getAnnotation(ÅpenRessurs.class);
61+
var metodenavn = method.getName();
62+
LOG.trace("{} i klasse {}", metodenavn, method.getDeclaringClass());
63+
setCallAndConsumerId(ctx);
64+
if (beskyttetRessurs == null && (åpenRessurs != null || method.getDeclaringClass().getName().startsWith("io.swagger"))) {
65+
KontekstHolder.setKontekst(BasisKontekst.ikkeAutentisertRequest(MDCOperations.getConsumerId()));
66+
LOG.trace("{} er whitelisted", metodenavn);
67+
} else if (beskyttetRessurs == null) {
68+
throw new WebApplicationException(metodenavn + " mangler annotering", Response.Status.INTERNAL_SERVER_ERROR);
69+
} else {
70+
var tokenString = getTokenFromHeader(ctx)
71+
.or(() -> getCookie(ctx, cookiePath))
72+
.orElseThrow(() -> new TokenFeil("Mangler token"));
73+
validerToken(tokenString);
74+
}
75+
} catch (MalformedClaimException|TokenFeil e) {
76+
throw new WebApplicationException(e, Response.Status.FORBIDDEN);
77+
} catch (WebApplicationException e) {
78+
throw e;
79+
} catch (Exception e) {
80+
throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);
81+
}
82+
}
83+
84+
public static void fjernKontekst() {
85+
if (KontekstHolder.harKontekst()) {
86+
KontekstHolder.fjernKontekst();
87+
}
88+
}
89+
90+
private static void setCallAndConsumerId(ContainerRequestContext request) {
91+
String callId = Optional.ofNullable(request.getHeaderString(MDCOperations.HTTP_HEADER_CALL_ID)) // NOSONAR Akseptertet headere
92+
.or(() -> Optional.ofNullable(request.getHeaderString(MDCOperations.HTTP_HEADER_ALT_CALL_ID)))
93+
.orElseGet(MDCOperations::generateCallId);
94+
MDCOperations.putCallId(callId);
95+
96+
Optional.ofNullable(request.getHeaderString(MDCOperations.HTTP_HEADER_CONSUMER_ID))
97+
.ifPresent(MDCOperations::putConsumerId);
98+
}
99+
100+
private static Optional<TokenString> getTokenFromHeader(ContainerRequestContext request) {
101+
String headerValue = request.getHeaderString(AUTHORIZATION_HEADER);
102+
return headerValue != null && headerValue.startsWith(OpenIDToken.OIDC_DEFAULT_TOKEN_TYPE)
103+
? Optional.of(new TokenString(headerValue.substring(OpenIDToken.OIDC_DEFAULT_TOKEN_TYPE.length())))
104+
: Optional.empty();
105+
}
106+
107+
private static Optional<TokenString> getCookie(ContainerRequestContext request, String cookiePath) {
108+
if (cookiePath == null || request.getCookies() == null) {
109+
return Optional.empty();
110+
}
111+
return request.getCookies().values().stream()
112+
.filter(c -> c.getValue() != null)
113+
.filter(c -> ID_TOKEN_COOKIE_NAME.equalsIgnoreCase(c.getName()))
114+
.filter(c -> cookiePath.equalsIgnoreCase(c.getPath()))
115+
.findFirst()
116+
.or(() -> request.getCookies().values().stream()
117+
.filter(c -> c.getValue() != null)
118+
.filter(c -> ID_TOKEN_COOKIE_NAME.equalsIgnoreCase(c.getName()))
119+
.findFirst())
120+
.map(Cookie::getValue)
121+
.map(TokenString::new);
122+
}
123+
124+
public static void validerToken(TokenString tokenString) throws MalformedClaimException {
125+
// Sett opp OpenIDToken
126+
var claims = JwtUtil.getClaims(tokenString.token());
127+
var configuration = ConfigProvider.getOpenIDConfiguration(claims.getIssuer())
128+
.orElseThrow(() -> new TokenFeil("Token mangler issuer claim"));
129+
var expiresAt = Optional.ofNullable(JwtUtil.getExpirationTime(claims)).orElseGet(() -> Instant.now().plusSeconds(300));
130+
var token = new OpenIDToken(configuration.type(), OpenIDToken.OIDC_DEFAULT_TOKEN_TYPE, tokenString, null, expiresAt.toEpochMilli());
131+
132+
// Valider
133+
var tokenValidator = OidcTokenValidatorConfig.instance().getValidator(token.provider());
134+
var validateResult = tokenValidator.validate(token.primary());
135+
136+
// Håndter valideringsresultat
137+
if (needToRefreshToken(token, validateResult, tokenValidator)) {
138+
throw new ValideringsFeil("Token expired");
139+
}
140+
if (validateResult.isValid()) {
141+
KontekstHolder.setKontekst(RequestKontekst.forRequest(validateResult.subject(), validateResult.compactSubject(), validateResult.identType(), token));
142+
LOG.trace("token validert");
143+
} else {
144+
throw new ValideringsFeil("Ugyldig token");
145+
}
146+
}
147+
148+
private static boolean needToRefreshToken(OpenIDToken token, OidcTokenValidatorResult validateResult, OidcTokenValidator tokenValidator) {
149+
return !validateResult.isValid() && tokenValidator.validateWithoutExpirationTime(token.primary()).isValid();
150+
}
151+
152+
private static class TokenFeil extends RuntimeException {
153+
TokenFeil(String message) {
154+
super(message);
155+
}
156+
}
157+
158+
private static class ValideringsFeil extends RuntimeException {
159+
ValideringsFeil(String message) {
160+
super(message);
161+
}
162+
}
163+
164+
}

0 commit comments

Comments
 (0)