Skip to content

Commit

Permalink
feat(#2269): Add basic auth to prometheus monitoring endpoint (#2270)
Browse files Browse the repository at this point in the history
* feat(#2269): Add basic auth to prometheus monitoring endpoint

* Use constants for auth

* refactor: rename variable

---------

Co-authored-by: bossenti <[email protected]>
  • Loading branch information
dominikriemer and bossenti authored Dec 7, 2023
1 parent 5afa814 commit db41c7d
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 ";

}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@
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;
Expand All @@ -45,12 +48,20 @@
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() {
Expand All @@ -68,7 +79,7 @@ protected void doFilterInternal(HttpServletRequest request,
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)) {
Expand All @@ -78,6 +89,22 @@ protected void doFilterInternal(HttpServletRequest request,
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);
Expand All @@ -86,6 +113,21 @@ protected void doFilterInternal(HttpServletRequest request,
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);
Expand Down

0 comments on commit db41c7d

Please sign in to comment.