-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Protect custom resource with custom filter
- Loading branch information
Showing
6 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
...s/src/main/java/com/github/thomasdarimont/keycloak/custom/endpoints/filter/AzpFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package com.github.thomasdarimont.keycloak.custom.endpoints.filter; | ||
|
||
import com.github.thomasdarimont.keycloak.custom.support.KeycloakSessionLookup; | ||
import jakarta.annotation.Priority; | ||
import jakarta.ws.rs.ForbiddenException; | ||
import jakarta.ws.rs.InternalServerErrorException; | ||
import jakarta.ws.rs.container.ContainerRequestContext; | ||
import jakarta.ws.rs.container.ContainerRequestFilter; | ||
import jakarta.ws.rs.ext.Provider; | ||
import lombok.extern.jbosslog.JBossLog; | ||
import org.keycloak.representations.AccessToken; | ||
|
||
import java.io.IOException; | ||
import java.util.regex.Pattern; | ||
|
||
import static com.github.thomasdarimont.keycloak.custom.endpoints.filter.TokenFilter.ACCESS_TOKEN_SESSION_KEY; | ||
|
||
@AzpFilterBinding | ||
@JBossLog | ||
@Priority(1) | ||
@Provider | ||
public class AzpFilter implements ContainerRequestFilter { | ||
|
||
private final Pattern clientPattern = Pattern.compile("acme.*"); | ||
|
||
@Override | ||
public void filter(ContainerRequestContext requestContext) throws IOException { | ||
AccessToken accessToken = KeycloakSessionLookup.currentSession().getAttribute(ACCESS_TOKEN_SESSION_KEY, AccessToken.class); | ||
|
||
if (accessToken == null) { | ||
log.error("AzpFilter must run after TokenFilter"); | ||
throw new InternalServerErrorException(); | ||
} | ||
|
||
if (!clientPattern.matcher(accessToken.getIssuedFor()).matches()) { | ||
throw new ForbiddenException("This resource is only accessible for acme clients"); | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
...ain/java/com/github/thomasdarimont/keycloak/custom/endpoints/filter/AzpFilterBinding.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.github.thomasdarimont.keycloak.custom.endpoints.filter; | ||
|
||
import jakarta.ws.rs.NameBinding; | ||
|
||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
|
||
@NameBinding | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface AzpFilterBinding { | ||
} |
58 changes: 58 additions & 0 deletions
58
...in/java/com/github/thomasdarimont/keycloak/custom/endpoints/filter/ProtectedResource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.github.thomasdarimont.keycloak.custom.endpoints.filter; | ||
|
||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
import jakarta.ws.rs.Produces; | ||
import jakarta.ws.rs.core.Response; | ||
import org.keycloak.models.KeycloakSession; | ||
|
||
import java.util.Map; | ||
|
||
import static com.github.thomasdarimont.keycloak.custom.endpoints.filter.TokenFilter.ACCESS_TOKEN_SESSION_KEY; | ||
|
||
/** | ||
* Example for a resource protected by a request filter. | ||
* Methods annotated with @AuthFilterBinding execute the filter before executing the method | ||
*/ | ||
public class ProtectedResource { | ||
|
||
private final KeycloakSession keycloakSession; | ||
|
||
public ProtectedResource(KeycloakSession keycloakSession) { | ||
this.keycloakSession = keycloakSession; | ||
} | ||
|
||
/* | ||
* Run a token check | ||
*/ | ||
@GET | ||
@Path("/protected") | ||
@TokenFilterBinding | ||
@Produces("application/json") | ||
public Response protectedResource() { | ||
return Response.ok(Map.of("secret", keycloakSession.getAttribute(ACCESS_TOKEN_SESSION_KEY))).build(); | ||
} | ||
|
||
/* | ||
* Run a token check AND an azp check | ||
*/ | ||
@GET | ||
@Path("/veryprotected") | ||
@AzpFilterBinding | ||
@TokenFilterBinding | ||
@Produces("application/json") | ||
public Response veryProtectedResource() { | ||
return Response.ok(Map.of("supersecret", "The magic words are squeamish ossifrage", | ||
"secret", keycloakSession.getAttribute(ACCESS_TOKEN_SESSION_KEY))).build(); | ||
} | ||
|
||
/* | ||
* Run no checks at all | ||
*/ | ||
@GET | ||
@Path("/public") | ||
@Produces("application/json") | ||
public Response openResource() { | ||
return Response.ok(Map.of("secret", "no secrets here")).build(); | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
...src/main/java/com/github/thomasdarimont/keycloak/custom/endpoints/filter/TokenFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.github.thomasdarimont.keycloak.custom.endpoints.filter; | ||
|
||
import com.github.thomasdarimont.keycloak.custom.support.KeycloakSessionLookup; | ||
import jakarta.annotation.Priority; | ||
import jakarta.ws.rs.NotAuthorizedException; | ||
import jakarta.ws.rs.container.ContainerRequestContext; | ||
import jakarta.ws.rs.container.ContainerRequestFilter; | ||
import jakarta.ws.rs.ext.Provider; | ||
import org.keycloak.authorization.util.Tokens; | ||
import org.keycloak.models.KeycloakSession; | ||
import org.keycloak.representations.AccessToken; | ||
|
||
import java.io.IOException; | ||
|
||
/** | ||
* Custom request filter for token handling. Executed if a request method is annotated with @AuthFilterBinding | ||
*/ | ||
@Priority(0) | ||
@Provider | ||
@TokenFilterBinding | ||
public class TokenFilter implements ContainerRequestFilter { | ||
|
||
public static final String ACCESS_TOKEN_SESSION_KEY = "acme-access-token"; | ||
|
||
@Override | ||
public void filter(ContainerRequestContext requestContext) throws IOException { | ||
// Important: The filter is instantiated only once. KeycloakSession must be local, not a field | ||
KeycloakSession keycloakSession = KeycloakSessionLookup.currentSession(); | ||
|
||
AccessToken accessToken = Tokens.getAccessToken(keycloakSession); | ||
|
||
if (accessToken == null) { | ||
throw new NotAuthorizedException("Invalid or missing token"); | ||
} | ||
|
||
/* | ||
* Store the access token in the keycloak session, so that it can be used by the actual | ||
* resource without having to repeat the validation step | ||
*/ | ||
keycloakSession.setAttribute(ACCESS_TOKEN_SESSION_KEY, accessToken); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
...n/java/com/github/thomasdarimont/keycloak/custom/endpoints/filter/TokenFilterBinding.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.github.thomasdarimont.keycloak.custom.endpoints.filter; | ||
|
||
import jakarta.ws.rs.NameBinding; | ||
|
||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
|
||
@NameBinding | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface TokenFilterBinding { | ||
} |