diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/EfspServer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/EfspServer.java index 988b34c55..2586e7a4f 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/EfspServer.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/EfspServer.java @@ -14,6 +14,7 @@ import edu.suffolk.litlab.efsp.db.MessageSettingsDatabase; import edu.suffolk.litlab.efsp.db.UserDatabase; import edu.suffolk.litlab.efsp.docassemble.DocassembleToFilingInformationConverter; +import edu.suffolk.litlab.efsp.server.auth.AuthenticateRequestInterceptor; import edu.suffolk.litlab.efsp.server.auth.SecurityHub; import edu.suffolk.litlab.efsp.server.services.ApiUserSettingsService; import edu.suffolk.litlab.efsp.server.services.AuthenticationService; @@ -25,12 +26,12 @@ import edu.suffolk.litlab.efsp.server.setup.EfmRestCallbackInterface; import edu.suffolk.litlab.efsp.server.setup.truefiling.TrueFilingModuleSetup; import edu.suffolk.litlab.efsp.server.setup.tyler.TylerModuleSetup; -import edu.suffolk.litlab.efsp.server.utils.AuthenticateRequestInterceptor; import edu.suffolk.litlab.efsp.server.utils.EnumExceptionMapper; import edu.suffolk.litlab.efsp.server.utils.JsonExceptionMapper; import edu.suffolk.litlab.efsp.server.utils.ObservabilityHeadersInterceptor; import edu.suffolk.litlab.efsp.server.utils.ObservabilityResetInterceptor; import edu.suffolk.litlab.efsp.server.utils.OrgMessageSender; +import edu.suffolk.litlab.efsp.server.utils.ProxyServerExceptionMapper; import edu.suffolk.litlab.efsp.server.utils.SendMessage; import edu.suffolk.litlab.efsp.server.utils.ServiceHelpers; import edu.suffolk.litlab.efsp.server.utils.SoapExceptionMapper; @@ -136,7 +137,7 @@ protected EfspServer( Map.of( "xml", MediaType.APPLICATION_XML, "json", MediaType.APPLICATION_JSON)); - sf.setProviders(providers(ldSupplier)); + sf.setProviders(providers(ldSupplier, security)); sf.setAddress(ServiceHelpers.BASE_LOCAL_URL); server = sf.create(); @@ -196,7 +197,7 @@ private static void setupDatabases(DataSource codeDs, DataSource userDs) } } - public static List providers(Supplier ldSupplier) { + public static List providers(Supplier ldSupplier, SecurityHub security) { var objMapper = new ObjectMapper().registerModule(new Jdk8Module()); return List.of( new JAXBElementProvider(), @@ -204,9 +205,10 @@ public static List providers(Supplier ldSupplier) { new SoapExceptionMapper(), new JsonExceptionMapper(), new EnumExceptionMapper(), + new ProxyServerExceptionMapper(), new ObservabilityHeadersInterceptor(), new ObservabilityResetInterceptor(), - new AuthenticateRequestInterceptor(ldSupplier)); + new AuthenticateRequestInterceptor(ldSupplier, security)); } public static void main(String[] args) throws Exception { diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/AuthenticateRequestInterceptor.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/AuthenticateRequestInterceptor.java similarity index 73% rename from proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/AuthenticateRequestInterceptor.java rename to proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/AuthenticateRequestInterceptor.java index 960af1257..eaf648bf0 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/AuthenticateRequestInterceptor.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/AuthenticateRequestInterceptor.java @@ -1,11 +1,10 @@ -package edu.suffolk.litlab.efsp.server.utils; +package edu.suffolk.litlab.efsp.server.auth; import edu.suffolk.litlab.efsp.Jurisdiction; import edu.suffolk.litlab.efsp.db.LoginDatabase; import edu.suffolk.litlab.efsp.db.model.AtRest; -import edu.suffolk.litlab.efsp.server.auth.TylerLogin; import edu.suffolk.litlab.efsp.server.logging.MDCWrappers; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; +import edu.suffolk.litlab.efsp.stdlib.NonEmptyString; import edu.suffolk.litlab.efsp.utils.Hasher; import jakarta.annotation.Priority; import jakarta.ws.rs.Priorities; @@ -30,9 +29,12 @@ public class AuthenticateRequestInterceptor implements ContainerRequestFilter { private static final Logger log = LoggerFactory.getLogger(AuthenticateRequestInterceptor.class); public static final String API_KEY_HEADER = "X-API-KEY"; private final Supplier ldSupplier; + private final SecurityHub securityHub; - public AuthenticateRequestInterceptor(Supplier ldSupplier) { + public AuthenticateRequestInterceptor( + Supplier ldSupplier, SecurityHub securityHub) { this.ldSupplier = ldSupplier; + this.securityHub = securityHub; } @Override @@ -45,11 +47,19 @@ public void filter(ContainerRequestContext requestContext) throws IOException { requestContext.abortWith(Response.status(401).build()); return; } - var maybeJurisdiction = + Optional maybeJurisdiction = getJurisdiction(requestContext.getUriInfo().getAbsolutePath().getPath()); - var maybeCreds = maybeJurisdiction.flatMap(j -> getCreds(requestContext, j)); + var maybeCreds = + maybeJurisdiction.flatMap( + j -> getCreds(securityHub.getTokenHeaderValue(requestContext.getHeaders(), j))); var p = maybeCreds.map(creds -> new TylerUserFromServer(creds)); - var security = new EfspSecurityContext(p, atRest.get(), ""); + Optional userId = + maybeJurisdiction.flatMap( + j -> { + var headerVal = securityHub.getUserIdHeaderValue(requestContext.getHeaders(), j); + return NonEmptyString.create(headerVal); + }); + var security = new EfspSecurityContext(p, userId, atRest.get(), ""); requestContext.setSecurityContext(security); } catch (SQLException ex) { log.error("SQL when trying to check API Key / login:", ex); @@ -81,15 +91,12 @@ public static Optional getJurisdiction(String path) { *

Based on what the current path of the request is (it needs to be under "jurisdictions/..." * to get the right login token). */ - public static Optional getCreds( - ContainerRequestContext requestContext, Jurisdiction jurisdiction) { - String tylerToken = - requestContext.getHeaderString(TylerLogin.getHeaderKeyFromJurisdiction(jurisdiction)); - var maybeCreds = TylerUserNamePassword.userCredsFromAuthorization(tylerToken); + public static Optional getCreds(String token) { + var maybeCreds = UserNamePassword.userCredsFromAuthorization(token); if (maybeCreds.isPresent()) { - MDC.put(MDCWrappers.USER_ID, Hasher.makeHash(tylerToken)); + MDC.put(MDCWrappers.USER_ID, Hasher.makeHash(token)); } else { - if (tylerToken != null && !tylerToken.isBlank()) { + if (token != null && !token.isBlank()) { MDC.put(MDCWrappers.USER_ID, "(invalid token)"); } } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/EfspSecurityContext.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/EfspSecurityContext.java similarity index 67% rename from proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/EfspSecurityContext.java rename to proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/EfspSecurityContext.java index cbb6e5d89..b05e15c4d 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/EfspSecurityContext.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/EfspSecurityContext.java @@ -1,6 +1,8 @@ -package edu.suffolk.litlab.efsp.server.utils; +package edu.suffolk.litlab.efsp.server.auth; import edu.suffolk.litlab.efsp.db.model.AtRest; +import edu.suffolk.litlab.efsp.stdlib.NonEmptyString; +import jakarta.annotation.Nullable; import jakarta.ws.rs.core.SecurityContext; import java.security.Principal; import java.util.Optional; @@ -9,12 +11,17 @@ public class EfspSecurityContext implements SecurityContext { private final Optional principal; + @Nullable private final Optional userIdFromHeader; private final AtRest serverInfo; private final String role; // TODO: make this the Tyler role? idk how to do that every query public EfspSecurityContext( - Optional principal, AtRest serverInfo, String role) { + Optional principal, + Optional userIdFromHeader, + AtRest serverInfo, + String role) { this.principal = principal; + this.userIdFromHeader = userIdFromHeader; this.serverInfo = serverInfo; this.role = role; } @@ -28,6 +35,18 @@ public Optional getTylerUser() { return principal; } + public UserCreds getUserCreds() { + if (principal.isPresent()) { + return principal.get().creds(); + } else { + return new NullUserCreds(); + } + } + + public Optional getUserId() { + return userIdFromHeader; + } + public UUID getServerId() { return serverInfo.serverId; } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/LoginInterface.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/LoginInterface.java index d3031b1fb..f4223ba04 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/LoginInterface.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/LoginInterface.java @@ -1,13 +1,27 @@ package edu.suffolk.litlab.efsp.server.auth; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.ws.rs.core.MultivaluedMap; import java.util.Optional; public interface LoginInterface { + /** If LoginResult is present, login succeeded. */ Optional login(JsonNode loginInfo); String getLoginName(); - String getHeaderKey(); + /** + * Gets the required login token for all subsequent calls. + * + * @return might be null + */ + String getTokenHeaderValue(MultivaluedMap headers); + + /** + * Gets the user ID of the user initiating these calls. + * + * @return might be null + */ + String getUserIdHeaderValue(MultivaluedMap headers); } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/NeedsAuthorization.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/NeedsAuthorization.java similarity index 90% rename from proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/NeedsAuthorization.java rename to proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/NeedsAuthorization.java index 76ce7bee5..8ecb6b24b 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/NeedsAuthorization.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/NeedsAuthorization.java @@ -1,4 +1,4 @@ -package edu.suffolk.litlab.efsp.server.utils; +package edu.suffolk.litlab.efsp.server.auth; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/NullUserCreds.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/NullUserCreds.java new file mode 100644 index 000000000..3f093336f --- /dev/null +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/NullUserCreds.java @@ -0,0 +1,15 @@ +package edu.suffolk.litlab.efsp.server.auth; + +import java.util.List; +import org.apache.cxf.headers.Header; + +public class NullUserCreds implements UserCreds { + + public String getUserName() { + return ""; + } + + public List

toHeaders() { + return List.of(); + } +} diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/SecurityHub.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/SecurityHub.java index 261fd5699..336a290fe 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/SecurityHub.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/SecurityHub.java @@ -5,14 +5,13 @@ import edu.suffolk.litlab.efsp.db.LoginDatabase; import edu.suffolk.litlab.efsp.db.model.AtRest; import edu.suffolk.litlab.efsp.db.model.NewTokens; +import jakarta.ws.rs.core.MultivaluedMap; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +29,8 @@ public class SecurityHub { private static final Logger log = LoggerFactory.getLogger(SecurityHub.class); - private final List tylerLoginObjs; - private final Map>> loginFunctions; + private final Map loginByKey; + private final Map loginByJurisdiction; private final Supplier ldSupplier; /** @@ -41,21 +40,33 @@ public class SecurityHub { */ public SecurityHub(Supplier ldSupplier, List jurisdictions) { this.ldSupplier = ldSupplier; - if (jurisdictions.isEmpty()) { - this.tylerLoginObjs = List.of(); - } else { - this.tylerLoginObjs = - jurisdictions.stream().map(j -> new TylerLogin(j)).collect(Collectors.toList()); + this.loginByKey = new HashMap<>(); + this.loginByJurisdiction = new HashMap<>(); + for (var jurisdiction : jurisdictions) { + var login = + switch (jurisdiction.getVendor()) { + case Jurisdiction.Vendor.TYLER -> new TylerLogin(jurisdiction); + case Jurisdiction.Vendor.TRUE_FILING -> new TrueFilingLogin(jurisdiction); + }; + this.loginByKey.put(login.getLoginName(), login); + this.loginByJurisdiction.put(jurisdiction, login); } + } - this.loginFunctions = new HashMap<>(); - this.loginFunctions.put( - "jeffnet", - info -> - Optional.of(new LoginResult(Map.of("JEFFNET-TOKEN", "deprecated"), Optional.empty()))); - this.loginFunctions.putAll( - this.tylerLoginObjs.stream() - .collect(Collectors.toMap(lo -> lo.getLoginName(), lo -> (info) -> lo.login(info)))); + public String getUserIdHeaderValue( + MultivaluedMap headers, Jurisdiction jurisdiction) { + if (!this.loginByJurisdiction.containsKey(jurisdiction)) { + return null; + } + return this.loginByJurisdiction.get(jurisdiction).getUserIdHeaderValue(headers); + } + + public String getTokenHeaderValue( + MultivaluedMap headers, Jurisdiction jurisdiction) { + if (!this.loginByJurisdiction.containsKey(jurisdiction)) { + return null; + } + return this.loginByJurisdiction.get(jurisdiction).getTokenHeaderValue(headers); } /** @@ -99,11 +110,15 @@ public Optional login(String apiKey, JsonNode loginInfo) { if (orgName.equalsIgnoreCase("api_key")) { continue; } + if (orgName.equalsIgnoreCase("jeffnet")) { + // Backwards compatible + newTokens.put("JEFFNET-TOKEN", "deprecated"); + continue; + } // TODO(brycew): feels hacky, but we don't want an additional column to the db for each new // jurisdiction - if (!loginFunctions.containsKey(orgName)) { - log.error( - "There is no {} to login to: loginFunctions: {}", orgName, loginFunctions.keySet()); + if (!loginByKey.containsKey(orgName)) { + log.error("There is no {} to login to: loginFunctions: {}", orgName, loginByKey.keySet()); return Optional.empty(); } @@ -115,7 +130,7 @@ public Optional login(String apiKey, JsonNode loginInfo) { log.error("There is no {} to login to: enabled map: {}", permissionsName, atRest.enabled); return Optional.empty(); } - Optional maybeResult = loginFunctions.get(orgName).apply(loginInfo.get(orgName)); + Optional maybeResult = loginByKey.get(orgName).login(loginInfo.get(orgName)); if (maybeResult.isEmpty()) { log.warn("Couldn't login to {}", orgName); return Optional.empty(); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TrueFilingLogin.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TrueFilingLogin.java new file mode 100644 index 000000000..73c8e1a30 --- /dev/null +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TrueFilingLogin.java @@ -0,0 +1,37 @@ +package edu.suffolk.litlab.efsp.server.auth; + +import com.fasterxml.jackson.databind.JsonNode; +import edu.suffolk.litlab.efsp.Jurisdiction; +import jakarta.ws.rs.core.MultivaluedMap; +import java.util.Map; +import java.util.Optional; + +public class TrueFilingLogin implements LoginInterface { + + private final Jurisdiction jurisdiction; + + public TrueFilingLogin(Jurisdiction jurisdiction) { + this.jurisdiction = jurisdiction; + } + + @Override + public Optional login(JsonNode loginInfo) { + // TrueFiling doesn't have a user account system, so nothing to do! + return Optional.of(new LoginResult(Map.of(), Optional.empty())); + } + + @Override + public String getLoginName() { + return "truefiling-" + jurisdiction.getName(); + } + + @Override + public String getTokenHeaderValue(MultivaluedMap headers) { + return null; + } + + @Override + public String getUserIdHeaderValue(MultivaluedMap headers) { + return null; + } +} diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TylerLogin.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TylerLogin.java index a5aa958c9..ce6ae116e 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TylerLogin.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TylerLogin.java @@ -6,6 +6,7 @@ import edu.suffolk.litlab.efsp.tyler.TylerClients; import edu.suffolk.litlab.efsp.tyler.TylerUserClient; import edu.suffolk.litlab.efsp.tyler.TylerUserFactory; +import jakarta.ws.rs.core.MultivaluedMap; import java.util.Map; import java.util.Optional; import org.slf4j.Logger; @@ -80,8 +81,8 @@ public Optional login(JsonNode loginInfo) { } @Override - public String getHeaderKey() { - return getHeaderKeyFromJurisdiction(this.jurisdiction); + public String getTokenHeaderValue(MultivaluedMap headers) { + return headers.getFirst(getHeaderKey()); } @Override @@ -89,7 +90,16 @@ public String getLoginName() { return "tyler-" + this.jurisdiction.getName(); } - public static String getHeaderId(Jurisdiction jurisdiction) { + @Override + public String getUserIdHeaderValue(MultivaluedMap headers) { + return headers.getFirst(getHeaderId(this.jurisdiction)); + } + + private String getHeaderKey() { + return getHeaderKeyFromJurisdiction(this.jurisdiction); + } + + private static String getHeaderId(Jurisdiction jurisdiction) { return "TYLER-ID-" + jurisdiction.getName().toUpperCase(); } } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TylerUserFromServer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TylerUserFromServer.java new file mode 100644 index 000000000..74b6069fa --- /dev/null +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/TylerUserFromServer.java @@ -0,0 +1,11 @@ +package edu.suffolk.litlab.efsp.server.auth; + +import java.security.Principal; + +public record TylerUserFromServer(UserCreds creds) implements Principal { + // Principals can only have one name, so stick with the user name (email). + @Override + public String getName() { + return creds.getUserName(); + } +} diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/UserCreds.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/UserCreds.java new file mode 100644 index 000000000..a888b643c --- /dev/null +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/UserCreds.java @@ -0,0 +1,15 @@ +package edu.suffolk.litlab.efsp.server.auth; + +import java.util.List; +import org.apache.cxf.headers.Header; + +public interface UserCreds { + + /** The user's identifiable name (in Tyler this is an email). */ + String getUserName(); + + /** + * @return the user's credentials in the form of a SOAP header. + */ + List
toHeaders(); +} diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/tyler/TylerUserNamePassword.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/UserNamePassword.java similarity index 59% rename from proxyserver/src/main/java/edu/suffolk/litlab/efsp/tyler/TylerUserNamePassword.java rename to proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/UserNamePassword.java index ff6c272f8..bb9a7b02f 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/tyler/TylerUserNamePassword.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/auth/UserNamePassword.java @@ -1,9 +1,8 @@ -package edu.suffolk.litlab.efsp.tyler; +package edu.suffolk.litlab.efsp.server.auth; import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; -import java.util.Arrays; import java.util.List; import java.util.Optional; import javax.xml.namespace.QName; @@ -11,23 +10,28 @@ import org.apache.cxf.jaxb.JAXBDataBinding; import tyler.efm.latest.services.schema.authenticateresponse.AuthenticateResponseType; +/** + * A class that handles authentication to an external server by simply accepting a user's username + * and password, separated by a colon in a header. + * + *

Only Tyler is silly enough to do this though, so practically this is just theirs. + */ @XmlRootElement(name = "UserNameHeader") -public class TylerUserNamePassword { +public class UserNamePassword implements UserCreds { @XmlElement(name = "UserName") private String userName; @XmlElement(name = "Password") private String password; - public TylerUserNamePassword() {} + public UserNamePassword() {} - public TylerUserNamePassword(String userName, String password) { + public UserNamePassword(String userName, String password) { this.userName = userName; this.password = password; } - public static Optional userCredsFromAuthorization( - String userColonPassword) { + public static Optional userCredsFromAuthorization(String userColonPassword) { if (userColonPassword == null) { return Optional.empty(); } @@ -36,19 +40,21 @@ public static Optional userCredsFromAuthorization( } String email = userColonPassword.split(":")[0]; String password = userColonPassword.split(":")[1]; - return Optional.of(new TylerUserNamePassword(email, password)); + return Optional.of(new UserNamePassword(email, password)); } public String getUserName() { return userName; } - public Header toHeader() { + @Override + public List

toHeaders() { try { - return new Header( - new QName("urn:tyler:efm:services", "UserNameHeader"), - this, - new JAXBDataBinding(TylerUserNamePassword.class)); + return List.of( + new Header( + new QName("urn:tyler:efm:services", "UserNameHeader"), + this, + new JAXBDataBinding(UserNamePassword.class))); } catch (JAXBException ex) { // We are always passing this class to JAXB. If at any point it would fail, // it should be a compile time thing tbh. We don't need the extra overhead @@ -60,15 +66,11 @@ public Header toHeader() { /** * Convenience method to make the Tyler specific SOAP Header. * - * @param userName the email of the user that is sending this request - * @param passwordHash the auth token (gotten from the AuthenticateUser call). + * @param AuthenticateResponseType authRes the response from the server if the user is + * authenticated. * @return the Header object to the request context's Header.HEADER_LIST */ - public static Header makeHeader(String userName, String passwordHash) { - return (new TylerUserNamePassword(userName, passwordHash)).toHeader(); - } - public static List
makeHeaderList(AuthenticateResponseType authRes) { - return Arrays.asList(makeHeader(authRes.getEmail(), authRes.getPasswordHash())); + return (new UserNamePassword(authRes.getEmail(), authRes.getPasswordHash())).toHeaders(); } } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/AdminUserService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/AdminUserService.java index d0abcecba..0db47f326 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/AdminUserService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/AdminUserService.java @@ -9,13 +9,14 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import edu.suffolk.litlab.efsp.Jurisdiction; -import edu.suffolk.litlab.efsp.server.auth.TylerLogin; +import edu.suffolk.litlab.efsp.server.auth.EfspSecurityContext; +import edu.suffolk.litlab.efsp.server.auth.NeedsAuthorization; +import edu.suffolk.litlab.efsp.server.auth.NullUserCreds; +import edu.suffolk.litlab.efsp.server.auth.UserCreds; import edu.suffolk.litlab.efsp.server.logging.MDCWrappers; -import edu.suffolk.litlab.efsp.server.utils.EfspSecurityContext; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; -import edu.suffolk.litlab.efsp.server.utils.NeedsAuthorization; import edu.suffolk.litlab.efsp.server.utils.ServiceHelpers; -import edu.suffolk.litlab.efsp.server.utils.TylerUserFromServer; +import edu.suffolk.litlab.efsp.stdlib.NonEmptyString; import edu.suffolk.litlab.efsp.tyler.TylerClients; import edu.suffolk.litlab.efsp.tyler.TylerErrorCodes; import edu.suffolk.litlab.efsp.tyler.TylerFirmClient; @@ -162,12 +163,13 @@ public Response getAll() { @NeedsAuthorization public Response getSelfUser(@Context SecurityContext security, @Context HttpHeaders httpHeaders) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.getSelfUser"); - Optional port = setupUserPort(((EfspSecurityContext) security)); + var efspSecurity = (EfspSecurityContext) security; + Optional port = setupUserPort(efspSecurity); if (port.isEmpty()) { return Response.status(401).build(); } - String userId = httpHeaders.getHeaderString(TylerLogin.getHeaderId(jurisdiction)); - if (userId == null || userId.isBlank()) { + var userId = efspSecurity.getUserId(); + if (userId.isEmpty()) { return Response.status(500) .entity( "Server does not have a Tyler UUID for the current account. Can you give it to me?") @@ -175,7 +177,7 @@ public Response getSelfUser(@Context SecurityContext security, @Context HttpHead } GetUserRequestType req = new GetUserRequestType(); - req.setUserID(userId); + req.setUserID(userId.get().get()); GetUserResponseType resp = port.get().getUser(req); return makeResponse(resp, () -> Response.ok(resp.getUser()).build()); } @@ -222,7 +224,7 @@ public Response updateNotificationPrefs( public Response selfResendActivationEmail( @Context SecurityContext security, String emailToSendTo) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.selfResendActivationEmail"); - Optional port = setupUserPort(Optional.empty(), false); + Optional port = setupUserPort(new NullUserCreds(), false); if (port.isEmpty()) { return Response.status(401).build(); } @@ -240,7 +242,7 @@ public Response resendActivationEmail( @Context SecurityContext security, @PathParam("id") String id) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.resendActivationEmail"); Optional port = - setupFirmPort(firmFactory, ((EfspSecurityContext) security).getTylerUser()); + setupFirmPort(firmFactory, ((EfspSecurityContext) security).getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -282,7 +284,7 @@ public Response resetPassword( @Context SecurityContext security, @PathParam("id") String id, ResetPasswordParams params) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.resetPassword"); Optional port = - setupFirmPort(firmFactory, ((EfspSecurityContext) security).getTylerUser()); + setupFirmPort(firmFactory, ((EfspSecurityContext) security).getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -333,7 +335,7 @@ public Response setPassword(@Context SecurityContext security, SetPasswordParams @NeedsAuthorization public Response selfResetPassword(String emailToSend) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.selfResetPassword"); - Optional port = setupUserPort(Optional.empty(), false); + Optional port = setupUserPort(new NullUserCreds(), false); if (port.isEmpty()) { return Response.status(401).build(); } @@ -361,20 +363,19 @@ public Response getUser( @PathParam("id") String id) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.getUser"); EfspSecurityContext efspSecurity = (EfspSecurityContext) security; - String tylerId = - httpHeaders.getHeaderString(TylerLogin.getHeaderKeyFromJurisdiction(jurisdiction)); + Optional userId = efspSecurity.getUserId(); GetUserRequestType getUserReq = new GetUserRequestType(); getUserReq.setUserID(id); Function userGetter = null; - if (tylerId != null && !tylerId.isBlank() && tylerId.equals(id)) { + if (userId.map(s -> s.get().equals(id)).orElse(false)) { Optional userPort = setupUserPort(efspSecurity); if (userPort.isEmpty()) { return Response.status(401).build(); } userGetter = (req) -> userPort.get().getUser(getUserReq); } else { - Optional port = setupFirmPort(firmFactory, efspSecurity.getTylerUser()); + Optional port = setupFirmPort(firmFactory, efspSecurity.getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -393,7 +394,7 @@ public Response getUserList( @QueryParam("page_size") @DefaultValue("20") int pageSize) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.getUserList"); Optional port = - setupFirmPort(firmFactory, ((EfspSecurityContext) security).getTylerUser()); + setupFirmPort(firmFactory, ((EfspSecurityContext) security).getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -411,17 +412,19 @@ public Response getUserList( @PATCH @Path("/user") @NeedsAuthorization - public Response updateUser( - @Context SecurityContext security, @Context HttpHeaders httpHeaders, UserType updatedUser) { + public Response updateUser(@Context SecurityContext security, UserType updatedUser) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.updateUser"); - Optional port = - setupUserPort(((EfspSecurityContext) security).getTylerUser(), true); + var efspSecurity = (EfspSecurityContext) security; + Optional port = setupUserPort(efspSecurity.getUserCreds(), true); if (port.isEmpty()) { return Response.status(401).build(); } + if (efspSecurity.getUserId().isEmpty()) { + return Response.status(401).build(); + } // Ensure the user exists already. GetUserRequestType getUserReq = new GetUserRequestType(); - getUserReq.setUserID(httpHeaders.getHeaderString(TylerLogin.getHeaderId(jurisdiction))); + getUserReq.setUserID(efspSecurity.getUserId().map(s -> s.get()).orElse("")); GetUserResponseType userRes = port.get().getUser(getUserReq); if (TylerErrorCodes.checkErrors(userRes.getError()).isPresent()) { return Response.status(401, userRes.getError().getErrorText()).build(); @@ -450,7 +453,7 @@ public Response updateUserById( @Context SecurityContext security, @PathParam("id") String id, UserType updatedUser) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.updateUserById"); Optional port = - setupFirmPort(firmFactory, ((EfspSecurityContext) security).getTylerUser()); + setupFirmPort(firmFactory, ((EfspSecurityContext) security).getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -503,7 +506,7 @@ private static UpdateUserRequestType updateUser(UserType existingUser, UserType public Response getRoles(@Context SecurityContext security, @PathParam("id") String id) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.getRoles"); Optional port = - setupFirmPort(firmFactory, ((EfspSecurityContext) security).getTylerUser()); + setupFirmPort(firmFactory, ((EfspSecurityContext) security).getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -528,7 +531,7 @@ public Response addRoles( @Context SecurityContext security, @PathParam("id") String id, List toAdd) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.addRoles"); Optional port = - setupFirmPort(firmFactory, ((EfspSecurityContext) security).getTylerUser()); + setupFirmPort(firmFactory, ((EfspSecurityContext) security).getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -562,7 +565,7 @@ public Response removeRoles( @Context SecurityContext security, @PathParam("id") String id, List toRm) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.removeRoles"); Optional port = - setupFirmPort(firmFactory, ((EfspSecurityContext) security).getTylerUser()); + setupFirmPort(firmFactory, ((EfspSecurityContext) security).getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -631,9 +634,8 @@ public Response registerUser( MDC.put(MDCWrappers.OPERATION, "AdminUserService.registerUser"); final var regType = req.getRegistrationType(); boolean needsAuth = regType.equals(RegistrationType.FIRM_ADMIN_NEW_MEMBER); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional port = - setupFirmPort(firmFactory, tylerUser.map(u -> u.creds()), needsAuth); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional port = setupFirmPort(firmFactory, userCreds, needsAuth); if (port.isEmpty()) { return Response.status(401).build(); } @@ -726,7 +728,7 @@ private void logTylerErrorsWithContext(TylerErrorCodes.Error error, Registration public Response removeUser(@Context SecurityContext security, @PathParam("id") String id) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.removeUser"); Optional port = - setupFirmPort(firmFactory, ((EfspSecurityContext) security).getTylerUser()); + setupFirmPort(firmFactory, ((EfspSecurityContext) security).getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -743,7 +745,7 @@ public Response removeUser(@Context SecurityContext security, @PathParam("id") S public Response getNotificationPreferenceList(@Context SecurityContext security) { MDC.put(MDCWrappers.OPERATION, "AdminUserService.getNotificationPreferenceList"); Optional port = - setupFirmPort(firmFactory, ((EfspSecurityContext) security).getTylerUser()); + setupFirmPort(firmFactory, ((EfspSecurityContext) security).getUserCreds()); if (port.isEmpty()) { return Response.status(401).build(); } @@ -754,7 +756,7 @@ public Response getNotificationPreferenceList(@Context SecurityContext security) /** Default needsSoapHeader to True: most ops need Tyler Authentication in the SOAP header. */ private Optional setupUserPort(EfspSecurityContext security) { - return setupUserPort(security.getTylerUser(), true); + return setupUserPort(security.getUserCreds(), true); } /** @@ -764,10 +766,10 @@ private Optional setupUserPort(EfspSecurityContext security) { * @param needsSoapHeader True if the operation needs Authenticated Tyler creds to work * @return false if setup didn't work, and subsequent service calls will likely fail */ - private Optional setupUserPort( - Optional tylerUser, boolean needsSoapHeader) { + private Optional setupUserPort(UserCreds userCreds, boolean needsSoapHeader) { if (needsSoapHeader) { - if (tylerUser.isEmpty()) { + List
headersList = userCreds.toHeaders(); + if (headersList.isEmpty()) { return Optional.empty(); } Consumer setup = @@ -780,7 +782,6 @@ private Optional setupUserPort( httpClientPolicy.setReceiveTimeout(180_000); http.setClient(httpClientPolicy); Map ctx = bp.getRequestContext(); - List
headersList = List.of(tylerUser.get().creds().toHeader()); ctx.put(Header.HEADER_LIST, headersList); }; return Optional.of(userFactory.makeUserClient(setup)); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/ApiUserSettingsService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/ApiUserSettingsService.java index 90f830cb4..82027ff72 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/ApiUserSettingsService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/ApiUserSettingsService.java @@ -3,9 +3,9 @@ import com.webcohesion.enunciate.metadata.rs.ResourceGroup; import edu.suffolk.litlab.efsp.db.LoginDatabase; import edu.suffolk.litlab.efsp.db.model.AtRest; -import edu.suffolk.litlab.efsp.server.utils.EfspSecurityContext; +import edu.suffolk.litlab.efsp.server.auth.EfspSecurityContext; +import edu.suffolk.litlab.efsp.server.auth.NeedsAuthorization; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; -import edu.suffolk.litlab.efsp.server.utils.NeedsAuthorization; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/AuthenticationService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/AuthenticationService.java index 9e3ec2aa1..b41dc3410 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/AuthenticationService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/AuthenticationService.java @@ -12,6 +12,8 @@ import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.Optional; @@ -56,17 +58,19 @@ public AuthenticationService(SecurityHub security) { */ @StatusCodes({@ResponseCode(code = 403, condition = "login information not valid")}) @POST - public Response authenticateUser(String loginInfo) { + public Response authenticateUser(@Context HttpHeaders headers, String loginInfo) { MDC.put(MDCWrappers.OPERATION, "AuthenticationService.authenticateUser"); ObjectMapper mapper = new ObjectMapper(); - String apiKey; + String apiKey = headers.getHeaderString("X-API-KEY"); try { JsonNode node = mapper.readTree(loginInfo); - if (!node.isObject() || !node.has("api_key") || !node.get("api_key").isTextual()) { - log.error("Call didn't pass in an api_key in the auth call"); - return Response.status(401).build(); + if (apiKey == null || apiKey.isBlank()) { + if (!node.isObject() || !node.has("api_key") || !node.get("api_key").isTextual()) { + log.error("Call didn't pass in an api_key in the auth call"); + return Response.status(401).build(); + } + apiKey = node.get("api_key").asText(); } - apiKey = node.get("api_key").asText(); if (apiKey == null || apiKey.isBlank()) { log.error("Call passed in a null / blank api_key"); return Response.status(401).build(); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CasesService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CasesService.java index 15f18cd6d..4664a8e44 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CasesService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CasesService.java @@ -26,20 +26,19 @@ import ecf4.latest.tyler.efm.wsdl.webservicesprofile_implementation_4_0.CourtRecordMDEService; import edu.suffolk.litlab.efsp.Jurisdiction; import edu.suffolk.litlab.efsp.model.Name; -import edu.suffolk.litlab.efsp.server.auth.TylerLogin; +import edu.suffolk.litlab.efsp.server.auth.EfspSecurityContext; +import edu.suffolk.litlab.efsp.server.auth.NeedsAuthorization; +import edu.suffolk.litlab.efsp.server.auth.UserCreds; import edu.suffolk.litlab.efsp.server.ecf4.EcfCaseTypeFactory; import edu.suffolk.litlab.efsp.server.logging.MDCWrappers; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; -import edu.suffolk.litlab.efsp.server.utils.NeedsAuthorization; import edu.suffolk.litlab.efsp.server.utils.ServiceHelpers; import edu.suffolk.litlab.efsp.server.utils.ServiceHelpers.FileableCourtType; import edu.suffolk.litlab.efsp.tyler.Ecf4Helper; import edu.suffolk.litlab.efsp.tyler.SoapClientChooser; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; import edu.suffolk.litlab.efsp.tyler.ecfcodes.CodeDatabase; import edu.suffolk.litlab.efsp.tyler.ecfcodes.CourtLocationInfo; import edu.suffolk.litlab.efsp.tyler.ecfcodes.DataFieldRow; -import edu.suffolk.litlab.efsp.utils.Hasher; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; @@ -49,6 +48,7 @@ import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; import jakarta.xml.bind.JAXBElement; import jakarta.xml.ws.BindingProvider; import java.lang.reflect.Method; @@ -84,11 +84,9 @@ public class CasesService { new ecf4.latest.oasis.names.tc.legalxml_courtfiling.schema.xsd.commontypes_4 .ObjectFactory(); private final Supplier cdSupplier; - private final Jurisdiction jurisdiction; private final EndpointReflection ef; public CasesService(Jurisdiction jurisdiction, Supplier cdSupplier) { - this.jurisdiction = jurisdiction; Optional maybeRecords = SoapClientChooser.getCourtRecordFactory(jurisdiction); if (maybeRecords.isEmpty()) { @@ -148,7 +146,7 @@ public Response getEndpointsUnderCourt(@PathParam("court_id") String courtId) { @Path("/courts/{court_id}/cases") @NeedsAuthorization public Response getCaseList( - @Context HttpHeaders httpHeaders, + @Context SecurityContext security, @PathParam("court_id") String courtId, @QueryParam("docket_number") String docketId, @QueryParam("business_name") String businessName, @@ -157,7 +155,8 @@ public Response getCaseList( @QueryParam("last_name") String lastName) throws JsonProcessingException { MDC.put(MDCWrappers.OPERATION, "CasesService.getCaseList"); - Optional maybePort = setupRecordPort(httpHeaders); + EfspSecurityContext efspSecurity = (EfspSecurityContext) security; + Optional maybePort = setupRecordPort(efspSecurity.getUserCreds()); if (maybePort.isEmpty()) { return Response.status(401).build(); } @@ -253,11 +252,12 @@ public Response getCaseList( @Path("/courts/{court_id}/cases/{case_tracking_id}") @NeedsAuthorization public Response getCase( - @Context HttpHeaders httpHeaders, + @Context SecurityContext security, @PathParam("court_id") String courtId, @PathParam("case_tracking_id") String caseId) { MDC.put(MDCWrappers.OPERATION, "CasesService.getCase"); - Optional maybePort = setupRecordPort(httpHeaders); + EfspSecurityContext efspSecurity = (EfspSecurityContext) security; + Optional maybePort = setupRecordPort(efspSecurity.getUserCreds()); if (maybePort.isEmpty()) { return Response.status(401).build(); } @@ -340,11 +340,12 @@ public Response getDocument( @Path("/courts/{court_id}/service-contacts/{service_contact_id}/cases") @NeedsAuthorization public Response getServiceAttachCaseList( - @Context HttpHeaders httpHeaders, + @Context SecurityContext security, @PathParam("court_id") String courtId, @PathParam("service_contact_id") String serviceId) { MDC.put(MDCWrappers.OPERATION, "CasesService.getServiceAttachCaseList"); - Optional maybePort = setupRecordPort(httpHeaders); + EfspSecurityContext efspSecurity = (EfspSecurityContext) security; + Optional maybePort = setupRecordPort(efspSecurity.getUserCreds()); if (maybePort.isEmpty()) { return Response.status(401).build(); } @@ -368,11 +369,12 @@ public Response getServiceAttachCaseList( @Path("/courts/{court_id}/cases/{case_tracking_id}/service-information") @NeedsAuthorization public Response getServiceInformation( - @Context HttpHeaders httpHeaders, + @Context SecurityContext security, @PathParam("court_id") String courtId, @PathParam("case_tracking_id") String caseId) { MDC.put(MDCWrappers.OPERATION, "CasesService.getServiceInformation"); - Optional maybePort = setupRecordPort(httpHeaders); + EfspSecurityContext efspSecurity = (EfspSecurityContext) security; + Optional maybePort = setupRecordPort(efspSecurity.getUserCreds()); if (maybePort.isEmpty()) { return Response.status(401).build(); } @@ -397,11 +399,12 @@ public Response getServiceInformation( @Path("/courts/{court_id}/cases/{case_tracking_id}/service-information-history") @NeedsAuthorization public Response getServiceInformationHistory( - @Context HttpHeaders httpHeaders, + @Context SecurityContext security, @PathParam("court_id") String courtId, @PathParam("case_tracking_id") String caseId) { MDC.put(MDCWrappers.OPERATION, "CasesService.getServiceInformationHistory"); - Optional maybePort = setupRecordPort(httpHeaders); + EfspSecurityContext efspSecurity = (EfspSecurityContext) security; + Optional maybePort = setupRecordPort(efspSecurity.getUserCreds()); if (maybePort.isEmpty()) { return Response.status(401).build(); } @@ -429,13 +432,8 @@ private static boolean hasError(QueryResponseMessageType resp) { && !resp.getError().get(0).getErrorCode().getValue().equals("0")); } - private Optional setupRecordPort(HttpHeaders httpHeaders) { - String tylerToken = - httpHeaders.getHeaderString(TylerLogin.getHeaderKeyFromJurisdiction(jurisdiction)); - MDC.put(MDCWrappers.USER_ID, Hasher.makeHash(tylerToken)); - Optional creds = - TylerUserNamePassword.userCredsFromAuthorization(tylerToken); - if (creds.isEmpty()) { + private Optional setupRecordPort(UserCreds userCreds) { + if (userCreds.toHeaders().isEmpty()) { log.warn("No creds?"); return Optional.empty(); } @@ -450,7 +448,7 @@ private Optional setupRecordPort(HttpHeaders httpHeaders) { httpClientPolicy.setReceiveTimeout(180_000); http.setClient(httpClientPolicy); - ServiceHelpers.setupServicePort((BindingProvider) port, creds.get()); + ServiceHelpers.setupServicePort((BindingProvider) port, userCreds); return Optional.of(port); } } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CourtSchedulingService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CourtSchedulingService.java index cf24f9b9e..44e487ebe 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CourtSchedulingService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/CourtSchedulingService.java @@ -40,6 +40,9 @@ import edu.suffolk.litlab.efsp.model.FilingInformation; import edu.suffolk.litlab.efsp.model.PartyId; import edu.suffolk.litlab.efsp.model.Person; +import edu.suffolk.litlab.efsp.server.auth.EfspSecurityContext; +import edu.suffolk.litlab.efsp.server.auth.NeedsAuthorization; +import edu.suffolk.litlab.efsp.server.auth.UserCreds; import edu.suffolk.litlab.efsp.server.ecf4.EcfCaseTypeFactory; import edu.suffolk.litlab.efsp.server.ecf4.EcfCourtSpecificSerializer; import edu.suffolk.litlab.efsp.server.ecf4.Ecfv5CaseTypeFactory; @@ -47,16 +50,12 @@ import edu.suffolk.litlab.efsp.server.ecf4.TylerEcf4Helper; import edu.suffolk.litlab.efsp.server.logging.MDCWrappers; import edu.suffolk.litlab.efsp.server.utils.Ecfv5XmlHelper; -import edu.suffolk.litlab.efsp.server.utils.EfspSecurityContext; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; -import edu.suffolk.litlab.efsp.server.utils.NeedsAuthorization; import edu.suffolk.litlab.efsp.server.utils.ServiceHelpers; -import edu.suffolk.litlab.efsp.server.utils.TylerUserFromServer; import edu.suffolk.litlab.efsp.tyler.Ecf4Helper; import edu.suffolk.litlab.efsp.tyler.SoapClientChooser; import edu.suffolk.litlab.efsp.tyler.TylerClients; import edu.suffolk.litlab.efsp.tyler.TylerFirmFactory; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; import edu.suffolk.litlab.efsp.tyler.ecfcodes.CaseCategory; import edu.suffolk.litlab.efsp.tyler.ecfcodes.CodeDatabase; import edu.suffolk.litlab.efsp.tyler.ecfcodes.ComboCaseCodes; @@ -259,7 +258,7 @@ public Response getReturnDate( String allVars) throws SQLException, JAXBException { MDC.put(MDCWrappers.OPERATION, "CourtSchedulingService.getReturnDate"); - Optional tylerUser = ((EfspSecurityContext) security).getTylerUser(); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); Optional maybeServ = setupSchedulingPort((EfspSecurityContext) security); if (maybeServ.isEmpty()) { return Response.status(401).build(); @@ -280,9 +279,9 @@ public Response getReturnDate( mediaType = MediaType.valueOf("application/json"); } InfoCollector collector = new FailFastCollector(); - boolean isIndividual = getIsIndividual(firmFactory, tylerUser.map(u -> u.creds())); + boolean isIndividual = getIsIndividual(firmFactory, userCreds); - var filingPort = setupFilingPort(tylerUser); + var filingPort = setupFilingPort(userCreds); if (filingPort.isEmpty()) { return Response.status(401).entity("Not logged in to file with " + courtId).build(); } @@ -316,7 +315,7 @@ public Response getReturnDate( Stream.concat(info.getNewPlaintiffs().stream(), info.getNewDefendants().stream()) .toList(); if (!isFirstIndexedFiling) { - Optional recordPort = setupRecordPort(tylerUser); + Optional recordPort = setupRecordPort(userCreds); if (recordPort.isEmpty()) { return Response.status(500) .entity("Can't make connection to retrieve court records for subsequent case") @@ -607,7 +606,7 @@ public Response reserveCourtDateSync( } private Optional setupSchedulingPort(EfspSecurityContext security) { - Optional creds = security.getTylerUser().map(u -> u.creds()); + Optional creds = security.getTylerUser().map(u -> u.creds()); if (creds.isEmpty()) { log.warn("No creds?"); return Optional.empty(); @@ -615,20 +614,16 @@ private Optional setupSchedulingPort(EfspSecurityContext sec CourtSchedulingMDE serv = schedFactory.getCourtSchedulingMDEPort(); ServiceHelpers.setupServicePort((BindingProvider) serv); Map ctx = ((BindingProvider) serv).getRequestContext(); - List
headersList = List.of(creds.get().toHeader()); + List
headersList = creds.get().toHeaders(); ctx.put(Header.HEADER_LIST, headersList); return Optional.of(serv); } - private Optional setupRecordPort(Optional tylerUser) { - if (tylerUser.isEmpty()) { - return Optional.empty(); - } - + private Optional setupRecordPort(UserCreds userCreds) { CourtRecordMDEPort port = recordFactory.getCourtRecordMDEPort(); ServiceHelpers.setupServicePort((BindingProvider) port); Map ctx = ((BindingProvider) port).getRequestContext(); - List
headersList = List.of(tylerUser.get().creds().toHeader()); + List
headersList = userCreds.toHeaders(); ctx.put(Header.HEADER_LIST, headersList); return Optional.of(port); } @@ -650,14 +645,10 @@ private static boolean hasError(ResponseMessageType rt) { || (!errorCodeText.isBlank() && !errorCodeText.equals("0")); } - private Optional setupFilingPort(Optional tylerUser) { - if (tylerUser.isEmpty()) { - return Optional.empty(); - } - + private Optional setupFilingPort(UserCreds userCreds) { FilingReviewMDEPort port = makeFilingPort(); Map ctx = ((BindingProvider) port).getRequestContext(); - List
headersList = List.of(tylerUser.get().creds().toHeader()); + List
headersList = userCreds.toHeaders(); ctx.put(Header.HEADER_LIST, headersList); return Optional.of(port); } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FilingReviewService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FilingReviewService.java index 3f8314624..a1c1e9768 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FilingReviewService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FilingReviewService.java @@ -11,15 +11,14 @@ import edu.suffolk.litlab.efsp.model.FilingInformation; import edu.suffolk.litlab.efsp.model.FilingResult; import edu.suffolk.litlab.efsp.model.Person; -import edu.suffolk.litlab.efsp.server.auth.TylerLogin; +import edu.suffolk.litlab.efsp.server.auth.EfspSecurityContext; +import edu.suffolk.litlab.efsp.server.auth.NeedsAuthorization; +import edu.suffolk.litlab.efsp.server.auth.UserCreds; import edu.suffolk.litlab.efsp.server.logging.MDCWrappers; import edu.suffolk.litlab.efsp.server.services.api.EfmFilingInterface; import edu.suffolk.litlab.efsp.server.setup.EfmRestCallbackInterface; -import edu.suffolk.litlab.efsp.server.utils.EfspSecurityContext; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; -import edu.suffolk.litlab.efsp.server.utils.NeedsAuthorization; import edu.suffolk.litlab.efsp.server.utils.OrgMessageSender; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; import edu.suffolk.litlab.efsp.utils.FailFastCollector; import edu.suffolk.litlab.efsp.utils.FilingError; import edu.suffolk.litlab.efsp.utils.InfoCollector; @@ -69,7 +68,6 @@ public class FilingReviewService { private final OrgMessageSender msgSender; private final Supplier udSupplier; private final EndpointReflection ef; - private final Jurisdiction jurisdiction; public FilingReviewService( Jurisdiction jurisdiction, @@ -83,7 +81,6 @@ public FilingReviewService( this.callbackInterfaces = callbackInterfaces; this.udSupplier = udSupplier; this.msgSender = msgSender; - this.jurisdiction = jurisdiction; this.ef = new EndpointReflection("/jurisdictions/" + jurisdiction.getName() + "/filingreview"); } @@ -133,11 +130,8 @@ public Response getFilingStatus( } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } - var toRet = filer.getFilingStatus(courtId, filingId, tylerUser.get().creds()); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + var toRet = filer.getFilingStatus(courtId, filingId, userCreds); return toRet; } @@ -157,15 +151,12 @@ public Response getFilingList( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = filingInterfaces.get(courtId); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } + var userCreds = ((EfspSecurityContext) security).getUserCreds(); try { LocalDate startDate = (startStr != null) ? LocalDate.parse(startStr) : null; LocalDate beforeDate = (beforeStr != null) ? LocalDate.parse(beforeStr) : null; // beforeDate is exclusive! - return filer.getFilingList(courtId, userId, startDate, beforeDate, tylerUser.get().creds()); + return filer.getFilingList(courtId, userId, startDate, beforeDate, userCreds); } catch (DateTimeParseException ex) { return Response.status(400) .entity( @@ -189,20 +180,18 @@ public Response checkFilingForReview( if (mediaType == null) { mediaType = MediaType.valueOf("application/json"); } - String userUuid = httpHeaders.getHeaderString(TylerLogin.getHeaderId(jurisdiction)); + var efspSecurity = (EfspSecurityContext) security; + var userUuid = efspSecurity.getUserId(); Result checked = checkFilingInterfaces(courtId); if (checked.isErr()) { return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } + var userCreds = efspSecurity.getUserCreds(); if (!converterMap.containsKey(mediaType.toString())) { return Response.status(415).entity("We only support " + converterMap.keySet()).build(); } - Result maybeParser = makeParser(courtId, tylerUser.get().creds(), filer); + Result maybeParser = makeParser(courtId, userCreds, filer); if (maybeParser.isErr()) { return maybeParser.unwrapErrOrElseThrow(); } @@ -219,7 +208,7 @@ public Response checkFilingForReview( FilingInformation info = res.unwrapOrElseThrow(); info.setCourtLocation(courtId); Result resEfm = - filer.checkFiling(info, tylerUser.get().creds(), userUuid, collector); + filer.checkFiling(info, userCreds, userUuid, collector); if (resEfm.isErr()) { log.error("Error on checkFiling: {}", resEfm.toString()); return Response.ok(collector.jsonSummary()).build(); @@ -249,14 +238,11 @@ public Response calculateFilingFees( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } + var userCreds = ((EfspSecurityContext) security).getUserCreds(); if (!converterMap.containsKey(mediaType.toString())) { return Response.status(415).entity("We only support " + converterMap.keySet()).build(); } - Result maybeParser = makeParser(courtId, tylerUser.get().creds(), filer); + Result maybeParser = makeParser(courtId, userCreds, filer); if (maybeParser.isErr()) { return maybeParser.unwrapErrOrElseThrow(); } @@ -272,7 +258,7 @@ public Response calculateFilingFees( } FilingInformation info = res.unwrapOrElseThrow(); info.setCourtLocation(courtId); - Result fees = filer.getFilingFees(info, tylerUser.get().creds()); + Result fees = filer.getFilingFees(info, userCreds); return fees.match(err -> Response.status(400).entity(err.toJson()).build(), respon -> respon); } finally { parser.close(); @@ -299,14 +285,11 @@ public Response getServiceTypes( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } + var userCreds = ((EfspSecurityContext) security).getUserCreds(); if (!converterMap.containsKey(mediaType.toString())) { return Response.status(415).entity("We only support " + converterMap.keySet()).build(); } - Result maybeParser = makeParser(courtId, tylerUser.get().creds(), filer); + Result maybeParser = makeParser(courtId, userCreds, filer); if (maybeParser.isErr()) { return maybeParser.unwrapErrOrElseThrow(); } @@ -320,7 +303,7 @@ public Response getServiceTypes( } FilingInformation info = res.unwrapOrElseThrow(); info.setCourtLocation(courtId); - Result fees = filer.getServiceTypes(info, tylerUser.get().creds()); + Result fees = filer.getServiceTypes(info, userCreds); return fees.match(err -> Response.status(400).entity(err.toJson()).build(), respon -> respon); } finally { parser.close(); @@ -338,12 +321,9 @@ public Response getPolicy( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } + var userCreds = ((EfspSecurityContext) security).getUserCreds(); - var toRet = filer.getPolicy(courtId, tylerUser.get().creds()); + var toRet = filer.getPolicy(courtId, userCreds); return toRet; } @@ -402,26 +382,22 @@ private Response fileOrServe( if (mediaType == null) { mediaType = MediaType.valueOf("application/json"); } - String userUuid = httpHeaders.getHeaderString(TylerLogin.getHeaderId(jurisdiction)); + var userUuid = security.getUserId(); Result checked = checkFilingInterfaces(courtId); if (checked.isErr()) { return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } + var userCreds = ((EfspSecurityContext) security).getUserCreds(); Result maybeInfo = - parseFiling(allVars, filer, courtId, tylerUser.get().creds(), mediaType); + parseFiling(allVars, filer, courtId, userCreds, mediaType); if (maybeInfo.isErr()) { return maybeInfo.unwrapErrOrElseThrow(); } FilingInformation info = maybeInfo.unwrapOrElseThrow(); info.setCourtLocation(courtId); Result result = - filer.sendFiling( - info, tylerUser.get().creds(), userUuid, EfmFilingInterface.ApiChoice.FileApi); + filer.sendFiling(info, userCreds, userUuid, EfmFilingInterface.ApiChoice.FileApi); if (result.isErr()) { return Response.status(500).entity(result.unwrapErrOrElseThrow().toJson()).build(); } @@ -452,7 +428,7 @@ private Response fileOrServe( user.getContactInfo().getEmail().orElse(""), filingIds, security.getServerId(), - tylerUser.get().creds().toString(), + userCreds.toString(), info.getCaseTypeCode().code, courtId, ts, @@ -491,7 +467,7 @@ private Result parseFiling( String allVars, EfmFilingInterface filer, String courtId, - TylerUserNamePassword activeToken, + UserCreds activeToken, MediaType mediaType) { log.trace("Court id: {}", courtId); if (!converterMap.containsKey(mediaType.toString())) { @@ -536,11 +512,8 @@ public Response getFilingDetails( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } - return filer.getFilingDetails(courtId, filingId, tylerUser.get().creds()); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + return filer.getFilingDetails(courtId, filingId, userCreds); } finally { } } @@ -559,11 +532,8 @@ public Response getFilingService( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } - var toRet = filer.getFilingService(courtId, filingId, contactId, tylerUser.get().creds()); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + var toRet = filer.getFilingService(courtId, filingId, contactId, userCreds); return toRet; } @@ -580,11 +550,8 @@ public Response cancelFiling( return checked.unwrapErrOrElseThrow(); } EfmFilingInterface filer = checked.unwrapOrElseThrow(); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - if (tylerUser.isEmpty()) { - return Response.status(401).entity("Not logged in to file with " + courtId).build(); - } - var toRet = filer.cancelFiling(courtId, filingId, tylerUser.get().creds()); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + var toRet = filer.cancelFiling(courtId, filingId, userCreds); return toRet; } @@ -596,7 +563,7 @@ private Result checkFilingInterfaces(String courtI } private Result makeParser( - String courtId, TylerUserNamePassword creds, EfmFilingInterface filer) { + String courtId, UserCreds creds, EfmFilingInterface filer) { var parser = filer.getParser(courtId, creds); if (parser.isEmpty()) { return Result.err(Response.status(404).entity("Cannot send filing to " + courtId).build()); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FirmAttorneyAndServiceService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FirmAttorneyAndServiceService.java index cf6d4416b..33fed688d 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FirmAttorneyAndServiceService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/FirmAttorneyAndServiceService.java @@ -9,11 +9,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.webcohesion.enunciate.metadata.rs.ResourceGroup; import edu.suffolk.litlab.efsp.Jurisdiction; +import edu.suffolk.litlab.efsp.server.auth.EfspSecurityContext; +import edu.suffolk.litlab.efsp.server.auth.NeedsAuthorization; import edu.suffolk.litlab.efsp.server.logging.MDCWrappers; import edu.suffolk.litlab.efsp.server.services.api.ServiceContactInput; -import edu.suffolk.litlab.efsp.server.utils.EfspSecurityContext; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; -import edu.suffolk.litlab.efsp.server.utils.NeedsAuthorization; import edu.suffolk.litlab.efsp.tyler.TylerClients; import edu.suffolk.litlab.efsp.tyler.TylerErrorCodes; import edu.suffolk.litlab.efsp.tyler.TylerFirmClient; @@ -112,8 +112,8 @@ public Response getAll() { @NeedsAuthorization public Response getSelfFirm(@Context SecurityContext security) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.getSelfFirm"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } @@ -127,8 +127,8 @@ public Response getSelfFirm(@Context SecurityContext security) { @NeedsAuthorization public Response updateFirm(@Context SecurityContext security, String json) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.updateFirm"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } @@ -166,8 +166,8 @@ public Response updateFirm(@Context SecurityContext security, String json) { @NeedsAuthorization public Response getAttorneyList(@Context SecurityContext security) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.getAttorneyList"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -182,8 +182,8 @@ public Response getAttorneyList(@Context SecurityContext security) { public Response getAttorney( @Context SecurityContext security, @PathParam("attorney_id") String attorneyId) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.getAttorney"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -199,8 +199,8 @@ public Response getAttorney( @NeedsAuthorization public Response createAttorney(@Context SecurityContext security, AttorneyType attorney) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.createAttorney"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -234,8 +234,8 @@ public Response createAttorney(@Context SecurityContext security, AttorneyType a public Response updateAttorney( @Context SecurityContext security, @PathParam("attorney_id") String attorneyId, String json) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.updateAttorney"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -268,8 +268,8 @@ public Response updateAttorney( public Response removeAttorney( @Context SecurityContext security, @PathParam("attorney_id") String attorneyId) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.removeAttorney"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -285,8 +285,8 @@ public Response removeAttorney( @NeedsAuthorization public Response getServiceContactList(@Context SecurityContext security) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.getServiceContactList"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } @@ -300,8 +300,8 @@ public Response getServiceContactList(@Context SecurityContext security) { public Response getServiceContact( @Context SecurityContext security, @PathParam("contact_id") String contactId) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.getServiceContact"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } @@ -321,8 +321,8 @@ public Response getServiceContact( public Response removeServiceContact( @Context SecurityContext security, @PathParam("contact_id") String contactId) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.removeServiceContact"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } @@ -340,8 +340,8 @@ public Response createServiceContact(@Context SecurityContext security, String s try { ServiceContactInput input = new ObjectMapper().readValue(strInput, ServiceContactInput.class); MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.createServiceContact"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } @@ -381,8 +381,8 @@ public Response createServiceContact(@Context SecurityContext security, String s public Response attachServiceContact( @Context SecurityContext security, @PathParam("contact_id") String contactId, String json) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.attachServiceContact"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } @@ -424,8 +424,8 @@ public Response detachServiceContact( @PathParam("case_id") String caseId, @QueryParam("case_party_id") String casePartyId) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.detachServiceContact"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } @@ -446,8 +446,8 @@ public Response detachServiceContact( public Response updateServiceContact( @Context SecurityContext security, @PathParam("contact_id") String contactId, String json) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.updateServiceContact"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } @@ -494,8 +494,8 @@ public Response updateServiceContact( @NeedsAuthorization public Response getPublicList(@Context SecurityContext security, String json) { MDC.put(MDCWrappers.OPERATION, "FirmAttorneyAndServiceService.getPublicList"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(401).build(); } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/JurisdictionServiceHandle.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/JurisdictionServiceHandle.java index 8df019ce0..6411e7667 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/JurisdictionServiceHandle.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/JurisdictionServiceHandle.java @@ -65,6 +65,22 @@ public JurisdictionServiceHandle( Optional.of(p)); } + public JurisdictionServiceHandle( + Jurisdiction jurisdiction, + FilingReviewService filingReview, + CasesService cases, + CodesService codes) { + this( + jurisdiction, + Optional.empty(), + Optional.of(cases), + Optional.of(codes), + Optional.empty(), + filingReview, + Optional.empty(), + Optional.empty()); + } + public JurisdictionServiceHandle( Jurisdiction jurisdiction, FilingReviewService filingReview, CodesService codes) { this( diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/MessageSettingsService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/MessageSettingsService.java index 4b621150f..ce41d04c9 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/MessageSettingsService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/MessageSettingsService.java @@ -5,10 +5,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import edu.suffolk.litlab.efsp.db.MessageSettingsDatabase; import edu.suffolk.litlab.efsp.db.model.MessageInfo; +import edu.suffolk.litlab.efsp.server.auth.EfspSecurityContext; +import edu.suffolk.litlab.efsp.server.auth.NeedsAuthorization; import edu.suffolk.litlab.efsp.server.logging.MDCWrappers; -import edu.suffolk.litlab.efsp.server.utils.EfspSecurityContext; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; -import edu.suffolk.litlab.efsp.server.utils.NeedsAuthorization; import edu.suffolk.litlab.efsp.server.utils.OrgMessageSender; import jakarta.ws.rs.GET; import jakarta.ws.rs.PATCH; diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/PaymentsService.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/PaymentsService.java index 804f8114b..f698f1d03 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/PaymentsService.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/PaymentsService.java @@ -11,17 +11,18 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import edu.suffolk.litlab.efsp.Jurisdiction; +import edu.suffolk.litlab.efsp.server.auth.EfspSecurityContext; +import edu.suffolk.litlab.efsp.server.auth.NeedsAuthorization; +import edu.suffolk.litlab.efsp.server.auth.NullUserCreds; +import edu.suffolk.litlab.efsp.server.auth.UserNamePassword; import edu.suffolk.litlab.efsp.server.logging.MDCWrappers; -import edu.suffolk.litlab.efsp.server.utils.EfspSecurityContext; import edu.suffolk.litlab.efsp.server.utils.EndpointReflection; -import edu.suffolk.litlab.efsp.server.utils.NeedsAuthorization; import edu.suffolk.litlab.efsp.server.utils.ServiceHelpers; import edu.suffolk.litlab.efsp.stdlib.RandomString; import edu.suffolk.litlab.efsp.tyler.TylerClients; import edu.suffolk.litlab.efsp.tyler.TylerErrorCodes; import edu.suffolk.litlab.efsp.tyler.TylerFirmClient; import edu.suffolk.litlab.efsp.tyler.TylerFirmFactory; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; import edu.suffolk.litlab.efsp.tyler.ecfcodes.CodeDatabase; import edu.suffolk.litlab.efsp.utils.Hasher; import jakarta.ws.rs.Consumes; @@ -146,8 +147,8 @@ public Response getAll() { @NeedsAuthorization public Response getGlobalPaymentList(@Context SecurityContext security) { MDC.put(MDCWrappers.OPERATION, "PaymentsService.getGlobalPaymentList"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -162,8 +163,8 @@ public Response getGlobalPaymentList(@Context SecurityContext security) { public Response getGlobalPaymentAccount( @Context SecurityContext security, @PathParam("account_id") String accountId) { MDC.put(MDCWrappers.OPERATION, "PaymentsService.getGlobalPaymentAccount"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -190,8 +191,8 @@ public Response getGlobalPaymentAccount( @NeedsAuthorization public Response createGlobalWaiverAccount(@Context SecurityContext security, String accountName) { MDC.put(MDCWrappers.OPERATION, "PaymentsService.createGlobalWaiverAccount"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -204,8 +205,8 @@ public Response createGlobalWaiverAccount(@Context SecurityContext security, Str public Response updateGlobalPaymentAccount( @Context SecurityContext security, @PathParam("account_id") String accountId, String json) { MDC.put(MDCWrappers.OPERATION, "PaymentsService.updateGlobalPaymentAccount"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -228,8 +229,8 @@ public Response updateGlobalPaymentAccount( public Response removeGlobalPaymentAccount( @Context SecurityContext security, @PathParam("account_id") String accountId) { MDC.put(MDCWrappers.OPERATION, "PaymentsService.removeGlobalPaymentAccount"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -246,8 +247,8 @@ public Response removeGlobalPaymentAccount( public Response getPaymentAccount( @Context SecurityContext security, @PathParam("account_id") String accountId) { MDC.put(MDCWrappers.OPERATION, "PaymentsService.getPaymentAccount"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -265,8 +266,8 @@ public Response getPaymentAccount( public Response removePaymentAccount( @Context SecurityContext security, @PathParam("account_id") String accountId) { MDC.put(MDCWrappers.OPERATION, "PaymentsService.removePaymentAccount"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -284,8 +285,8 @@ public Response getPaymentAccountList( @Context SecurityContext security, @DefaultValue("") @QueryParam("court_id") String courtId) throws SQLException { MDC.put(MDCWrappers.OPERATION, "PaymentsService.getPaymentAccountList"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -321,8 +322,8 @@ public Response getPaymentAccountList( @NeedsAuthorization public Response createWaiverAccount(@Context SecurityContext security, String accountName) { MDC.put(MDCWrappers.OPERATION, "PaymentsService.createWaiverAccount"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -344,8 +345,8 @@ public Response updatePaymentAccount( @Context SecurityContext security, @PathParam("account_id") String accountId, String json) throws JsonMappingException, JsonProcessingException { MDC.put(MDCWrappers.OPERATION, "PaymentsService.updatePaymentAccount"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = ServiceHelpers.setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = ServiceHelpers.setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -367,8 +368,8 @@ public Response updatePaymentAccount( @NeedsAuthorization public Response getPaymentAccountTypeList(@Context SecurityContext security) { MDC.put(MDCWrappers.OPERATION, "PaymentsService.getPaymentAccountTypeList"); - var tylerUser = ((EfspSecurityContext) security).getTylerUser(); - Optional firmPort = ServiceHelpers.setupFirmPort(firmFactory, tylerUser); + var userCreds = ((EfspSecurityContext) security).getUserCreds(); + Optional firmPort = ServiceHelpers.setupFirmPort(firmFactory, userCreds); if (firmPort.isEmpty()) { return Response.status(403).build(); } @@ -440,10 +441,10 @@ public Response redirectToToga( log.error(err); return Response.status(422).entity(errorHtml.formatted(err)).build(); } - var maybeCreds = TylerUserNamePassword.userCredsFromAuthorization(tylerInfo); + var maybeCreds = UserNamePassword.userCredsFromAuthorization(tylerInfo); Optional firmPort = - ServiceHelpers.setupFirmPort(firmFactory, maybeCreds, true); - if (firmPort.isEmpty()) { + ServiceHelpers.setupFirmPort(firmFactory, maybeCreds.orElse(new NullUserCreds()), true); + if (maybeCreds.isEmpty() || firmPort.isEmpty()) { String err = "Unable to use your login information with Tyler: will not be able to create the payment" + " account."; @@ -574,9 +575,9 @@ public Response makeNewPaymentAccount( return Response.status(404).entity(paymentsErrorHtml).build(); } TempAccount tempInfo = tempAccounts.get(resp.transactionId); - var maybeCreds = TylerUserNamePassword.userCredsFromAuthorization(tempInfo.loginInfo); + var maybeCreds = UserNamePassword.userCredsFromAuthorization(tempInfo.loginInfo); Optional maybeFirmPort = - ServiceHelpers.setupFirmPort(firmFactory, maybeCreds, true); + ServiceHelpers.setupFirmPort(firmFactory, maybeCreds.orElse(new NullUserCreds()), true); if (maybeFirmPort.isEmpty()) { log.warn("Couldn't get the firm port for {}", resp.transactionId); return Response.status(403).entity(paymentsErrorHtml).build(); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/api/EfmFilingInterface.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/api/EfmFilingInterface.java index 8032debbb..f182491af 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/api/EfmFilingInterface.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/api/EfmFilingInterface.java @@ -5,7 +5,8 @@ import edu.suffolk.litlab.efsp.ecfcodes.CodesParser; import edu.suffolk.litlab.efsp.model.FilingInformation; import edu.suffolk.litlab.efsp.model.FilingResult; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; +import edu.suffolk.litlab.efsp.server.auth.UserCreds; +import edu.suffolk.litlab.efsp.stdlib.NonEmptyString; import edu.suffolk.litlab.efsp.utils.FilingError; import edu.suffolk.litlab.efsp.utils.InfoCollector; import jakarta.ws.rs.core.Response; @@ -29,19 +30,17 @@ public enum ApiChoice { * the callbacks. If it's > 1, should be the same number as the FilingDocs in info */ public Result sendFiling( - FilingInformation info, TylerUserNamePassword creds, String userUuid, ApiChoice choice); + FilingInformation info, UserCreds creds, Optional userUuid, ApiChoice choice); public Result checkFiling( FilingInformation info, - TylerUserNamePassword creds, - String userUuid, + UserCreds creds, + Optional userUuid, InfoCollector collector); - public Result getFilingFees( - FilingInformation info, TylerUserNamePassword creds); + public Result getFilingFees(FilingInformation info, UserCreds creds); - public Result getServiceTypes( - FilingInformation info, TylerUserNamePassword creds); + public Result getServiceTypes(FilingInformation info, UserCreds creds); // TODO(brycew-later): make this a little more independent of HTTP public Response getFilingList( @@ -49,20 +48,20 @@ public Response getFilingList( String submitterId, LocalDate startDate, LocalDate beforeDate, - TylerUserNamePassword creds); + UserCreds creds); - public Response getFilingStatus(String courtId, String filingId, TylerUserNamePassword creds); + public Response getFilingStatus(String courtId, String filingId, UserCreds creds); - public Response getFilingDetails(String courtId, String filingId, TylerUserNamePassword creds); + public Response getFilingDetails(String courtId, String filingId, UserCreds creds); public Response getFilingService( - String courtId, String filingId, String contactId, TylerUserNamePassword creds); + String courtId, String filingId, String contactId, UserCreds creds); - public Response getPolicy(String courtId, TylerUserNamePassword creds); + public Response getPolicy(String courtId, UserCreds creds); - public Response cancelFiling(String courtId, String filingId, TylerUserNamePassword creds); + public Response cancelFiling(String courtId, String filingId, UserCreds creds); - public Optional getParser(String courtId, TylerUserNamePassword creds); + public Optional getParser(String courtId, UserCreds creds); /** * TYLER ONLY at the moment: returns a list of disclaimers that must be shown to the user before @@ -76,7 +75,4 @@ public Response getFilingService( /** Used to properly verify with the SecurityHub. */ public String getOrgName(); - - /** Used to get the proper header that contains the right token. */ - public String getHeaderKey(); } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/Ecf4Filer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/Ecf4Filer.java index cdce9ad81..355084b7e 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/Ecf4Filer.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/Ecf4Filer.java @@ -52,14 +52,16 @@ import edu.suffolk.litlab.efsp.model.Name; import edu.suffolk.litlab.efsp.model.PartyId; import edu.suffolk.litlab.efsp.model.Person; -import edu.suffolk.litlab.efsp.server.auth.TylerLogin; +import edu.suffolk.litlab.efsp.server.auth.UserCreds; import edu.suffolk.litlab.efsp.server.ecf4.EcfCaseTypeFactory; import edu.suffolk.litlab.efsp.server.ecf4.EcfCourtSpecificSerializer; import edu.suffolk.litlab.efsp.server.ecf4.PaymentFactory; import edu.suffolk.litlab.efsp.server.ecf4.PolicyCacher; import edu.suffolk.litlab.efsp.server.ecf4.QueryType; import edu.suffolk.litlab.efsp.server.ecf4.TylerEcf4Helper; +import edu.suffolk.litlab.efsp.server.utils.ProxyServerException; import edu.suffolk.litlab.efsp.server.utils.ServiceHelpers; +import edu.suffolk.litlab.efsp.stdlib.NonEmptyString; import edu.suffolk.litlab.efsp.tyler.Ecf4Helper; import edu.suffolk.litlab.efsp.tyler.SoapClientChooser; import edu.suffolk.litlab.efsp.tyler.TylerAdminUserUtils; @@ -67,7 +69,6 @@ import edu.suffolk.litlab.efsp.tyler.TylerFirmFactory; import edu.suffolk.litlab.efsp.tyler.TylerUserClient; import edu.suffolk.litlab.efsp.tyler.TylerUserFactory; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; import edu.suffolk.litlab.efsp.tyler.ecfcodes.CaseCategory; import edu.suffolk.litlab.efsp.tyler.ecfcodes.CaseType; import edu.suffolk.litlab.efsp.tyler.ecfcodes.CodeDatabase; @@ -111,7 +112,6 @@ public class Ecf4Filer extends EfmCheckableFilingInterface { private static Logger log = LoggerFactory.getLogger(Ecf4Filer.class); private final Supplier cdSupplier; - private final String headerKey; private final ecf4.latest.oasis.names.tc.legalxml_courtfiling.schema.xsd .filingstatusquerymessage_4.ObjectFactory statusObjFac; @@ -138,8 +138,6 @@ public Ecf4Filer( Jurisdiction jurisdiction, Supplier cdSupplier, PolicyCacher policyCacher) { this.jurisdiction = jurisdiction; this.cdSupplier = cdSupplier; - TylerLogin login = new TylerLogin(jurisdiction); - this.headerKey = login.getHeaderKey(); statusObjFac = new ecf4.latest.oasis.names.tc.legalxml_courtfiling.schema.xsd.filingstatusquerymessage_4 .ObjectFactory(); @@ -188,20 +186,14 @@ public Ecf4Filer( } } - @Override - public String getHeaderKey() { - return this.headerKey; - } - - public Optional getParser(String courtId, TylerUserNamePassword creds) { + public Optional getParser(String courtId, UserCreds creds) { CodeDatabase cd = cdSupplier.get(); return getParser(cd, courtId, creds); } - private Optional getParser( - CodeDatabase cd, String courtId, TylerUserNamePassword creds) { + private Optional getParser(CodeDatabase cd, String courtId, UserCreds creds) { var filingPort = setupFilingPort(creds); - boolean isIndividual = getIsIndividual(firmFactory, Optional.of(creds)); + boolean isIndividual = getIsIndividual(firmFactory, creds); var policy = policyCacher.getPolicyFor(filingPort, courtId).getDevelopmentPolicyParameters().getValue(); return TylerCodesParser.makeParser(cd, policy, courtId, isIndividual); @@ -216,7 +208,7 @@ public record CoreMessageAndNames( private CoreMessageAndNames prepareFiling( FilingInformation info, InfoCollector collector, - TylerUserNamePassword creds, + UserCreds creds, FilingReviewMDEPort filingPort, CourtRecordMDEPort recordPort, QueryType queryType) @@ -514,8 +506,8 @@ private Result serveFilingIfReady( CoreFilingMessageType cfm, FilingInformation info, InfoCollector collector, - TylerUserNamePassword creds, - String userUuid) { + UserCreds creds, + Optional userUuid) { ServiceMDEPort port = setupServicePort(creds); ServiceReceiptMessageType receipt = port.serveFiling(cfm); StringBuilder sb = new StringBuilder(); @@ -551,8 +543,8 @@ private Result serveFilingIfReady( public Result submitFilingIfReady( FilingInformation info, InfoCollector collector, - TylerUserNamePassword creds, - String userUuid, + UserCreds creds, + Optional userUuid, ApiChoice choice) { FilingReviewMDEPort filingPort; CoreFilingMessageType cfm; @@ -657,14 +649,14 @@ public Result submitFilingIfReady( } public Person repairLeadContact( - Optional maybeLeadContact, TylerUserNamePassword creds, String userUuid) { + Optional maybeLeadContact, UserCreds creds, Optional userUuid) { Supplier userInfo = () -> { - TylerUserClient userPort = setupUserPort(creds); - if (userUuid == null || userUuid.isBlank()) { + if (userUuid.isEmpty()) { return Person.TestPerson(new Name(""), "", false); } - return TylerAdminUserUtils.getUser(userPort, userUuid); + TylerUserClient userPort = setupUserPort(creds); + return TylerAdminUserUtils.getUser(userPort, userUuid.get().get()); }; Person leadContact = maybeLeadContact.orElseGet(userInfo); if (leadContact.getContactInfo().getEmail().isEmpty() @@ -675,8 +667,7 @@ public Person repairLeadContact( } @Override - public Result getFilingFees( - FilingInformation info, TylerUserNamePassword creds) { + public Result getFilingFees(FilingInformation info, UserCreds creds) { FailFastCollector collector = new FailFastCollector(); CoreFilingMessageType cfm; FilingReviewMDEPort filingPort = setupFilingPort(creds); @@ -748,8 +739,7 @@ private List getAllLocations() throws SQLException { } @Override - public Result getServiceTypes( - FilingInformation info, TylerUserNamePassword creds) { + public Result getServiceTypes(FilingInformation info, UserCreds creds) { Optional court = getCourtInfo(info); if (court.isEmpty()) { return Result.ok(Response.status(404).entity("No court " + info.getCourtLocation()).build()); @@ -789,7 +779,7 @@ public Response getFilingList( String submitterId, java.time.LocalDate startDate, java.time.LocalDate beforeDate, - TylerUserNamePassword creds) { + UserCreds creds) { try { List courtIds = getAllLocations(); if (courtId != null && !courtId.equals("0") && !courtIds.contains(courtId)) { @@ -870,7 +860,7 @@ public Response getFilingList( } @Override - public Response getFilingStatus(String courtId, String filingId, TylerUserNamePassword creds) { + public Response getFilingStatus(String courtId, String filingId, UserCreds creds) { try { List courtIds = getAllLocations(); if (!courtIds.contains(courtId)) { @@ -891,7 +881,7 @@ public Response getFilingStatus(String courtId, String filingId, TylerUserNamePa @Override public Response getFilingService( - String courtId, String filingId, String contactId, TylerUserNamePassword creds) { + String courtId, String filingId, String contactId, UserCreds creds) { FilingReviewMDEPort port = setupFilingPort(creds); FilingServiceQueryMessageType req = new FilingServiceQueryMessageType(); ServiceContactIdentificationType id = new ServiceContactIdentificationType(); @@ -904,7 +894,7 @@ public Response getFilingService( } @Override - public Response getFilingDetails(String courtId, String filingId, TylerUserNamePassword creds) { + public Response getFilingDetails(String courtId, String filingId, UserCreds creds) { try { List courtIds = getAllLocations(); if (!courtIds.contains(courtId)) { @@ -923,7 +913,7 @@ public Response getFilingDetails(String courtId, String filingId, TylerUserNameP } @Override - public Response getPolicy(String courtId, TylerUserNamePassword creds) { + public Response getPolicy(String courtId, UserCreds creds) { try { List courtIds = getAllLocations(); if (!courtIds.contains(courtId)) { @@ -939,7 +929,7 @@ public Response getPolicy(String courtId, TylerUserNamePassword creds) { } @Override - public Response cancelFiling(String courtId, String filingId, TylerUserNamePassword creds) { + public Response cancelFiling(String courtId, String filingId, UserCreds creds) { try { List courtIds = getAllLocations(); if (!courtIds.contains(courtId)) { @@ -977,7 +967,13 @@ private static T prep(T newMsg, String courtId) { return TylerEcf4Helper.prep(newMsg, courtId); } - private FilingReviewMDEPort setupFilingPort(TylerUserNamePassword creds) { + private FilingReviewMDEPort setupFilingPort(UserCreds creds) { + List
headersList = creds.toHeaders(); + if (headersList.isEmpty()) { + // We know we're in Tyler, which needs Headers. If it's actually a NullUserCred, need to stop + // here. + throw new ProxyServerException.AuthenticationNeeded(); + } FilingReviewMDEPort port = makeFilingPort(); Client client = ClientProxy.getClient(port); HTTPConduit http = (HTTPConduit) client.getConduit(); @@ -986,35 +982,35 @@ private FilingReviewMDEPort setupFilingPort(TylerUserNamePassword creds) { httpClientPolicy.setReceiveTimeout(180_000); http.setClient(httpClientPolicy); Map ctx = ((BindingProvider) port).getRequestContext(); - List
headersList = List.of(creds.toHeader()); + ctx.put(Header.HEADER_LIST, headersList); return port; } - private ServiceMDEPort setupServicePort(TylerUserNamePassword creds) { + private ServiceMDEPort setupServicePort(UserCreds creds) { ServiceMDEPort port = makeServicePort(); Map ctx = ((BindingProvider) port).getRequestContext(); - List
headersList = List.of(creds.toHeader()); + List
headersList = creds.toHeaders(); ctx.put(Header.HEADER_LIST, headersList); return port; } - private CourtRecordMDEPort setupRecordPort(TylerUserNamePassword creds) { + private CourtRecordMDEPort setupRecordPort(UserCreds creds) { CourtRecordMDEPort port = recordFactory.getCourtRecordMDEPort(); ServiceHelpers.setupServicePort((BindingProvider) port); Map ctx = ((BindingProvider) port).getRequestContext(); - List
headersList = List.of(creds.toHeader()); + List
headersList = creds.toHeaders(); ctx.put(Header.HEADER_LIST, headersList); return port; } - private TylerUserClient setupUserPort(TylerUserNamePassword creds) { + private TylerUserClient setupUserPort(UserCreds creds) { Consumer setup = (BindingProvider bp) -> { ServiceHelpers.setupServicePort(bp); ServiceHelpers.changeTimeout(bp, 180_000); Map ctx = bp.getRequestContext(); - List
headersList = List.of(creds.toHeader()); + List
headersList = creds.toHeaders(); ctx.put(Header.HEADER_LIST, headersList); }; return userFactory.makeUserClient(setup); diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/EfmCheckableFilingInterface.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/EfmCheckableFilingInterface.java index ca4befa1f..f9b3da4c7 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/EfmCheckableFilingInterface.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/services/impl/EfmCheckableFilingInterface.java @@ -4,17 +4,22 @@ import com.hubspot.algebra.Result; import edu.suffolk.litlab.efsp.model.FilingInformation; import edu.suffolk.litlab.efsp.model.FilingResult; +import edu.suffolk.litlab.efsp.server.auth.UserCreds; import edu.suffolk.litlab.efsp.server.services.api.EfmFilingInterface; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; +import edu.suffolk.litlab.efsp.stdlib.NonEmptyString; import edu.suffolk.litlab.efsp.utils.FailFastCollector; import edu.suffolk.litlab.efsp.utils.FilingError; import edu.suffolk.litlab.efsp.utils.InfoCollector; +import java.util.Optional; public abstract class EfmCheckableFilingInterface implements EfmFilingInterface { @Override public Result sendFiling( - FilingInformation info, TylerUserNamePassword creds, String userUuid, ApiChoice choice) { + FilingInformation info, + UserCreds creds, + Optional userUuid, + ApiChoice choice) { FailFastCollector collector = new FailFastCollector(); return submitFilingIfReady(info, collector, creds, userUuid, choice); } @@ -22,8 +27,8 @@ public Result sendFiling( @Override public Result checkFiling( FilingInformation info, - TylerUserNamePassword creds, - String userUuid, + UserCreds creds, + Optional userUuid, InfoCollector collector) { return submitFilingIfReady(info, collector, creds, userUuid, ApiChoice.FileApi) .mapOk(n -> null); @@ -39,7 +44,7 @@ public Result checkFiling( public abstract Result submitFilingIfReady( FilingInformation info, InfoCollector collector, - TylerUserNamePassword creds, - String userUuid, + UserCreds creds, + Optional userUuid, ApiChoice choice); } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ProxyServerException.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ProxyServerException.java new file mode 100644 index 000000000..beaf8c95f --- /dev/null +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ProxyServerException.java @@ -0,0 +1,6 @@ +package edu.suffolk.litlab.efsp.server.utils; + +public sealed class ProxyServerException extends RuntimeException { + + public static final class AuthenticationNeeded extends ProxyServerException {} +} diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ProxyServerExceptionMapper.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ProxyServerExceptionMapper.java new file mode 100644 index 000000000..d0e8518dc --- /dev/null +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ProxyServerExceptionMapper.java @@ -0,0 +1,21 @@ +package edu.suffolk.litlab.efsp.server.utils; + +import edu.suffolk.litlab.efsp.server.utils.ProxyServerException.AuthenticationNeeded; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +@Provider +public class ProxyServerExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(ProxyServerException exception) { + return switch (exception) { + case AuthenticationNeeded n -> + Response.status(Response.Status.UNAUTHORIZED) + .entity("Need to be logged in to file") + .build(); + default -> Response.status(500).build(); + }; + } +} diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ServiceHelpers.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ServiceHelpers.java index 14b678d5e..4367daa45 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ServiceHelpers.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/ServiceHelpers.java @@ -3,10 +3,10 @@ import static edu.suffolk.litlab.efsp.stdlib.StdLib.GetEnv; import edu.suffolk.litlab.efsp.ecfcodes.NameAndCode; +import edu.suffolk.litlab.efsp.server.auth.UserCreds; import edu.suffolk.litlab.efsp.tyler.TylerErrorCodes; import edu.suffolk.litlab.efsp.tyler.TylerFirmClient; import edu.suffolk.litlab.efsp.tyler.TylerFirmFactory; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; import edu.suffolk.litlab.efsp.tyler.ecfcodes.CodeDatabase; import jakarta.ws.rs.core.Response; import jakarta.xml.ws.BindingProvider; @@ -69,8 +69,8 @@ public static void setupServicePort(BindingProvider bp) { ctx.put("set-jaxb-validation-event-handler", "false"); } - public static void setupServicePort(BindingProvider bp, TylerUserNamePassword creds) { - setupServicePort(bp, List.of(creds.toHeader())); + public static void setupServicePort(BindingProvider bp, UserCreds creds) { + setupServicePort(bp, creds.toHeaders()); } public static void setupServicePort(BindingProvider bp, List
headerList) { @@ -130,9 +130,8 @@ public static Response.ResponseBuilder getCourts( } } - public static boolean getIsIndividual( - TylerFirmFactory firmFactory, Optional tylerUser) { - Optional firmClient = setupFirmPort(firmFactory, tylerUser, true); + public static boolean getIsIndividual(TylerFirmFactory firmFactory, UserCreds userCreds) { + Optional firmClient = setupFirmPort(firmFactory, userCreds, true); boolean isIndividual = firmClient .map( @@ -156,21 +155,16 @@ public static boolean getIsIndividual( } public static Optional setupFirmPort( - TylerFirmFactory firmFactory, Optional tylerUser) { - return setupFirmPort(firmFactory, tylerUser.map(u -> u.creds()), true); + TylerFirmFactory firmFactory, UserCreds userCreds) { + return setupFirmPort(firmFactory, userCreds, true); } public static Optional setupFirmPort( - TylerFirmFactory firmFactory, - Optional creds, - boolean needsSoapHeader) { + TylerFirmFactory firmFactory, UserCreds creds, boolean needsSoapHeader) { if (needsSoapHeader) { - if (creds.isEmpty()) { - log.warn("No creds?"); - return Optional.empty(); - } return Optional.of( - firmFactory.makeFirmClient((port) -> ServiceHelpers.setupServicePort(port, creds.get()))); + firmFactory.makeFirmClient( + (port) -> ServiceHelpers.setupServicePort(port, creds.toHeaders()))); } else { return Optional.of(firmFactory.makeFirmClient(ServiceHelpers::setupServicePort)); } diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/TylerUserFromServer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/TylerUserFromServer.java deleted file mode 100644 index 8c0e7b629..000000000 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/utils/TylerUserFromServer.java +++ /dev/null @@ -1,12 +0,0 @@ -package edu.suffolk.litlab.efsp.server.utils; - -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; -import java.security.Principal; - -public record TylerUserFromServer(TylerUserNamePassword creds) implements Principal { - // Principals can only have one name, so stick with the user name (email). - @Override - public String getName() { - return creds.getUserName(); - } -} diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/tyler/ecfcodes/TylerCodeUpdater.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/tyler/ecfcodes/TylerCodeUpdater.java index effc2ff3f..72faab02d 100644 --- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/tyler/ecfcodes/TylerCodeUpdater.java +++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/tyler/ecfcodes/TylerCodeUpdater.java @@ -10,6 +10,7 @@ import edu.suffolk.litlab.efsp.ecfcodes.CodeUpdater; import edu.suffolk.litlab.efsp.ecfcodes.CodeUpdater.CourtCodelistInfo; import edu.suffolk.litlab.efsp.ecfcodes.CodeUrlGetter; +import edu.suffolk.litlab.efsp.server.auth.UserNamePassword; import edu.suffolk.litlab.efsp.server.ecf4.TylerEcf4Helper; import edu.suffolk.litlab.efsp.server.logging.MDCWrappers; import edu.suffolk.litlab.efsp.server.utils.HeaderSigner; @@ -20,7 +21,6 @@ import edu.suffolk.litlab.efsp.tyler.TylerClients; import edu.suffolk.litlab.efsp.tyler.TylerUserClient; import edu.suffolk.litlab.efsp.tyler.TylerUserFactory; -import edu.suffolk.litlab.efsp.tyler.TylerUserNamePassword; import jakarta.xml.bind.JAXBException; import jakarta.xml.ws.BindingProvider; import jakarta.xml.ws.soap.SOAPFaultException; @@ -64,7 +64,7 @@ private static FilingReviewMDEPort loginWithTyler( authReq.setEmail(userEmail); authReq.setPassword(userPassword); AuthenticateResponseType authRes = userPort.authenticateUser(authReq); - List
headersList = TylerUserNamePassword.makeHeaderList(authRes); + List
headersList = UserNamePassword.makeHeaderList(authRes); FilingReviewMDEPort filingPort = filingFactory.get().getFilingReviewMDEPort(); ServiceHelpers.setupServicePort((BindingProvider) filingPort); Map ctx = ((BindingProvider) filingPort).getRequestContext(); diff --git a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/auth/SecurityHubTest.java b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/auth/SecurityHubTest.java index 73128758c..c9c589cd1 100644 --- a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/auth/SecurityHubTest.java +++ b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/auth/SecurityHubTest.java @@ -154,12 +154,15 @@ public void testTylerLogin() throws Exception { } @Test - public void testLoginWithUnauthorizedJeffNet() throws Exception { + public void testLoginWithDeprecatedJeffNet() throws Exception { ObjectNode jeffNetNode = mapper.createObjectNode(); jeffNetNode.put("api_key", "the_jeffnet_key"); loginNode.set("jeffnet", jeffNetNode); Optional withJeffnet = hub.login(API_KEY, loginNode); - assertThat(withJeffnet).isEmpty(); + assertThat(withJeffnet).isPresent(); + var tokens = withJeffnet.get().getTokens(); + assertThat(tokens).containsKey("JEFFNET-TOKEN"); + assertThat(tokens.get("JEFFNET-TOKEN")).isEqualTo("deprecated"); } @Test diff --git a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/services/AdminUserServiceTest.java b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/services/AdminUserServiceTest.java index 901604754..57dd4099d 100644 --- a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/services/AdminUserServiceTest.java +++ b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/services/AdminUserServiceTest.java @@ -12,6 +12,7 @@ import edu.suffolk.litlab.efsp.db.LoginDatabase; import edu.suffolk.litlab.efsp.db.model.AtRest; import edu.suffolk.litlab.efsp.server.EfspServer; +import edu.suffolk.litlab.efsp.server.auth.SecurityHub; import edu.suffolk.litlab.efsp.tyler.TylerClients; import edu.suffolk.litlab.efsp.tyler.TylerFirmClient; import edu.suffolk.litlab.efsp.tyler.TylerFirmFactory; @@ -106,7 +107,8 @@ private void startServer() { sf.setAddress(ENDPOINT_ADDRESS); Map extensionMappings = Map.of("json", MediaType.APPLICATION_JSON); sf.setExtensionMappings(extensionMappings); - List providers = EfspServer.providers(() -> ld); + List providers = + EfspServer.providers(() -> ld, new SecurityHub(() -> ld, List.of(Jurisdiction.ILLINOIS))); sf.setProviders(providers); server = sf.create(); diff --git a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/services/CodesServiceTest.java b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/services/CodesServiceTest.java index 754cf0d97..24067c3fa 100644 --- a/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/services/CodesServiceTest.java +++ b/proxyserver/src/test/java/edu/suffolk/litlab/efsp/server/services/CodesServiceTest.java @@ -88,7 +88,7 @@ private void startServer() throws Exception { "xml", MediaType.APPLICATION_XML, "json", MediaType.APPLICATION_JSON); sf.setExtensionMappings(extensionMappings); - List providers = EfspServer.providers(() -> null); + List providers = EfspServer.providers(() -> null, null); sf.setProviders(providers); server = sf.create(); }