diff --git a/universal-application-tool-0.0.1/app/auth/AdminAuthClient.java b/universal-application-tool-0.0.1/app/auth/AdminAuthClient.java
new file mode 100644
index 0000000000..723dedfcab
--- /dev/null
+++ b/universal-application-tool-0.0.1/app/auth/AdminAuthClient.java
@@ -0,0 +1,13 @@
+package auth;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * AdminAuthClient is the annotation for the auth client responsible for admin authentication. This
+ * client must implement {@link org.pac4j.core.client.IndirectClient}.
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AdminAuthClient {}
diff --git a/universal-application-tool-0.0.1/app/auth/ApplicantAuthClient.java b/universal-application-tool-0.0.1/app/auth/ApplicantAuthClient.java
new file mode 100644
index 0000000000..2c49076c98
--- /dev/null
+++ b/universal-application-tool-0.0.1/app/auth/ApplicantAuthClient.java
@@ -0,0 +1,14 @@
+package auth;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * ApplicantAuthClient is the annotation for the auth client responsible for applicant
+ * authentication. This client must implement IndirectClient -> {@link
+ * org.pac4j.core.client.IndirectClient}.
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ApplicantAuthClient {}
diff --git a/universal-application-tool-0.0.1/app/auth/oidc/AdOidcClient.java b/universal-application-tool-0.0.1/app/auth/oidc/AdOidcClient.java
deleted file mode 100644
index baf43a0655..0000000000
--- a/universal-application-tool-0.0.1/app/auth/oidc/AdOidcClient.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package auth.oidc;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import javax.inject.Qualifier;
-
-/**
- * AdOidcClient is an annotation for AD-flavored OidcClient.
- *
- *
See {@link modules.SecurityModule#provideAdClient}.
- */
-@Qualifier
-@Retention(RetentionPolicy.RUNTIME)
-public @interface AdOidcClient {}
diff --git a/universal-application-tool-0.0.1/app/auth/oidc/AdOidcProvider.java b/universal-application-tool-0.0.1/app/auth/oidc/AdOidcProvider.java
new file mode 100644
index 0000000000..fdaf1012fb
--- /dev/null
+++ b/universal-application-tool-0.0.1/app/auth/oidc/AdOidcProvider.java
@@ -0,0 +1,91 @@
+package auth.oidc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import auth.ProfileFactory;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.typesafe.config.Config;
+import java.util.ArrayList;
+import java.util.Collections;
+import org.pac4j.core.http.callback.PathParameterCallbackUrlResolver;
+import org.pac4j.oidc.client.OidcClient;
+import org.pac4j.oidc.config.OidcConfiguration;
+import repository.UserRepository;
+
+/** Provider class for the AD OIDC Client. */
+public class AdOidcProvider implements Provider {
+
+ private final Config configuration;
+ private final String baseUrl;
+ private final ProfileFactory profileFactory;
+ private final Provider applicantRepositoryProvider;
+
+ @Inject
+ public AdOidcProvider(
+ Config configuration,
+ ProfileFactory profileFactory,
+ Provider applicantRepositoryProvider) {
+ this.configuration = checkNotNull(configuration);
+ this.baseUrl = configuration.getString("base_url");
+ this.profileFactory = profileFactory;
+ this.applicantRepositoryProvider = applicantRepositoryProvider;
+ }
+
+ @Override
+ public OidcClient get() {
+ if (!configuration.hasPath("adfs.client_id") || !configuration.hasPath("adfs.secret")) {
+ return null;
+ }
+ OidcConfiguration config = new OidcConfiguration();
+ // Resource identifier that tells AD that this is civiform from the portal.
+ config.setClientId(configuration.getString("adfs.client_id"));
+
+ // The token that we created within AD and use to sign our requests.
+ config.setSecret(configuration.getString("adfs.secret"));
+
+ // Endpoint that app can use to get the public keys from.
+ config.setDiscoveryURI(configuration.getString("adfs.discovery_uri"));
+
+ // Tells AD to use a post response when it sends info back from
+ // the auth request.
+ config.setResponseMode("form_post");
+
+ // Tells AD to give us an id token back from this request.
+ config.setResponseType("id_token");
+
+ // Scopes are the other things that we want from the AD endpoint
+ // (needs to also be configured on AD side).
+ // Note: ADFS has the extra claim: allatclaims which returns
+ // access token in the id_token.
+ String[] defaultScopes = {"openid", "profile", "email"};
+ String[] extraScopes = configuration.getString("adfs.additional_scopes").split(" ");
+ ArrayList allClaims = new ArrayList<>();
+ Collections.addAll(allClaims, defaultScopes);
+ Collections.addAll(allClaims, extraScopes);
+ config.setScope(String.join(" ", allClaims));
+
+ // Security setting that adds a random number to ensure cannot be reused.
+ config.setUseNonce(true);
+
+ // Don't have custom state data.
+ config.setWithState(false);
+
+ OidcClient client = new OidcClient(config);
+ client.setName("AdClient");
+
+ // Telling AD where to send people back to. This gets
+ // combined with the name to create the url.
+ client.setCallbackUrl(baseUrl + "/callback");
+
+ // This is specific to the implemention using pac4j. pac4j has concept
+ // of a profile for different identity profiles we have different creators.
+ // This is what links the user to the stuff they have access to.
+ client.setProfileCreator(
+ new AdfsProfileAdapter(
+ config, client, profileFactory, configuration, applicantRepositoryProvider));
+ client.setCallbackUrlResolver(new PathParameterCallbackUrlResolver());
+ client.init();
+ return client;
+ }
+}
diff --git a/universal-application-tool-0.0.1/app/auth/oidc/IdcsOidcClient.java b/universal-application-tool-0.0.1/app/auth/oidc/IdcsOidcClient.java
deleted file mode 100644
index c9d5b87593..0000000000
--- a/universal-application-tool-0.0.1/app/auth/oidc/IdcsOidcClient.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package auth.oidc;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import javax.inject.Qualifier;
-
-/**
- * IdcsOidcClient is an annotation for IDCS-flavored OidcClient.
- *
- * See {@link modules.SecurityModule#provideIDCSClient}.
- */
-@Qualifier
-@Retention(RetentionPolicy.RUNTIME)
-public @interface IdcsOidcClient {}
diff --git a/universal-application-tool-0.0.1/app/auth/oidc/IdcsOidcProvider.java b/universal-application-tool-0.0.1/app/auth/oidc/IdcsOidcProvider.java
new file mode 100644
index 0000000000..4f07dd8481
--- /dev/null
+++ b/universal-application-tool-0.0.1/app/auth/oidc/IdcsOidcProvider.java
@@ -0,0 +1,59 @@
+package auth.oidc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import auth.ProfileFactory;
+import com.google.inject.Inject;
+import com.typesafe.config.Config;
+import javax.inject.Provider;
+import org.pac4j.core.http.callback.PathParameterCallbackUrlResolver;
+import org.pac4j.oidc.client.OidcClient;
+import org.pac4j.oidc.config.OidcConfiguration;
+import repository.UserRepository;
+
+public class IdcsOidcProvider implements Provider {
+
+ private final Config configuration;
+ private final String baseUrl;
+ private final ProfileFactory profileFactory;
+ private final Provider applicantRepositoryProvider;
+
+ @Inject
+ public IdcsOidcProvider(
+ Config configuration,
+ ProfileFactory profileFactory,
+ Provider applicantRepositoryProvider) {
+ this.configuration = checkNotNull(configuration);
+ this.baseUrl = configuration.getString("base_url");
+ this.profileFactory = checkNotNull(profileFactory);
+ this.applicantRepositoryProvider = applicantRepositoryProvider;
+ }
+
+ @Override
+ public OidcClient get() {
+ if (!configuration.hasPath("idcs.client_id") || !configuration.hasPath("idcs.secret")) {
+ return null;
+ }
+ OidcConfiguration config = new OidcConfiguration();
+ config.setClientId(configuration.getString("idcs.client_id"));
+ config.setSecret(configuration.getString("idcs.secret"));
+ config.setDiscoveryURI(configuration.getString("idcs.discovery_uri"));
+ config.setResponseMode("form_post");
+ // Our local fake IDCS doesn't support 'token' auth.
+ if (baseUrl.contains("localhost:")) {
+ config.setResponseType("id_token");
+ } else {
+ config.setResponseType("id_token token");
+ }
+ config.setUseNonce(true);
+ config.setWithState(false);
+ config.setScope("openid profile email");
+ OidcClient client = new OidcClient(config);
+ client.setCallbackUrl(baseUrl + "/callback");
+ client.setProfileCreator(
+ new IdcsProfileAdapter(config, client, profileFactory, applicantRepositoryProvider));
+ client.setCallbackUrlResolver(new PathParameterCallbackUrlResolver());
+ client.init();
+ return client;
+ }
+}
diff --git a/universal-application-tool-0.0.1/app/auth/saml/LoginRadiusSamlClient.java b/universal-application-tool-0.0.1/app/auth/saml/LoginRadiusSamlClient.java
deleted file mode 100644
index c3af2fa247..0000000000
--- a/universal-application-tool-0.0.1/app/auth/saml/LoginRadiusSamlClient.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package auth.saml;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import javax.inject.Qualifier;
-
-/**
- * LoginRadiusSamlClient is an annotation for SAML client customized for LoginRadius.
- *
- * See {@link modules.SecurityModule#provideLoginRadiusClient}.
- */
-@Qualifier
-@Retention(RetentionPolicy.RUNTIME)
-public @interface LoginRadiusSamlClient {}
diff --git a/universal-application-tool-0.0.1/app/auth/saml/LoginRadiusSamlProvider.java b/universal-application-tool-0.0.1/app/auth/saml/LoginRadiusSamlProvider.java
new file mode 100644
index 0000000000..f89f2e4d4d
--- /dev/null
+++ b/universal-application-tool-0.0.1/app/auth/saml/LoginRadiusSamlProvider.java
@@ -0,0 +1,88 @@
+package auth.saml;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import auth.ProfileFactory;
+import com.google.inject.Inject;
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigException;
+import java.util.IllegalFormatException;
+import java.util.Optional;
+import javax.inject.Provider;
+import org.pac4j.core.http.callback.PathParameterCallbackUrlResolver;
+import org.pac4j.saml.client.SAML2Client;
+import org.pac4j.saml.config.SAML2Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import repository.UserRepository;
+
+public class LoginRadiusSamlProvider implements Provider {
+
+ private static final Logger logger = LoggerFactory.getLogger(LoginRadiusSamlProvider.class);
+
+ private final Config configuration;
+ private final ProfileFactory profileFactory;
+ private final Provider applicantRepositoryProvider;
+ private final String baseUrl;
+
+ @Inject
+ public LoginRadiusSamlProvider(
+ Config configuration,
+ ProfileFactory profileFactory,
+ Provider applicantRepositoryProvider) {
+ this.configuration = checkNotNull(configuration);
+ this.profileFactory = checkNotNull(profileFactory);
+ this.baseUrl = configuration.getString("base_url");
+ this.applicantRepositoryProvider = checkNotNull(applicantRepositoryProvider);
+ }
+
+ @Override
+ public SAML2Client get() {
+ if (!configuration.hasPath("login_radius.keystore_password")
+ || !configuration.hasPath("login_radius.private_key_password")
+ || !configuration.hasPath("login_radius.api_key")) {
+ return null;
+ }
+
+ Optional metadataResourceUrlOpt = formatMetadataResourceUrl();
+
+ if (metadataResourceUrlOpt.isEmpty()) {
+ logger.warn("Invalid SAML metadata resource URL generated in LoginRadiusSamlProvider");
+ return null;
+ }
+
+ String metadataResourceUrl = metadataResourceUrlOpt.get();
+ SAML2Configuration config = new SAML2Configuration();
+ config.setKeystoreResourceFilepath(configuration.getString("login_radius.keystore_name"));
+ config.setKeystorePassword(configuration.getString("login_radius.keystore_password"));
+ config.setPrivateKeyPassword(configuration.getString("login_radius.private_key_password"));
+ config.setIdentityProviderMetadataResourceUrl(metadataResourceUrl);
+ SAML2Client client = new SAML2Client(config);
+
+ client.setProfileCreator(
+ new SamlCiviFormProfileAdapter(
+ config, client, profileFactory, applicantRepositoryProvider));
+
+ client.setCallbackUrlResolver(new PathParameterCallbackUrlResolver());
+ client.setCallbackUrl(baseUrl + "/callback");
+ client.init();
+ return client;
+ }
+
+ private Optional formatMetadataResourceUrl() {
+ try {
+ String metadataResourceUrl =
+ String.format(
+ "%s?apikey=%s&appName=%s",
+ configuration.getString("login_radius.metadata_uri"),
+ configuration.getString("login_radius.api_key"),
+ configuration.getString("login_radius.saml_app_name"));
+ return Optional.of(metadataResourceUrl);
+ } catch (IllegalFormatException
+ | NullPointerException
+ | ConfigException.Missing
+ | ConfigException.WrongType e) {
+ return Optional.empty();
+ }
+ }
+}
diff --git a/universal-application-tool-0.0.1/app/controllers/LegacyRoutesController.java b/universal-application-tool-0.0.1/app/controllers/LegacyRoutesController.java
new file mode 100644
index 0000000000..b39c7cc1e3
--- /dev/null
+++ b/universal-application-tool-0.0.1/app/controllers/LegacyRoutesController.java
@@ -0,0 +1,26 @@
+package controllers;
+
+import static play.mvc.Results.redirect;
+
+import java.util.Optional;
+import play.mvc.Http;
+import play.mvc.Result;
+
+/**
+ * Class for handling legacy routes after they have been removed between release cycles. All the
+ * routes in this class should eventually be deleted.
+ */
+public class LegacyRoutesController {
+
+ public Result idcsLoginWithRedirect(Http.Request request, Optional redirectTo) {
+ return redirect(routes.LoginController.applicantLogin(redirectTo));
+ }
+
+ public Result adfsLogin(Http.Request request) {
+ return redirect(routes.LoginController.adminLogin());
+ }
+
+ public Result loginRadiusLoginWithRedirect(Http.Request request, Optional redirectTo) {
+ return redirect(routes.LoginController.applicantLogin(redirectTo));
+ }
+}
diff --git a/universal-application-tool-0.0.1/app/controllers/LoginController.java b/universal-application-tool-0.0.1/app/controllers/LoginController.java
index ffbd72843a..de86bc2ea4 100644
--- a/universal-application-tool-0.0.1/app/controllers/LoginController.java
+++ b/universal-application-tool-0.0.1/app/controllers/LoginController.java
@@ -1,12 +1,10 @@
package controllers;
-import static com.google.common.base.Preconditions.checkNotNull;
import static controllers.CallbackController.REDIRECT_TO_SESSION_KEY;
+import auth.AdminAuthClient;
+import auth.ApplicantAuthClient;
import auth.AuthIdentityProviderName;
-import auth.oidc.AdOidcClient;
-import auth.oidc.IdcsOidcClient;
-import auth.saml.LoginRadiusSamlClient;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.typesafe.config.Config;
@@ -22,7 +20,6 @@
import org.pac4j.oidc.config.OidcConfiguration;
import org.pac4j.play.PlayWebContext;
import org.pac4j.play.http.PlayHttpActionAdapter;
-import org.pac4j.saml.client.SAML2Client;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;
@@ -33,11 +30,9 @@
*/
public class LoginController extends Controller {
- private final OidcClient idcsClient;
+ private final IndirectClient adminClient;
- private final OidcClient adClient;
-
- private final SAML2Client loginRadiusClient;
+ private final IndirectClient applicantClient;
private final SessionStore sessionStore;
@@ -45,56 +40,55 @@ public class LoginController extends Controller {
private final Config config;
- private final String applicantIdp;
-
@Inject
public LoginController(
- @AdOidcClient @Nullable OidcClient adClient,
- @IdcsOidcClient @Nullable OidcClient idcsClient,
- @LoginRadiusSamlClient @Nullable SAML2Client loginRadiusClient,
+ @AdminAuthClient @Nullable IndirectClient adminClient,
+ @ApplicantAuthClient @Nullable IndirectClient applicantClient,
SessionStore sessionStore,
Config config) {
- this.idcsClient = idcsClient;
- this.adClient = adClient;
- this.loginRadiusClient = loginRadiusClient;
+ this.adminClient = adminClient;
+ this.applicantClient = applicantClient;
this.sessionStore = Preconditions.checkNotNull(sessionStore);
- this.applicantIdp = checkNotNull(config).getString("auth.applicant_idp");
this.httpActionAdapter = PlayHttpActionAdapter.INSTANCE;
this.config = config;
}
- public Result loginWithRedirect(Http.Request request, Optional redirectTo) {
- if (applicantIdp.equals(AuthIdentityProviderName.LOGIN_RADIUS_APPLICANT.getString())) {
- return loginRadiusLoginWithRedirect(request, redirectTo);
- }
- return idcsLoginWithRedirect(request, redirectTo);
- }
-
- public Result idcsLogin(Http.Request request) {
- return login(request, idcsClient);
+ public Result adminLogin(Http.Request request) {
+ return login(request, adminClient);
}
- public Result idcsLoginWithRedirect(Http.Request request, Optional redirectTo) {
+ public Result applicantLogin(Http.Request request, Optional redirectTo) {
if (redirectTo.isEmpty()) {
- return idcsLogin(request);
+ return login(request, applicantClient);
}
- return login(request, idcsClient)
+ return login(request, applicantClient)
.addingToSession(request, REDIRECT_TO_SESSION_KEY, redirectTo.get());
}
- public Result loginRadiusLogin(Http.Request request) {
- return login(request, loginRadiusClient);
- }
+ public Result register(Http.Request request) {
+ String idp;
+ try {
+ idp = config.getString("auth.applicant_idp");
+ } catch (ConfigException.Missing e) {
+ // Default to IDCS.
+ idp = AuthIdentityProviderName.IDCS_APPLICANT.toString();
+ }
- public Result loginRadiusLoginWithRedirect(Http.Request request, Optional redirectTo) {
- if (redirectTo.isEmpty()) {
- return loginRadiusLogin(request);
+ boolean isIDCS = idp.equals(AuthIdentityProviderName.IDCS_APPLICANT.toString());
+
+ // Because this is only being called when we know IDCS is available, this route should
+ // technically
+ // never happen.
+ if (!isIDCS) {
+ return login(request, applicantClient);
}
- return login(request, loginRadiusClient)
- .addingToSession(request, REDIRECT_TO_SESSION_KEY, redirectTo.get());
+
+ return idcsRegister(request);
}
- public Result register(Http.Request request) {
+ // IDCS has specific register behavior that is different from other IDPs, which have the register
+ // option on the same screen as the login page.
+ private Result idcsRegister(Http.Request request) {
String registerUrl = null;
try {
registerUrl = config.getString("idcs.register_uri");
@@ -110,11 +104,7 @@ public Result register(Http.Request request) {
.addingToSession(
request,
REDIRECT_TO_SESSION_KEY,
- routes.LoginController.idcsLoginWithRedirect(Optional.empty()).url());
- }
-
- public Result adfsLogin(Http.Request request) {
- return login(request, adClient);
+ routes.LoginController.applicantLogin(Optional.empty()).url());
}
// Logic taken from org.pac4j.play.deadbolt2.Pac4jHandler.beforeAuthCheck.
diff --git a/universal-application-tool-0.0.1/app/modules/SecurityModule.java b/universal-application-tool-0.0.1/app/modules/SecurityModule.java
index 69b236f440..2c8816c5f3 100644
--- a/universal-application-tool-0.0.1/app/modules/SecurityModule.java
+++ b/universal-application-tool-0.0.1/app/modules/SecurityModule.java
@@ -4,50 +4,44 @@
import static play.mvc.Results.forbidden;
import static play.mvc.Results.redirect;
+import auth.AdminAuthClient;
+import auth.ApplicantAuthClient;
+import auth.AuthIdentityProviderName;
import auth.Authorizers;
import auth.CiviFormProfileData;
import auth.FakeAdminClient;
import auth.GuestClient;
import auth.ProfileFactory;
import auth.Roles;
-import auth.oidc.AdOidcClient;
-import auth.oidc.AdfsProfileAdapter;
-import auth.oidc.IdcsOidcClient;
-import auth.oidc.IdcsProfileAdapter;
-import auth.saml.LoginRadiusSamlClient;
-import auth.saml.SamlCiviFormProfileAdapter;
+import auth.oidc.AdOidcProvider;
+import auth.oidc.IdcsOidcProvider;
+import auth.saml.LoginRadiusSamlProvider;
import com.google.common.collect.ImmutableMap;
import com.google.inject.AbstractModule;
+import com.google.inject.ConfigurationException;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import controllers.routes;
import java.net.URI;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import javax.annotation.Nullable;
-import javax.inject.Provider;
import org.pac4j.core.authorization.authorizer.RequireAllRolesAuthorizer;
import org.pac4j.core.authorization.authorizer.RequireAnyRoleAuthorizer;
import org.pac4j.core.client.Client;
import org.pac4j.core.client.Clients;
+import org.pac4j.core.client.IndirectClient;
import org.pac4j.core.config.Config;
import org.pac4j.core.context.HttpConstants;
import org.pac4j.core.context.session.SessionStore;
-import org.pac4j.core.http.callback.PathParameterCallbackUrlResolver;
-import org.pac4j.oidc.client.OidcClient;
-import org.pac4j.oidc.config.OidcConfiguration;
import org.pac4j.play.CallbackController;
import org.pac4j.play.LogoutController;
import org.pac4j.play.http.PlayHttpActionAdapter;
import org.pac4j.play.store.PlayCookieSessionStore;
import org.pac4j.play.store.ShiroAesDataEncrypter;
-import org.pac4j.saml.client.SAML2Client;
-import org.pac4j.saml.config.SAML2Configuration;
import play.Environment;
-import repository.UserRepository;
/** SecurityModule configures and initializes all authentication and authorization classes. */
public class SecurityModule extends AbstractModule {
@@ -101,175 +95,71 @@ protected void configure() {
PlayCookieSessionStore sessionStore =
new PlayCookieSessionStore(new ShiroAesDataEncrypter(aesKey));
bind(SessionStore.class).toInstance(sessionStore);
- }
- @Provides
- @Singleton
- protected GuestClient guestClient(ProfileFactory profileFactory) {
- return new GuestClient(profileFactory);
+ String applicantAuthClient = "idcs";
+
+ try {
+ applicantAuthClient = configuration.getString("auth.applicant_idp");
+ } catch (ConfigurationException ignore) {
+ // Default to IDCS.
+ }
+
+ bindAdminIdpProvider();
+ bindApplicantIdpProvider(applicantAuthClient);
}
- @Provides
- @Singleton
- protected FakeAdminClient fakeAdminClient(ProfileFactory profileFactory) {
- return new FakeAdminClient(profileFactory, this.configuration);
+ private void bindAdminIdpProvider() {
+ // Currently the only supported admin auth provider. As we add other admin auth providers,
+ // this can be converted into a switch statement.
+ bind(IndirectClient.class)
+ .annotatedWith(AdminAuthClient.class)
+ .toProvider(AdOidcProvider.class);
}
- /** Creates a singleton object of OidcClient configured for IDCS and initializes it on startup. */
- @Provides
- @Nullable
- @Singleton
- @IdcsOidcClient
- protected OidcClient provideIDCSClient(
- ProfileFactory profileFactory, Provider applicantRepositoryProvider) {
- if (!this.configuration.hasPath("idcs.client_id")
- || !this.configuration.hasPath("idcs.secret")) {
- return null;
- }
- OidcConfiguration config = new OidcConfiguration();
- config.setClientId(this.configuration.getString("idcs.client_id"));
- config.setSecret(this.configuration.getString("idcs.secret"));
- config.setDiscoveryURI(this.configuration.getString("idcs.discovery_uri"));
- config.setResponseMode("form_post");
- // Our local fake IDCS doesn't support 'token' auth.
- if (baseUrl.contains("localhost:")) {
- config.setResponseType("id_token");
- } else {
- config.setResponseType("id_token token");
+ private void bindApplicantIdpProvider(String applicantIdpName) {
+ AuthIdentityProviderName idpName = AuthIdentityProviderName.forString(applicantIdpName).get();
+
+ switch (idpName) {
+ case LOGIN_RADIUS_APPLICANT:
+ bind(IndirectClient.class)
+ .annotatedWith(ApplicantAuthClient.class)
+ .toProvider(LoginRadiusSamlProvider.class);
+ break;
+ case IDCS_APPLICANT:
+ default:
+ bind(IndirectClient.class)
+ .annotatedWith(ApplicantAuthClient.class)
+ .toProvider(IdcsOidcProvider.class);
}
- config.setUseNonce(true);
- config.setWithState(false);
- config.setScope("openid profile email");
- OidcClient client = new OidcClient(config);
- client.setCallbackUrl(baseUrl + "/callback");
- client.setProfileCreator(
- new IdcsProfileAdapter(config, client, profileFactory, applicantRepositoryProvider));
- client.setCallbackUrlResolver(new PathParameterCallbackUrlResolver());
- client.init();
- return client;
}
- /**
- * Creates a singleton object of SAML2Client configured for LoginRadius and initializes it on
- * startup.
- */
@Provides
- @Nullable
@Singleton
- @LoginRadiusSamlClient
- protected SAML2Client provideLoginRadiusClient(
- ProfileFactory profileFactory, Provider applicantRepositoryProvider) {
- if (!this.configuration.hasPath("login_radius.keystore_password")
- || !this.configuration.hasPath("login_radius.private_key_password")
- || !this.configuration.hasPath("login_radius.api_key")) {
- return null;
- }
-
- String metadataResourceUrl =
- String.format(
- "%s?apikey=%s&appName=%s",
- this.configuration.getString("login_radius.metadata_uri"),
- this.configuration.getString("login_radius.api_key"),
- this.configuration.getString("login_radius.saml_app_name"));
- SAML2Configuration config = new SAML2Configuration();
- config.setKeystoreResourceFilepath(this.configuration.getString("login_radius.keystore_name"));
- config.setKeystorePassword(this.configuration.getString("login_radius.keystore_password"));
- config.setPrivateKeyPassword(this.configuration.getString("login_radius.private_key_password"));
- config.setIdentityProviderMetadataResourceUrl(metadataResourceUrl);
- SAML2Client client = new SAML2Client(config);
-
- client.setProfileCreator(
- new SamlCiviFormProfileAdapter(
- config, client, profileFactory, applicantRepositoryProvider));
-
- client.setCallbackUrlResolver(new PathParameterCallbackUrlResolver());
- client.setCallbackUrl(baseUrl + "/callback");
- client.init();
- return client;
+ protected GuestClient guestClient(ProfileFactory profileFactory) {
+ return new GuestClient(profileFactory);
}
- /** Creates a singleton object of OidcClient configured for AD and initializes it on startup. */
@Provides
- @Nullable
@Singleton
- @AdOidcClient
- protected OidcClient provideAdClient(
- ProfileFactory profileFactory, Provider applicantRepositoryProvider) {
- if (!this.configuration.hasPath("adfs.client_id")
- || !this.configuration.hasPath("adfs.secret")) {
- return null;
- }
- OidcConfiguration config = new OidcConfiguration();
- // Resource identifier that tells AD that this is civiform from the portal.
- config.setClientId(this.configuration.getString("adfs.client_id"));
-
- // The token that we created within AD and use to sign our requests.
- config.setSecret(this.configuration.getString("adfs.secret"));
-
- // Endpoint that app can use to get the public keys from.
- config.setDiscoveryURI(this.configuration.getString("adfs.discovery_uri"));
-
- // Tells AD to use a post response when it sends info back from
- // the auth request.
- config.setResponseMode("form_post");
-
- // Tells AD to give us an id token back from this request.
- config.setResponseType("id_token");
-
- // Scopes are the other things that we want from the AD endpoint
- // (needs to also be configured on AD side).
- // Note: ADFS has the extra claim: allatclaims which returns
- // access token in the id_token.
- String[] defaultScopes = {"openid", "profile", "email"};
- String[] extraScopes = this.configuration.getString("adfs.additional_scopes").split(" ");
- ArrayList allClaims = new ArrayList<>();
- Collections.addAll(allClaims, defaultScopes);
- Collections.addAll(allClaims, extraScopes);
- config.setScope(String.join(" ", allClaims));
-
- // Security setting that adds a random number to ensure cannot be reused.
- config.setUseNonce(true);
-
- // Don't have custom state data.
- config.setWithState(false);
-
- OidcClient client = new OidcClient(config);
- client.setName("AdClient");
-
- // Telling AD where to send people back to. This gets
- // combined with the name to create the url.
- client.setCallbackUrl(baseUrl + "/callback");
-
- // This is specific to the implemention using pac4j. pac4j has concept
- // of a profile for different identity profiles we have different creators.
- // This is what links the user to the stuff they have access to.
- client.setProfileCreator(
- new AdfsProfileAdapter(
- config, client, profileFactory, this.configuration, applicantRepositoryProvider));
- client.setCallbackUrlResolver(new PathParameterCallbackUrlResolver());
- client.init();
- return client;
+ protected FakeAdminClient fakeAdminClient(ProfileFactory profileFactory) {
+ return new FakeAdminClient(profileFactory, this.configuration);
}
@Provides
@Singleton
protected Config provideConfig(
GuestClient guestClient,
- @AdOidcClient @Nullable OidcClient adClient,
- @IdcsOidcClient @Nullable OidcClient idcsClient,
- @LoginRadiusSamlClient @Nullable SAML2Client loginRadiusClient,
+ @ApplicantAuthClient @Nullable IndirectClient applicantAuthClient,
+ @AdminAuthClient @Nullable IndirectClient adminAuthClient,
FakeAdminClient fakeAdminClient) {
List clientList = new ArrayList<>();
- clientList.add(guestClient);
- if (idcsClient != null) {
- clientList.add(idcsClient);
+ if (applicantAuthClient != null) {
+ clientList.add(applicantAuthClient);
}
- if (loginRadiusClient != null) {
- clientList.add(loginRadiusClient);
- }
- if (adClient != null) {
- clientList.add(adClient);
+ if (adminAuthClient != null) {
+ clientList.add(adminAuthClient);
}
+ clientList.add(guestClient);
if (fakeAdminClient.canEnable(URI.create(baseUrl).getHost())) {
clientList.add(fakeAdminClient);
}
diff --git a/universal-application-tool-0.0.1/app/views/LoginForm.java b/universal-application-tool-0.0.1/app/views/LoginForm.java
index f0624266bd..4b8c438710 100644
--- a/universal-application-tool-0.0.1/app/views/LoginForm.java
+++ b/universal-application-tool-0.0.1/app/views/LoginForm.java
@@ -177,7 +177,7 @@ private ContainerTag debugContent() {
private Tag loginButton(Messages messages) {
String msg = messages.at(MessageKey.BUTTON_LOGIN.getKeyName());
return redirectButton(
- applicantIdp, msg, routes.LoginController.loginWithRedirect(Optional.empty()).url())
+ applicantIdp, msg, routes.LoginController.applicantLogin(Optional.empty()).url())
.withClasses(BaseStyles.LOGIN_REDIRECT_BUTTON);
}
@@ -197,7 +197,7 @@ private Tag guestButton(Messages messages) {
private Tag adminLink(Messages messages) {
String msg = messages.at(MessageKey.LINK_ADMIN_LOGIN.getKeyName());
return a(msg)
- .withHref(routes.LoginController.adfsLogin().url())
+ .withHref(routes.LoginController.adminLogin().url())
.withClasses(BaseStyles.ADMIN_LOGIN);
}
}
diff --git a/universal-application-tool-0.0.1/app/views/applicant/ApplicantUpsellCreateAccountView.java b/universal-application-tool-0.0.1/app/views/applicant/ApplicantUpsellCreateAccountView.java
index 895489e0dd..e206cf3b28 100644
--- a/universal-application-tool-0.0.1/app/views/applicant/ApplicantUpsellCreateAccountView.java
+++ b/universal-application-tool-0.0.1/app/views/applicant/ApplicantUpsellCreateAccountView.java
@@ -89,8 +89,7 @@ public Content render(
.with(
new LinkElement()
.setHref(
- routes.LoginController.idcsLoginWithRedirect(
- Optional.of(redirectTo))
+ routes.LoginController.applicantLogin(Optional.of(redirectTo))
.url())
.setText(
messages.at(MessageKey.LINK_CREATE_ACCOUNT_OR_SIGN_IN.getKeyName()))
diff --git a/universal-application-tool-0.0.1/conf/routes b/universal-application-tool-0.0.1/conf/routes
index 278cc3481a..eeaeff9372 100644
--- a/universal-application-tool-0.0.1/conf/routes
+++ b/universal-application-tool-0.0.1/conf/routes
@@ -139,11 +139,15 @@ POST /callback/:client_name controllers.CallbackController.callback(req
# Log into application
GET /loginForm controllers.HomeController.loginForm(request: Request, message: java.util.Optional[String])
-GET /idcsLogin controllers.LoginController.idcsLoginWithRedirect(request: Request, redirectTo: java.util.Optional[String])
+GET /adminLogin controllers.LoginController.adminLogin(request: Request)
+GET /applicantLogin controllers.LoginController.applicantLogin(request: Request, redirectTo: java.util.Optional[String])
GET /idcsRegister controllers.LoginController.register(request: Request)
-GET /adfsLogin controllers.LoginController.adfsLogin(request: Request)
-GET /loginRadiusLogin controllers.LoginController.loginRadiusLoginWithRedirect(request: Request, redirectTo: java.util.Optional[String])
-GET /applicantlogin controllers.LoginController.loginWithRedirect(request: Request, redirectTo: java.util.Optional[String])
+
+# Legacy login routes -- to be deleted
+# Log into application
+GET /idcsLogin controllers.LegacyRoutesController.idcsLoginWithRedirect(request: Request, redirectTo: java.util.Optional[String])
+GET /adfsLogin controllers.LegacyRoutesController.adfsLogin(request: Request)
+GET /loginRadiusLogin controllers.LegacyRoutesController.loginRadiusLoginWithRedirect(request: Request, redirectTo: java.util.Optional[String])
# Log out of application
GET /logout @org.pac4j.play.LogoutController.logout(request: Request)
diff --git a/universal-application-tool-0.0.1/test/app/SecurityBrowserTest.java b/universal-application-tool-0.0.1/test/app/SecurityBrowserTest.java
index b472657239..2a66f21421 100644
--- a/universal-application-tool-0.0.1/test/app/SecurityBrowserTest.java
+++ b/universal-application-tool-0.0.1/test/app/SecurityBrowserTest.java
@@ -33,7 +33,7 @@ public void getApplicantRepository() {
}
protected void loginWithSimulatedIdcs() {
- goTo(routes.LoginController.idcsLoginWithRedirect(Optional.empty()));
+ goTo(routes.LoginController.applicantLogin(Optional.empty()));
// If we are not cookied, enter a username and password.
if (browser.pageSource().contains("Enter any login")) {
browser.$("[name='login']").click();