From a9c0215f4bf068d23ab85c786147ab6766a7c7f6 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 16 Feb 2026 18:49:08 +0530 Subject: [PATCH] oauth fix Signed-off-by: Abhishek Kumar --- .../veeam/filter/BearerOrBasicAuthFilter.java | 68 +++++++++---------- .../cloudstack/veeam/sso/SsoService.java | 3 + 2 files changed, 34 insertions(+), 37 deletions(-) diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/filter/BearerOrBasicAuthFilter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/filter/BearerOrBasicAuthFilter.java index 62b6f319b31..511e89ec68c 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/filter/BearerOrBasicAuthFilter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/filter/BearerOrBasicAuthFilter.java @@ -22,6 +22,7 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Base64; import java.util.List; +import java.util.Map; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; @@ -36,6 +37,10 @@ import javax.servlet.http.HttpServletResponse; import org.apache.cloudstack.veeam.VeeamControlService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + public class BearerOrBasicAuthFilter implements Filter { // Keep these aligned with SsoService (move to ConfigKeys later) @@ -43,6 +48,8 @@ public class BearerOrBasicAuthFilter implements Filter { public static final String ISSUER = "veeam-control"; public static final String HMAC_SECRET = "change-this-super-secret-key-change-this"; + private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); + @Override public void init(FilterConfig filterConfig) {} @Override public void destroy() {} @@ -136,20 +143,35 @@ public class BearerOrBasicAuthFilter implements Filter { if (!constantTimeEquals(expectedSig, providedSig)) return false; - final String payloadJson; + Map payloadMap; try { - payloadJson = new String(Base64.getUrlDecoder().decode(payloadB64), StandardCharsets.UTF_8); - } catch (IllegalArgumentException e) { + String payloadJson = new String(Base64.getUrlDecoder().decode(payloadB64), StandardCharsets.UTF_8); + payloadMap = JSON_MAPPER.readValue( + payloadJson, + new TypeReference<>() {} + ); + } catch (IllegalArgumentException | JsonProcessingException e) { return false; } - // Super small “claims” extraction (good enough for our minting format) - final String iss = JsonMini.getString(payloadJson, "iss"); - final String scope = JsonMini.getString(payloadJson, "scope"); - final Long exp = JsonMini.getLong(payloadJson, "exp"); + final String iss = (String)payloadMap.get("iss"); + final String scope = (String)payloadMap.get("scope"); + final Object expObj = payloadMap.get("exp"); + Long exp = null; + if (expObj instanceof Number) { + exp = ((Number) expObj).longValue(); + } else if (expObj instanceof String) { + try { + exp = Long.parseLong((String) expObj); + } catch (NumberFormatException ignored) {} + } - if (!ISSUER.equals(iss)) return false; - if (exp == null || Instant.now().getEpochSecond() >= exp) return false; + if (!ISSUER.equals(iss)) { + return false; + } + if (exp == null || Instant.now().getEpochSecond() >= exp) { + return false; + } return scope != null && hasRequiredScopes(scope); } @@ -216,32 +238,4 @@ public class BearerOrBasicAuthFilter implements Filter { for (int i = 0; i < x.length; i++) r |= x[i] ^ y[i]; return r == 0; } - - // Tiny JSON extractor for flat string/number claims. Good enough for tokens you mint. - static final class JsonMini { - static String getString(String json, String key) { - final String needle = "\"" + key + "\":"; - int i = json.indexOf(needle); - if (i < 0) return null; - i += needle.length(); - while (i < json.length() && Character.isWhitespace(json.charAt(i))) i++; - if (i >= json.length() || json.charAt(i) != '"') return null; - i++; - int j = json.indexOf('"', i); - if (j < 0) return null; - return json.substring(i, j); - } - - static Long getLong(String json, String key) { - final String needle = "\"" + key + "\":"; - int i = json.indexOf(needle); - if (i < 0) return null; - i += needle.length(); - while (i < json.length() && Character.isWhitespace(json.charAt(i))) i++; - int j = i; - while (j < json.length() && (Character.isDigit(json.charAt(j)))) j++; - if (j == i) return null; - return Long.parseLong(json.substring(i, j)); - } - } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/sso/SsoService.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/sso/SsoService.java index 26a29d6d531..a402b88ab76 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/sso/SsoService.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/sso/SsoService.java @@ -101,6 +101,8 @@ public class SsoService extends ManagerBase implements RouteHandler { final String effectiveScope = (scope == null) ? "ovirt-app-api" : scope; final long ttl = DEFAULT_TTL_SECONDS; + long nowMillis = Instant.now().toEpochMilli(); + long expMillis = nowMillis + ttl * 1000L; final String token; try { token = JwtUtil.issueHs256Jwt(BearerOrBasicAuthFilter.ISSUER, username, effectiveScope, ttl, @@ -115,6 +117,7 @@ public class SsoService extends ManagerBase implements RouteHandler { payload.put("access_token", token); payload.put("token_type", "bearer"); payload.put("expires_in", ttl); + payload.put("exp", expMillis); payload.put("scope", effectiveScope); io.getWriter().write(resp, HttpServletResponse.SC_OK, payload, outFormat);