[Fix] Refactored AuthenticationResource exception handling

This commit is contained in:
Robert von Burg 2023-11-30 15:05:50 +01:00
parent 3c2aac3b9e
commit 92f87cbc77
Signed by: eitch
GPG Key ID: 75DB9C85C74331F7
1 changed files with 69 additions and 141 deletions

View File

@ -15,19 +15,10 @@
*/
package li.strolch.rest.endpoint;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_AUTHORIZATION;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_AUTHORIZATION_EXPIRATION_DATE;
import static li.strolch.rest.filters.AuthenticationRequestFilter.getRemoteIp;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import com.google.gson.*;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.*;
@ -41,14 +32,26 @@ import li.strolch.privilege.model.Privilege;
import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.privilege.model.Usage;
import li.strolch.rest.RestfulStrolchComponent;
import li.strolch.runtime.sessions.StrolchSessionHandler;
import li.strolch.rest.helper.ResponseUtil;
import li.strolch.runtime.privilege.PrivilegeHandler;
import li.strolch.utils.helper.ExceptionHelper;
import li.strolch.runtime.sessions.StrolchSessionHandler;
import li.strolch.utils.iso8601.ISO8601;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_AUTHORIZATION;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_AUTHORIZATION_EXPIRATION_DATE;
import static li.strolch.rest.filters.AuthenticationRequestFilter.getRemoteIp;
import static li.strolch.utils.helper.ExceptionHelper.getRootCause;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -61,11 +64,9 @@ public class AuthenticationResource {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response authenticate(@Context HttpServletRequest request, @Context HttpHeaders headers, String data) {
JsonObject login = JsonParser.parseString(data).getAsJsonObject();
try {
if (!login.has("username") || login.get("username").getAsString().length() < 2) {
logger.error("Authentication failed: Username was not given or is too short!");
JsonObject loginResult = new JsonObject();
@ -103,29 +104,8 @@ public class AuthenticationResource {
return getAuthenticationResponse(request, certificate, source, true);
} catch (InvalidCredentialsException e) {
logger.error("Authentication failed due to: " + e.getMessage());
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", "Could not log in as the given credentials are invalid");
return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build();
} catch (AccessDeniedException e) {
logger.error("Authentication failed due to: " + e.getMessage());
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg",
MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$
return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build();
} catch (StrolchException | PrivilegeException e) {
logger.error(e.getMessage(), e);
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg",
MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$
return Response.status(Status.FORBIDDEN).entity(loginResult.toString()).build();
} catch (Exception e) {
logger.error(e.getMessage(), e);
String msg = e.getMessage();
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", MessageFormat.format("{0}: {1}", e.getClass().getName(), msg));
return Response.serverError().entity(loginResult.toString()).build();
return handleAuthenticationException(e);
}
}
@ -133,38 +113,13 @@ public class AuthenticationResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("sso")
public Response authenticateSingleSignOn(@Context HttpServletRequest request, @Context HttpHeaders headers) {
try {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request);
Certificate certificate = sessionHandler.authenticateSingleSignOn(request.getUserPrincipal(), source);
return getAuthenticationResponse(request, certificate, source, true);
} catch (InvalidCredentialsException e) {
logger.error("Authentication failed due to: " + e.getMessage());
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", "Could not log in as the given credentials are invalid");
return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build();
} catch (AccessDeniedException e) {
logger.error("Authentication failed due to: " + e.getMessage());
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg",
MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$
return Response.status(Status.UNAUTHORIZED).entity(loginResult.toString()).build();
} catch (StrolchException | PrivilegeException e) {
logger.error(e.getMessage(), e);
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg",
MessageFormat.format("Could not log in due to: {0}", e.getMessage())); //$NON-NLS-2$
return Response.status(Status.FORBIDDEN).entity(loginResult.toString()).build();
} catch (Exception e) {
logger.error(e.getMessage(), e);
String msg = e.getMessage();
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", MessageFormat.format("{0}: {1}", e.getClass().getName(), msg));
return Response.serverError().entity(loginResult.toString()).build();
return handleAuthenticationException(e);
}
}
@ -177,7 +132,6 @@ public class AuthenticationResource {
JsonObject logoutResult = new JsonObject();
try {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request);
Certificate certificate = sessionHandler.validate(authToken, source);
@ -189,16 +143,8 @@ public class AuthenticationResource {
MessageFormat.format("{0} has been logged out.", certificate.getUsername()));
return Response.ok().entity(logoutResult.toString()).build();
} catch (StrolchException | PrivilegeException e) {
logger.error("Failed to invalidate session due to: " + e.getMessage());
logoutResult.addProperty("msg",
MessageFormat.format("Could not logout due to: {0}", e.getMessage())); //$NON-NLS-2$
return Response.status(Status.UNAUTHORIZED).entity(logoutResult.toString()).build();
} catch (Exception e) {
logger.error(e.getMessage(), e);
String msg = e.getMessage();
logoutResult.addProperty("msg", MessageFormat.format("{0}: {1}", e.getClass().getName(), msg));
return Response.serverError().entity(logoutResult.toString()).build();
return handleSessionException("Failed to invalidate session", e);
}
}
@ -207,58 +153,67 @@ public class AuthenticationResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("{authToken}")
public Response validateSession(@Context HttpServletRequest request, @PathParam("authToken") String authToken) {
try {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request);
sessionHandler.validate(authToken, source);
return Response.ok().build();
} catch (StrolchException | PrivilegeException e) {
logger.error("Session validation failed: " + e.getMessage());
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}", e.getMessage()));
String json = new Gson().toJson(root);
return Response.status(Status.UNAUTHORIZED).entity(json).build();
} catch (Exception e) {
logger.error(e.getMessage(), e);
String msg = e.getMessage();
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}: {1}", e.getClass().getName(), msg));
String json = new Gson().toJson(root);
return Response.serverError().entity(json).build();
return handleSessionException("Session validation failed", e);
}
}
private static Response handleAuthenticationException(Exception e) {
logger.error(e.getMessage(), e);
Status status;
Throwable rootCause = getRootCause(e);
String msg = MessageFormat.format("Could not log in due to: {0}", rootCause);
switch (rootCause) {
case InvalidCredentialsException ignored -> {
status = Status.UNAUTHORIZED;
msg = "Could not log in as the given credentials are invalid";
}
case AccessDeniedException ignored -> status = Status.UNAUTHORIZED;
case StrolchException ignored -> status = Status.FORBIDDEN;
case PrivilegeException ignored -> status = Status.FORBIDDEN;
default -> status = Status.INTERNAL_SERVER_ERROR;
}
JsonObject loginResult = new JsonObject();
loginResult.addProperty("msg", msg);
return Response.status(status).entity(loginResult.toString()).build();
}
private static Response handleSessionException(String context, Exception e) {
logger.error(e.getMessage(), e);
Throwable rootCause = getRootCause(e);
Status status;
String msg = MessageFormat.format("{0}: {1}", context, rootCause);
switch (rootCause) {
case InvalidCredentialsException ignored -> status = Status.UNAUTHORIZED;
case AccessDeniedException ignored -> status = Status.UNAUTHORIZED;
case StrolchException ignored -> status = Status.FORBIDDEN;
case PrivilegeException ignored -> status = Status.FORBIDDEN;
case null, default -> status = Status.INTERNAL_SERVER_ERROR;
}
JsonObject resultJ = new JsonObject();
resultJ.addProperty("msg", msg);
return Response.status(status).entity(resultJ.toString()).build();
}
@GET
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("{authToken}")
public Response getValidatedSession(@Context HttpServletRequest request, @PathParam("authToken") String authToken) {
try {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request);
Certificate certificate = sessionHandler.validate(authToken, source);
return getAuthenticationResponse(request, certificate, source, false);
} catch (StrolchException | PrivilegeException e) {
logger.error("Session validation failed: " + e.getMessage());
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}", e.getMessage()));
String json = new Gson().toJson(root);
return Response.status(Status.UNAUTHORIZED).entity(json).build();
} catch (Exception e) {
logger.error(e.getMessage(), e);
String msg = e.getMessage();
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}: {1}", e.getClass().getName(), msg));
String json = new Gson().toJson(root);
return Response.serverError().entity(json).build();
return handleSessionException("Session validation failed", e);
}
}
@ -267,30 +222,14 @@ public class AuthenticationResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("{authToken}")
public Response refreshSession(@Context HttpServletRequest request, @PathParam("authToken") String authToken) {
try {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request);
Certificate certificate = sessionHandler.validate(authToken, source);
Certificate refreshedCert = sessionHandler.refreshSession(certificate, source);
return getAuthenticationResponse(request, refreshedCert, source, true);
} catch (StrolchException | PrivilegeException e) {
logger.error("Session validation failed: " + e.getMessage());
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}", e.getMessage()));
String json = new Gson().toJson(root);
return Response.status(Status.UNAUTHORIZED).entity(json).build();
} catch (Exception e) {
logger.error(e.getMessage(), e);
String msg = e.getMessage();
JsonObject root = new JsonObject();
root.addProperty("msg", MessageFormat.format("Session invalid: {0}: {1}", e.getClass().getName(), msg));
String json = new Gson().toJson(root);
return Response.serverError().entity(json).build();
return handleSessionException("Session validation failed", e);
}
}
@ -298,7 +237,6 @@ public class AuthenticationResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("challenge")
public Response initiateChallenge(@Context HttpServletRequest request, String data) {
try {
JsonObject jsonObject = JsonParser.parseString(data).getAsJsonObject();
String username = jsonObject.get("username").getAsString();
@ -307,15 +245,9 @@ public class AuthenticationResource {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
String source = getRemoteIp(request);
sessionHandler.initiateChallengeFor(Usage.byValue(usage), username, source);
return ResponseUtil.toResponse();
} catch (PrivilegeException e) {
logger.error("Challenge initialization failed: " + e.getMessage());
JsonObject root = new JsonObject();
root.addProperty("msg", ExceptionHelper.getExceptionMessage(e));
String json = new Gson().toJson(root);
return Response.status(Status.UNAUTHORIZED).entity(json).build();
} catch (Exception e) {
return handleSessionException("Challenge initialization failed", e);
}
}
@ -323,7 +255,6 @@ public class AuthenticationResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("challenge")
public Response validateChallenge(@Context HttpServletRequest request, String data) {
try {
JsonObject jsonObject = JsonParser.parseString(data).getAsJsonObject();
@ -344,12 +275,8 @@ public class AuthenticationResource {
return setCookiesAndReturnResponse(request, restComponent, cookieMaxAge, expirationDate, result, authToken);
} catch (PrivilegeException e) {
logger.error("Challenge validation failed: " + e.getMessage());
JsonObject root = new JsonObject();
root.addProperty("msg", ExceptionHelper.getExceptionMessage(e));
String json = new Gson().toJson(root);
return Response.status(Status.UNAUTHORIZED).entity(json).build();
} catch (Exception e) {
return handleSessionException("Challenge validation failed", e);
}
}
@ -431,7 +358,8 @@ public class AuthenticationResource {
JsonObject loginResult, String authToken) {
boolean secureCookie = restComponent.isSecureCookie();
if (secureCookie && !request.getScheme().equals("https")) {
String msg = "Authorization cookie is secure, but connection is not secure! Cookie won't be passed to client!";
String msg
= "Authorization cookie is secure, but connection is not secure! Cookie won't be passed to client!";
logger.error(msg);
}