This is an automated email from the ASF dual-hosted git repository.
bossenti pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new db41c7d35 feat(#2269): Add basic auth to prometheus monitoring
endpoint (#2270)
db41c7d35 is described below
commit db41c7d3506b9d5cae6363399ed723b07b8ebd42
Author: Dominik Riemer <[email protected]>
AuthorDate: Thu Dec 7 12:30:56 2023 +0100
feat(#2269): Add basic auth to prometheus monitoring endpoint (#2270)
* feat(#2269): Add basic auth to prometheus monitoring endpoint
* Use constants for auth
* refactor: rename variable
---------
Co-authored-by: bossenti <[email protected]>
---
.../commons/constants/HttpConstants.java | 1 +
.../security/UnauthorizedRequestEntryPoint.java | 2 +-
.../core/filter/TokenAuthenticationFilter.java | 44 +++++++++++++++++++++-
3 files changed, 45 insertions(+), 2 deletions(-)
diff --git
a/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
b/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
index 63dece4bb..9866f10e8 100644
---
a/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
+++
b/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
@@ -26,5 +26,6 @@ public class HttpConstants {
public static final String APPLICATION_JSON_TYPE = "application/json";
public static final String X_API_USER = "X-API-USER";
public static final String X_API_KEY = "X-API-KEY";
+ public static final String BASIC = "Basic ";
}
diff --git
a/streampipes-service-base/src/main/java/org/apache/streampipes/service/base/security/UnauthorizedRequestEntryPoint.java
b/streampipes-service-base/src/main/java/org/apache/streampipes/service/base/security/UnauthorizedRequestEntryPoint.java
index 144a990dc..94304a6d5 100644
---
a/streampipes-service-base/src/main/java/org/apache/streampipes/service/base/security/UnauthorizedRequestEntryPoint.java
+++
b/streampipes-service-base/src/main/java/org/apache/streampipes/service/base/security/UnauthorizedRequestEntryPoint.java
@@ -35,7 +35,7 @@ public class UnauthorizedRequestEntryPoint implements
AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException {
- LOG.error("Unauthorized request to {}", httpServletRequest.getPathInfo());
+ LOG.error("Unauthorized request to {}",
httpServletRequest.getServletPath());
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
e.getLocalizedMessage());
}
diff --git
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
index e389f4afb..ac4c83359 100644
---
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
+++
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
@@ -24,15 +24,18 @@ import
org.apache.streampipes.model.client.user.ServiceAccount;
import org.apache.streampipes.model.client.user.UserAccount;
import org.apache.streampipes.storage.api.IUserStorage;
import org.apache.streampipes.storage.management.StorageDispatcher;
+import
org.apache.streampipes.user.management.encryption.SecretEncryptionManager;
import org.apache.streampipes.user.management.jwt.JwtTokenProvider;
import org.apache.streampipes.user.management.model.PrincipalUserDetails;
import org.apache.streampipes.user.management.model.ServiceAccountDetails;
import org.apache.streampipes.user.management.model.UserAccountDetails;
import org.apache.streampipes.user.management.service.TokenService;
+import org.apache.streampipes.user.management.util.PasswordUtil;
import org.apache.streampipes.user.management.util.TokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import
org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
@@ -45,12 +48,20 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Base64;
+import java.util.List;
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider;
private final IUserStorage userStorage;
+ private final List<String> supportedBasicAuthPaths = List.of(
+ "/actuator/prometheus"
+ );
+
private static final Logger logger =
LoggerFactory.getLogger(TokenAuthenticationFilter.class);
public TokenAuthenticationFilter() {
@@ -68,7 +79,7 @@ public class TokenAuthenticationFilter extends
OncePerRequestFilter {
if (StringUtils.hasText(jwt) && tokenProvider.validateJwtToken(jwt)) {
String username = tokenProvider.getUserIdFromToken(jwt);
applySuccessfulAuth(request, username);
- } else {
+ } else if (isApiKeyAuth(request)) {
String apiKey = getApiKeyFromRequest(request);
String apiUser = getApiUserFromRequest(request);
if (StringUtils.hasText(apiKey) && StringUtils.hasText(apiUser)) {
@@ -78,6 +89,22 @@ public class TokenAuthenticationFilter extends
OncePerRequestFilter {
applySuccessfulAuth(request, apiUser);
}
}
+ } else {
+ var authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
+ if (authorizationHeader != null &&
authorizationHeader.startsWith(HttpConstants.BASIC)) {
+ if (supportedBasicAuthPaths.contains(request.getServletPath())) {
+ String base64Credentials =
authorizationHeader.substring(HttpConstants.BASIC.length()).trim();
+ String credentials = new
String(Base64.getDecoder().decode(base64Credentials));
+
+ String[] splitCredentials = credentials.split(":");
+ String username = splitCredentials[0];
+ String passphrase = splitCredentials[1];
+ var principal =
StorageDispatcher.INSTANCE.getNoSqlStore().getUserStorageAPI().getUser(username);
+ if (principal != null && checkCredentials(principal, passphrase)) {
+ applySuccessfulAuth(request, username);
+ }
+ }
+ }
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context",
ex);
@@ -86,6 +113,21 @@ public class TokenAuthenticationFilter extends
OncePerRequestFilter {
filterChain.doFilter(request, response);
}
+ private boolean checkCredentials(Principal principal, String passphrase)
+ throws NoSuchAlgorithmException, InvalidKeySpecException {
+ if (principal instanceof UserAccount) {
+ return PasswordUtil.validatePassword(passphrase, ((UserAccount)
principal).getPassword());
+ } else if (principal instanceof ServiceAccount) {
+ return
passphrase.equals(SecretEncryptionManager.decrypt(((ServiceAccount)
principal).getClientSecret()));
+ } else {
+ throw new IllegalArgumentException("Unknown user instance");
+ }
+ }
+
+ private boolean isApiKeyAuth(HttpServletRequest request) {
+ return request.getHeader(HttpConstants.X_API_USER) != null &&
request.getHeader(HttpConstants.X_API_KEY) != null;
+ }
+
private void applySuccessfulAuth(HttpServletRequest request,
String username) {
Principal user = userStorage.getUser(username);