-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Draft to support access restrictions in credential reset flow
see #121
- Loading branch information
1 parent
37a375b
commit fce43f6
Showing
10 changed files
with
258 additions
and
36 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
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
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
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
48 changes: 48 additions & 0 deletions
48
src/main/java/de/sventorben/keycloak/authorization/client/access/AccessProviderResolver.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,48 @@ | ||
package de.sventorben.keycloak.authorization.client.access; | ||
|
||
import de.sventorben.keycloak.authorization.client.RestrictClientAuthConfig; | ||
import de.sventorben.keycloak.authorization.client.access.role.ClientRoleBasedAccessProviderFactory; | ||
import org.jboss.logging.Logger; | ||
import org.keycloak.authentication.AuthenticationFlowContext; | ||
|
||
public final class AccessProviderResolver { | ||
|
||
private static final Logger LOG = Logger.getLogger(AccessProviderResolver.class); | ||
|
||
private final RestrictClientAuthConfig config; | ||
|
||
public AccessProviderResolver(RestrictClientAuthConfig config) { | ||
this.config = config; | ||
} | ||
|
||
public AccessProvider resolve(AuthenticationFlowContext context) { | ||
final String accessProviderId = config.getAccessProviderId(); | ||
|
||
if (accessProviderId != null) { | ||
AccessProvider accessProvider = context.getSession().getProvider(AccessProvider.class, accessProviderId); | ||
if (accessProvider == null) { | ||
LOG.warnf( | ||
"Configured access provider '%s' in authenticator config '%s' does not exist.", | ||
accessProviderId, config.getAuthenticatorConfigAlias()); | ||
} else { | ||
LOG.tracef( | ||
"Using access provider '%s' in authenticator config '%s'.", | ||
accessProviderId, config.getAuthenticatorConfigAlias()); | ||
return accessProvider; | ||
} | ||
} | ||
|
||
final AccessProvider defaultProvider = context.getSession().getProvider(AccessProvider.class); | ||
if (defaultProvider != null) { | ||
LOG.debugf( | ||
"No access provider is configured in authenticator config '%s'. Using server-wide default provider '%s'", | ||
config.getAuthenticatorConfigAlias(), defaultProvider); | ||
return defaultProvider; | ||
} | ||
|
||
LOG.infof( | ||
"Neither an access provider is configured in authenticator config '%s' nor has a server-wide default provider been set. Using '%s' as a fallback.", | ||
config.getAuthenticatorConfigAlias(), ClientRoleBasedAccessProviderFactory.PROVIDER_ID); | ||
return context.getSession().getProvider(AccessProvider.class, ClientRoleBasedAccessProviderFactory.PROVIDER_ID); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
...ventorben/keycloak/authorization/client/requiredactions/RestrictAccessRequiredAction.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,43 @@ | ||
package de.sventorben.keycloak.authorization.client.requiredactions; | ||
|
||
import org.keycloak.authentication.InitiatedActionSupport; | ||
import org.keycloak.authentication.RequiredActionContext; | ||
import org.keycloak.authentication.RequiredActionProvider; | ||
import org.keycloak.services.messages.Messages; | ||
import org.keycloak.sessions.AuthenticationSessionModel; | ||
|
||
import javax.ws.rs.core.Response; | ||
|
||
public class RestrictAccessRequiredAction implements RequiredActionProvider { | ||
|
||
@Override | ||
public InitiatedActionSupport initiatedActionSupport() { | ||
return InitiatedActionSupport.SUPPORTED; | ||
} | ||
|
||
@Override | ||
public void evaluateTriggers(RequiredActionContext requiredActionContext) { | ||
} | ||
|
||
@Override | ||
public void requiredActionChallenge(RequiredActionContext requiredActionContext) { | ||
requiredActionContext.challenge(htmlErrorResponse(requiredActionContext)); | ||
} | ||
|
||
@Override | ||
public void processAction(RequiredActionContext requiredActionContext) { | ||
} | ||
|
||
private Response htmlErrorResponse(RequiredActionContext context) { | ||
AuthenticationSessionModel authSession = context.getAuthenticationSession(); | ||
return context.form() | ||
.setError(Messages.ACCESS_DENIED, authSession.getAuthenticatedUser().getUsername(), | ||
authSession.getClient().getClientId()) | ||
.createErrorPage(Response.Status.FORBIDDEN); | ||
} | ||
|
||
@Override | ||
public void close() { | ||
|
||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
...en/keycloak/authorization/client/requiredactions/RestrictAccessRequiredActionFactory.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,56 @@ | ||
package de.sventorben.keycloak.authorization.client.requiredactions; | ||
|
||
import de.sventorben.keycloak.authorization.client.common.OperationalInfo; | ||
import org.keycloak.Config; | ||
import org.keycloak.authentication.InitiatedActionSupport; | ||
import org.keycloak.authentication.RequiredActionContext; | ||
import org.keycloak.authentication.RequiredActionFactory; | ||
import org.keycloak.authentication.RequiredActionProvider; | ||
import org.keycloak.models.KeycloakSession; | ||
import org.keycloak.models.KeycloakSessionFactory; | ||
import org.keycloak.provider.ServerInfoAwareProviderFactory; | ||
import org.keycloak.services.messages.Messages; | ||
import org.keycloak.sessions.AuthenticationSessionModel; | ||
|
||
import javax.ws.rs.core.Response; | ||
import java.util.Map; | ||
|
||
public class RestrictAccessRequiredActionFactory implements RequiredActionFactory, ServerInfoAwareProviderFactory { | ||
|
||
static final String PROVIDER_ID = "RESTRICT_CLIENT_AUTH_DENY_ACCESS"; | ||
|
||
@Override | ||
public String getDisplayText() { | ||
return "Restrict user authentication on clients"; | ||
} | ||
|
||
@Override | ||
public RequiredActionProvider create(KeycloakSession keycloakSession) { | ||
return new RestrictAccessRequiredAction(); | ||
} | ||
|
||
@Override | ||
public void init(Config.Scope scope) { | ||
|
||
} | ||
|
||
@Override | ||
public void postInit(KeycloakSessionFactory keycloakSessionFactory) { | ||
|
||
} | ||
|
||
@Override | ||
public void close() { | ||
|
||
} | ||
|
||
@Override | ||
public String getId() { | ||
return PROVIDER_ID; | ||
} | ||
|
||
@Override | ||
public Map<String, String> getOperationalInfo() { | ||
return OperationalInfo.get(); | ||
} | ||
} |
98 changes: 98 additions & 0 deletions
98
...k/authorization/client/requiredactions/RestrictClientAuthRequiredActionAuthenticator.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,98 @@ | ||
package de.sventorben.keycloak.authorization.client.requiredactions; | ||
|
||
import de.sventorben.keycloak.authorization.client.RestrictClientAuthConfig; | ||
import de.sventorben.keycloak.authorization.client.access.AccessProvider; | ||
import de.sventorben.keycloak.authorization.client.access.AccessProviderResolver; | ||
import de.sventorben.keycloak.authorization.client.common.OperationalInfo; | ||
import org.jboss.logging.Logger; | ||
import org.keycloak.authentication.AuthenticationFlowContext; | ||
import org.keycloak.authentication.AuthenticationFlowError; | ||
import org.keycloak.authentication.Authenticator; | ||
import org.keycloak.authentication.authenticators.resetcred.AbstractSetRequiredActionAuthenticator; | ||
import org.keycloak.events.Errors; | ||
import org.keycloak.models.ClientModel; | ||
import org.keycloak.models.KeycloakSession; | ||
import org.keycloak.models.RealmModel; | ||
import org.keycloak.models.UserModel; | ||
import org.keycloak.models.credential.PasswordCredentialModel; | ||
import org.keycloak.provider.ServerInfoAwareProviderFactory; | ||
import org.keycloak.representations.idm.OAuth2ErrorRepresentation; | ||
import org.keycloak.services.messages.Messages; | ||
import org.keycloak.sessions.AuthenticationSessionModel; | ||
import org.keycloak.utils.MediaTypeMatcher; | ||
|
||
import javax.ws.rs.core.MediaType; | ||
import javax.ws.rs.core.Response; | ||
import java.util.Map; | ||
|
||
public final class RestrictClientAuthRequiredActionAuthenticator extends AbstractSetRequiredActionAuthenticator implements ServerInfoAwareProviderFactory { | ||
|
||
private static final Logger LOG = Logger.getLogger(RestrictClientAuthRequiredActionAuthenticator.class); | ||
|
||
private static final String PROVIDER_ID = "restrict-client-auth-action-auth"; | ||
|
||
public RestrictClientAuthRequiredActionAuthenticator() {} | ||
|
||
@Override | ||
public void authenticate(final AuthenticationFlowContext context) { | ||
|
||
if (context.getExecution().isRequired()) { | ||
|
||
final ClientModel client = context.getSession().getContext().getClient(); | ||
final RestrictClientAuthConfig config = new RestrictClientAuthConfig(context.getAuthenticatorConfig()); | ||
|
||
final AccessProvider access = new AccessProviderResolver(config).resolve(context); | ||
|
||
final UserModel user = context.getUser(); | ||
if (access.isRestricted(client) && !access.isPermitted(client, user)) { | ||
context.getAuthenticationSession().addRequiredAction(RestrictAccessRequiredActionFactory.PROVIDER_ID); | ||
} | ||
} | ||
|
||
context.success(); | ||
} | ||
|
||
@Override | ||
public void action(AuthenticationFlowContext context) { | ||
LOG.warn("Action called!"); | ||
context.failure(AuthenticationFlowError.ACCESS_DENIED); | ||
} | ||
|
||
@Override | ||
public boolean requiresUser() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) { | ||
} | ||
|
||
@Override | ||
public void close() { | ||
} | ||
|
||
@Override | ||
public String getId() { | ||
return PROVIDER_ID; | ||
} | ||
|
||
@Override | ||
public Map<String, String> getOperationalInfo() { | ||
return OperationalInfo.get(); | ||
} | ||
|
||
@Override | ||
public String getDisplayType() { | ||
return "Restrict user authentication on clients (via required action)"; | ||
} | ||
|
||
@Override | ||
public String getHelpText() { | ||
return "Restricts user authentication on clients based on an access provider. Should be used in reset credentials flow. Access will be denied during required action evaluation."; | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
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 |
---|---|---|
@@ -1 +1,2 @@ | ||
de.sventorben.keycloak.authorization.client.RestrictClientAuthAuthenticatorFactory | ||
de.sventorben.keycloak.authorization.client.requiredactions.RestrictClientAuthRequiredActionAuthenticator |
1 change: 1 addition & 0 deletions
1
src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory
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 @@ | ||
de.sventorben.keycloak.authorization.client.requiredactions.RestrictAccessRequiredActionFactory |