[New] Implemented Basic authentication for REST APIs

This commit is contained in:
Robert von Burg 2020-04-23 10:06:55 +02:00
parent 984f6bff41
commit 0481ecc90d
2 changed files with 57 additions and 16 deletions

View File

@ -26,7 +26,8 @@ import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.*; import javax.ws.rs.core.*;
import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.Provider;
import java.io.IOException; import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -34,6 +35,7 @@ import java.util.Set;
import li.strolch.exception.StrolchAccessDeniedException; import li.strolch.exception.StrolchAccessDeniedException;
import li.strolch.exception.StrolchNotAuthenticatedException; import li.strolch.exception.StrolchNotAuthenticatedException;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.Usage;
import li.strolch.rest.RestfulStrolchComponent; import li.strolch.rest.RestfulStrolchComponent;
import li.strolch.rest.StrolchRestfulConstants; import li.strolch.rest.StrolchRestfulConstants;
import li.strolch.rest.StrolchSessionHandler; import li.strolch.rest.StrolchSessionHandler;
@ -103,7 +105,7 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
} }
@Override @Override
public void filter(ContainerRequestContext requestContext) throws IOException { public void filter(ContainerRequestContext requestContext) {
String remoteIp = getRemoteIp(this.request); String remoteIp = getRemoteIp(this.request);
logger.info("Remote IP: " + remoteIp + ": " + requestContext.getMethod() + " " + requestContext.getUriInfo() logger.info("Remote IP: " + remoteIp + ": " + requestContext.getMethod() + " " + requestContext.getUriInfo()
.getRequestUri()); .getRequestUri());
@ -152,8 +154,8 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
*/ */
protected Certificate validateSession(ContainerRequestContext requestContext, String remoteIp) { protected Certificate validateSession(ContainerRequestContext requestContext, String remoteIp) {
String sessionId = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); String authorization = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
if (StringHelper.isEmpty(sessionId)) { if (StringHelper.isEmpty(authorization)) {
Cookie cookie = requestContext.getCookies().get(StrolchRestfulConstants.STROLCH_AUTHORIZATION); Cookie cookie = requestContext.getCookies().get(StrolchRestfulConstants.STROLCH_AUTHORIZATION);
if (cookie == null) { if (cookie == null) {
@ -166,7 +168,7 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
return null; return null;
} }
sessionId = cookie.getValue(); String sessionId = cookie.getValue();
if (StringHelper.isEmpty(sessionId)) { if (StringHelper.isEmpty(sessionId)) {
logger.error("Authorization Cookie value missing on request to URL " + requestContext.getUriInfo() logger.error("Authorization Cookie value missing on request to URL " + requestContext.getUriInfo()
.getPath()); .getPath());
@ -176,11 +178,34 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
.build()); .build());
return null; return null;
} }
return validateCertificate(requestContext, sessionId, remoteIp);
} }
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance() authorization = authorization.trim();
.getComponent(StrolchSessionHandler.class); if (authorization.startsWith("Basic ")) {
Certificate certificate = sessionHandler.validate(sessionId, remoteIp); return authenticateBasic(requestContext, authorization, remoteIp);
}
return validateCertificate(requestContext, authorization, remoteIp);
}
private Certificate authenticateBasic(ContainerRequestContext requestContext, String authorization,
String remoteIp) {
String basicAuth = authorization.substring("Basic ".length());
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!") //$NON-NLS-1$
.build());
return null;
}
logger.info("Performing basic auth for user " + parts[0] + "...");
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
Certificate certificate = sessionHandler.authenticate(parts[0], parts[1].toCharArray(), remoteIp, Usage.SINGLE);
requestContext.setProperty(STROLCH_CERTIFICATE, certificate); requestContext.setProperty(STROLCH_CERTIFICATE, certificate);
requestContext.setProperty(STROLCH_REQUEST_SOURCE, remoteIp); requestContext.setProperty(STROLCH_REQUEST_SOURCE, remoteIp);
@ -188,6 +213,15 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
return certificate; return certificate;
} }
private Certificate validateCertificate(ContainerRequestContext requestContext, String sessionId, String remoteIp) {
StrolchSessionHandler sessionHandler = RestfulStrolchComponent.getInstance().getSessionHandler();
Certificate certificate = sessionHandler.validate(sessionId, remoteIp);
requestContext.setProperty(STROLCH_CERTIFICATE, certificate);
requestContext.setProperty(STROLCH_REQUEST_SOURCE, remoteIp);
return certificate;
}
public static String getRemoteIp(HttpServletRequest request) { public static String getRemoteIp(HttpServletRequest request) {
String remoteHost = request.getRemoteHost(); String remoteHost = request.getRemoteHost();

View File

@ -1,12 +1,12 @@
/* /*
* Copyright 2015 Robert von Burg <eitch@eitchnet.ch> * Copyright 2015 Robert von Burg <eitch@eitchnet.ch>
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,15 +17,16 @@ package li.strolch.rest.filters;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_CERTIFICATE; import static li.strolch.rest.StrolchRestfulConstants.STROLCH_CERTIFICATE;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.Provider;
import java.io.IOException;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.rest.RestfulStrolchComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* @author Reto Breitenmoser <reto.breitenmoser@4trees.ch> * @author Reto Breitenmoser <reto.breitenmoser@4trees.ch>
@ -34,13 +35,19 @@ import li.strolch.privilege.model.Certificate;
@Provider @Provider
public class AuthenticationResponseFilter implements ContainerResponseFilter { public class AuthenticationResponseFilter implements ContainerResponseFilter {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationResponseFilter.class);
@Override @Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException { throws IOException {
Certificate cert = (Certificate) requestContext.getProperty(STROLCH_CERTIFICATE); Certificate cert = (Certificate) requestContext.getProperty(STROLCH_CERTIFICATE);
if (cert != null) { if (cert == null)
responseContext.getHeaders().add(HttpHeaders.AUTHORIZATION, cert.getAuthToken()); return;
if (cert.getUsage().isSingle()) {
logger.info("Invalidating single usage certificate for " + cert.getUsername());
RestfulStrolchComponent.getInstance().getSessionHandler().invalidate(cert);
} }
} }
} }