Skip to content

Commit

Permalink
Merge pull request #27 from tidepool-org/BACK-2916-separate-personal-…
Browse files Browse the repository at this point in the history
…and-clinical-registration

[BACK-2916/BACK-2913] Separate personal and clinical registration
  • Loading branch information
darinkrauss authored Apr 23, 2024
2 parents c631113 + 553f93b commit 05d09c1
Show file tree
Hide file tree
Showing 35 changed files with 1,002 additions and 60 deletions.
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ Dockerfile

.git/
.gitignore

# Environment
.env
.envrc
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@
target/

# Original theme
themes/base
themes/base

# Environment
.env
.envrc
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM maven:3.8.4-jdk-11 as build
FROM maven:3.8.6-jdk-11 as build

COPY . /build
WORKDIR /build
Expand All @@ -7,11 +7,11 @@ RUN unset MAVEN_CONFIG && \
./mvnw versions:set -DnewVersion=LATEST && \
./mvnw install && \
./mvnw clean compile package && \
wget -O keycloak-rest-provider.jar https://github.com/toddkazakov/keycloak-user-migration/releases/download/v2.0/keycloak-rest-provider.jar && \
wget -O keycloak-rest-provider.jar https://github.com/daniel-frak/keycloak-user-migration/releases/download/1.0.0/keycloak-rest-provider-1.0.0.jar && \
wget -O keycloak-metrics-spi.jar https://github.com/aerogear/keycloak-metrics-spi/releases/download/3.0.0/keycloak-metrics-spi-3.0.0.jar && \
wget -O keycloak-home-idp-discovery.jar https://github.com/toddkazakov/keycloak-home-idp-discovery/releases/download/v21.3.2/keycloak-home-idp-discovery.jar
wget -O keycloak-home-idp-discovery.jar https://github.com/tidepool-org/keycloak-home-idp-discovery/releases/download/v21.4.0/keycloak-home-idp-discovery.jar

FROM alpine:3.15 as release
FROM alpine:latest as release

