[Major] Refactored basic auth and getRemoteIp() helper
This commit is contained in:
parent
2aca456729
commit
a7258f0d06
|
@ -51,7 +51,7 @@ 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.rest.helper.RestfulHelper.getRemoteIp;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getRootCause;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.hasCause;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import li.strolch.privilege.model.Certificate;
|
|||
import li.strolch.privilege.model.Usage;
|
||||
import li.strolch.rest.RestfulStrolchComponent;
|
||||
import li.strolch.rest.StrolchRestfulConstants;
|
||||
import li.strolch.rest.helper.RestfulHelper;
|
||||
import li.strolch.runtime.sessions.StrolchSessionHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -112,9 +113,10 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext) {
|
||||
String remoteIp = getRemoteIp(this.request);
|
||||
logger.info("Remote IP: " + remoteIp + ": " + requestContext.getMethod() + " " +
|
||||
requestContext.getUriInfo().getRequestUri());
|
||||
String remoteIp = RestfulHelper.getRemoteIp(this.request);
|
||||
logger.info("Remote IP: " + remoteIp + ": " + requestContext.getMethod() + " " + requestContext
|
||||
.getUriInfo()
|
||||
.getRequestUri());
|
||||
|
||||
try {
|
||||
|
||||
|
@ -126,19 +128,25 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
|
||||
} catch (StrolchNotAuthenticatedException e) {
|
||||
logger.error(e.getMessage());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User is not authenticated!").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.UNAUTHORIZED)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User is not authenticated!")
|
||||
.build());
|
||||
} catch (StrolchAccessDeniedException e) {
|
||||
logger.error(e.getMessage());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.FORBIDDEN).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User is not authorized!").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.UNAUTHORIZED)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User is not authorized!")
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.FORBIDDEN).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User cannot access the resource.").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User cannot access the resource.")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,9 +217,11 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
if (isEmpty(sessionId)) {
|
||||
logger.error(
|
||||
"No Authorization header or cookie on request to URL " + requestContext.getUriInfo().getPath());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Missing Authorization!").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.UNAUTHORIZED)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Missing Authorization!")
|
||||
.build());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -223,9 +233,11 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
|
||||
if (!getRestful().isBasicAuthEnabled()) {
|
||||
logger.error("Basic Auth is not available for URL " + requestContext.getUriInfo().getPath());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.FORBIDDEN).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Basic Auth not available").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.FORBIDDEN)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Basic Auth not available")
|
||||
.build());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -233,9 +245,11 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
basicAuth = new String(Base64.getDecoder().decode(basicAuth.getBytes()), StandardCharsets.UTF_8);
|
||||
String[] parts = basicAuth.split(":");
|
||||
if (parts.length != 2) {
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.BAD_REQUEST).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Invalid Basic Authorization!").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Invalid Basic Authorization!")
|
||||
.build());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -256,10 +270,14 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
Certificate certificate = sessionHandler.validate(sessionId, remoteIp);
|
||||
|
||||
if (certificate.getUsage() == Usage.SET_PASSWORD) {
|
||||
if (!requestContext.getUriInfo().getMatchedURIs()
|
||||
if (!requestContext
|
||||
.getUriInfo()
|
||||
.getMatchedURIs()
|
||||
.contains("strolch/privilege/users/" + certificate.getUsername() + "/password")) {
|
||||
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN).entity("Can only set password!")
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.FORBIDDEN)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Can only set password!")
|
||||
.build());
|
||||
return null;
|
||||
}
|
||||
|
@ -269,27 +287,4 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
requestContext.setProperty(STROLCH_REQUEST_SOURCE, remoteIp);
|
||||
return certificate;
|
||||
}
|
||||
|
||||
public static String getRemoteIp(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
logger.error("HttpServletRequest NOT AVAILABLE! Probably running in TEST!");
|
||||
return "(null)";
|
||||
}
|
||||
|
||||
String remoteHost = request.getRemoteHost();
|
||||
String remoteAddr = request.getRemoteAddr();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (remoteHost.equals(remoteAddr))
|
||||
sb.append(remoteAddr);
|
||||
else {
|
||||
sb.append(remoteHost).append(": (").append(remoteAddr).append(")");
|
||||
}
|
||||
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (isNotEmpty(xForwardedFor))
|
||||
sb.append(" (fwd)=> ").append(xForwardedFor);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package li.strolch.rest.helper;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import li.strolch.exception.StrolchAccessDeniedException;
|
||||
import li.strolch.exception.StrolchNotAuthenticatedException;
|
||||
import li.strolch.privilege.model.Certificate;
|
||||
import li.strolch.privilege.model.Usage;
|
||||
import li.strolch.runtime.sessions.StrolchSessionHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static li.strolch.utils.helper.StringHelper.isEmpty;
|
||||
|
||||
public class BasicAuth {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BasicAuth.class);
|
||||
|
||||
private StrolchSessionHandler sessionHandler;
|
||||
|
||||
public BasicAuth(StrolchSessionHandler sessionHandler) {
|
||||
this.sessionHandler = sessionHandler;
|
||||
}
|
||||
|
||||
public Certificate doBasicAuth(String authorization, String remoteIp) throws BasicAuthFailure {
|
||||
if (isEmpty(authorization))
|
||||
throw new BasicAuthFailure(Response.Status.UNAUTHORIZED, "Missing auth!");
|
||||
if (!authorization.startsWith("Basic "))
|
||||
throw new BasicAuthFailure(Response.Status.BAD_REQUEST, "Bad request!");
|
||||
|
||||
try {
|
||||
String auth = new String(Base64.getDecoder().decode(authorization.substring(6)), UTF_8);
|
||||
int splitIndex = auth.indexOf(':');
|
||||
String username = auth.substring(0, splitIndex);
|
||||
String password = auth.substring(splitIndex + 1);
|
||||
|
||||
return this.sessionHandler.authenticate(username, password.toCharArray(), remoteIp, Usage.SINGLE, false);
|
||||
|
||||
} catch (StrolchNotAuthenticatedException e) {
|
||||
logger.error(e.getMessage());
|
||||
throw new BasicAuthFailure(Response.Status.UNAUTHORIZED, "Not authenticated!", e);
|
||||
} catch (StrolchAccessDeniedException e) {
|
||||
logger.error(e.getMessage());
|
||||
throw new BasicAuthFailure(Response.Status.UNAUTHORIZED, "User is not authorized!", e);
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
throw new BasicAuthFailure(Response.Status.INTERNAL_SERVER_ERROR, "Internal error", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package li.strolch.rest.helper;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class BasicAuthFailure extends Exception {
|
||||
|
||||
private final Response.Status status;
|
||||
private final String reason;
|
||||
|
||||
public BasicAuthFailure(Response.Status status, String reason) {
|
||||
this.status = status;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public BasicAuthFailure(Response.Status status, String reason, Exception cause) {
|
||||
super(cause);
|
||||
this.status = status;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public Response.Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
|
@ -15,17 +15,10 @@
|
|||
*/
|
||||
package li.strolch.rest.helper;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
import li.strolch.model.visitor.StrolchRootElementVisitor;
|
||||
import li.strolch.privilege.model.Certificate;
|
||||
|
@ -36,16 +29,28 @@ import li.strolch.search.SearchResult;
|
|||
import li.strolch.utils.collections.Paging;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
public class RestfulHelper {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RestfulHelper.class);
|
||||
|
||||
public static Locale getLocale(HttpHeaders headers) {
|
||||
if (headers == null || StringHelper.isEmpty(headers.getHeaderString(HttpHeaders.ACCEPT_LANGUAGE)))
|
||||
return null;
|
||||
return headers.getAcceptableLanguages().get(0);
|
||||
return headers.getAcceptableLanguages().getFirst();
|
||||
}
|
||||
|
||||
public static Certificate getCert(HttpServletRequest request) {
|
||||
|
@ -105,4 +110,27 @@ public class RestfulHelper {
|
|||
root.add("data", data);
|
||||
return root;
|
||||
}
|
||||
|
||||
public static String getRemoteIp(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
logger.error("HttpServletRequest NOT AVAILABLE! Probably running in TEST!");
|
||||
return "(null)";
|
||||
}
|
||||
|
||||
String remoteHost = request.getRemoteHost();
|
||||
String remoteAddr = request.getRemoteAddr();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (remoteHost.equals(remoteAddr))
|
||||
sb.append(remoteAddr);
|
||||
else {
|
||||
sb.append(remoteHost).append(": (").append(remoteAddr).append(")");
|
||||
}
|
||||
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (isNotEmpty(xForwardedFor))
|
||||
sb.append(" (fwd)=> ").append(xForwardedFor);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package li.strolch.websocket;
|
||||
|
||||
import static li.strolch.rest.filters.AuthenticationRequestFilter.getRemoteIp;
|
||||
import static li.strolch.rest.helper.RestfulHelper.getRemoteIp;
|
||||
|
||||
import jakarta.servlet.annotation.WebFilter;
|
||||
|
||||
|
|
Loading…
Reference in New Issue