COPY --from=build /build/admin/target/*.jar /release/extensions/
COPY --from=build /build/*.jar /release/extensions/
Expand Down
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,4 @@ build-artifacts:

# Builds the docker image
build: build-artifacts
docker build -t tidepool/keycloak-extensions:$(image_tag) .


docker build --platform linux/amd64 --tag tidepool/keycloak-extensions:$(image_tag) .
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ public final class ConditionUserInContextFactory implements AuthenticatorFactory

public static final String CONF_NEGATE = "negate";

private Config.Scope config;

@Override
public String getDisplayType() {
return "Condition - User in Context";
Expand Down Expand Up @@ -72,7 +70,6 @@ public Authenticator create(KeycloakSession session) {

@Override
public void init(Config.Scope config) {
this.config = config;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.tidepool.keycloak.extensions.authenticator;

import org.jboss.logging.Logger;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.Authenticator;
import org.keycloak.models.*;
Expand All @@ -9,30 +8,25 @@

import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;

final class RedirectToRegistrationPage implements Authenticator {

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

RedirectToRegistrationPage() {
}

@Override
public void authenticate(AuthenticationFlowContext context) {
URI baseURI = prepareBaseUriBuilder(context).build();
URI baseURI = prepareBaseUriBuilder(context).build();
URI register = Urls.realmRegisterPage(baseURI, context.getRealm().getName());
context.forceChallenge(Response.seeOther(register).build());
}

private UriBuilder prepareBaseUriBuilder(AuthenticationFlowContext context) {
UriInfo uriInfo = context.getUriInfo();
UriBuilder uriBuilder = context.getUriInfo().getBaseUriBuilder();
ClientModel client = context.getSession().getContext().getClient();
AuthenticationSessionModel authSession = context.getAuthenticationSession();

String requestURI = uriInfo.getBaseUri().getPath();
UriBuilder uriBuilder = UriBuilder.fromUri(requestURI);
uriBuilder.replaceQuery(null);

if (client != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ public final class RedirectToRegistrationPageFactory implements AuthenticatorFac

private static final String PROVIDER_ID = "redirect-to-registration-page";

private Config.Scope config;

@Override
public String getDisplayType() {
return "Redirect to Registration Page";
Expand Down Expand Up @@ -65,7 +63,6 @@ public Authenticator create(KeycloakSession session) {

@Override
public void init(Config.Scope config) {
this.config = config;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.tidepool.keycloak.extensions.authenticator;

import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.Authenticator;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.AuthorizationEndpointBase;
import org.tidepool.keycloak.extensions.model.RoleBean;
import org.tidepool.keycloak.extensions.roles.UserRolePromptRequiredAction;

final class RegistrationRoleDiscoveryAuthenticator implements Authenticator {

@Override
public void close() {
}

@Override
public void authenticate(AuthenticationFlowContext context) {

// Reset APP_INITIATED_FLOW so restart redirects to login, not registration
context.getAuthenticationSession().setClientNote(AuthorizationEndpointBase.APP_INITIATED_FLOW, null);

String role = context.getUriInfo().getQueryParameters().getFirst(RoleBean.PARAMETER_ROLE);
if (RoleBean.ROLES_SET.contains(role)) {
context.getAuthenticationSession().setAuthNote(RoleBean.AUTH_NOTE_ROLE, role);
context.success();
} else {
context.challenge(context.form().createForm(UserRolePromptRequiredAction.ROLES_FORM_FTL));
}
}

@Override
public void action(AuthenticationFlowContext context) {
String role = context.getHttpRequest().getDecodedFormParameters().getFirst(RoleBean.PARAMETER_ROLE);
if (RoleBean.ROLES_SET.contains(role)) {
context.getAuthenticationSession().setAuthNote(RoleBean.AUTH_NOTE_ROLE, role);
context.success();
} else {
context.failure(AuthenticationFlowError.INTERNAL_ERROR);
}
}

@Override
public boolean requiresUser() {
return false;
}

@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}

@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.tidepool.keycloak.extensions.authenticator;

import java.util.List;
import java.util.Map;

import org.keycloak.Config.Scope;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel.Requirement;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ServerInfoAwareProviderFactory;

public final class RegistrationRoleDiscoveryAuthenticatorFactory
implements AuthenticatorFactory, ServerInfoAwareProviderFactory {

private static final String ID = "tidepool-registration-role-discovery";

private static final Requirement[] REQUIREMENT_CHOICES = { Requirement.REQUIRED, Requirement.DISABLED };

@Override
public Authenticator create(KeycloakSession session) {
return new RegistrationRoleDiscoveryAuthenticator();
}

@Override
public void init(Scope config) {
}

@Override
public void postInit(KeycloakSessionFactory factory) {
}

@Override
public void close() {
}

@Override
public String getId() {
return ID;
}

@Override
public String getDisplayType() {
return "Tidepool Registration Role Discovery";
}

@Override
public String getReferenceCategory() {
return "registration-role-discovery";
}

@Override
public boolean isConfigurable() {
return false;
}

@Override
public Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}

@Override
public boolean isUserSetupAllowed() {
return false;
}

@Override
public String getHelpText() {
return "Ensures a role is provided upon registration via query parameter or displayed form and sets the associated auth note";
}

@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}

@Override
public Map<String, String> getOperationalInfo() {
String version = getClass().getPackage().getImplementationVersion();
if (version == null) {
version = "dev-snapshot";
}
return Map.of("Version", version);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.tidepool.keycloak.extensions.authenticator;

import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormContext;
import org.keycloak.authentication.ValidationContext;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.tidepool.keycloak.extensions.model.RoleBean;

final class RegistrationRoleFormAction implements FormAction {

private static final String EVENT_DETAIL_ROLE = "role";

@Override
public void close() {
}

@Override
public void buildPage(FormContext context, LoginFormsProvider form) {
}

@Override
public void validate(ValidationContext context) {
String role = context.getAuthenticationSession().getAuthNote(RoleBean.AUTH_NOTE_ROLE);
if (RoleBean.ROLES_SET.contains(role)) {
context.getEvent().detail(EVENT_DETAIL_ROLE, role);
}

context.success();
}

@Override
public void success(FormContext context) {
String role = context.getAuthenticationSession().getAuthNote(RoleBean.AUTH_NOTE_ROLE);
if (RoleBean.ROLES_SET.contains(role)) {
RoleModel roleModel = context.getRealm().getRole(role);
if (roleModel != null) {
context.getUser().grantRole(roleModel);
}
}
}

@Override
public boolean requiresUser() {
return false;
}

@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}

@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
}
}
Loading

0 comments on commit 05d09c1

Please sign in to comment.