Skip to content

Commit

Permalink
Merge pull request #2612 from digma-ai/jaeger-auth-support
Browse files Browse the repository at this point in the history
Jaeger auth support
  • Loading branch information
asafchen-dig authored Nov 27, 2024
2 parents c1697b5 + 4d3640e commit 27e1739
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public interface JaegerUIConstants {
String JAEGER_UI_DOMAIN_NAME = "jaegerui";
String JAEGER_UI_SCHEMA = "http";
String JAEGER_UI_URL = JAEGER_UI_SCHEMA + "://" + JAEGER_UI_DOMAIN_NAME + "/index.html";
String JAEGER_API_URL = JAEGER_UI_SCHEMA + "://" + JAEGER_UI_DOMAIN_NAME;
String JAEGER_UI_RESOURCE_FOLDER_NAME = "/webview/jaegerui";
String JAEGER_UI_INDEX_TEMPLATE_NAME = "jaegeruitemplate.ftl";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

public class JaegerUiIndexTemplateBuilder extends BaseIndexTemplateBuilder {

private static final String JAEGER_URL_PARAM_NAME = "jaeger_url";
private static final String JAEGER_URL_FOR_LINK_PARAM_NAME = "BASE_URL_STRING_TO_OPEN_LINKS";
private static final String JAEGER_URL_FOR_API_PARAM_NAME = "BASE_URL_STRING_TO_FORWARD_API_CALLS";
private static final String JAEGER_QUERY_URL_CHANGED_FROM_DEFAULT_PARAM_NAME = "isUserChangedJaegerQueryUrl";
private static final String INITIAL_ROUTE_PARAM_NAME = "initial_route";

Expand All @@ -27,7 +28,8 @@ public JaegerUiIndexTemplateBuilder(JaegerUIVirtualFile file) {
public void addAppSpecificEnvVariable(@NotNull Project project, @NotNull Map<String, Object> data) {

var didUserChangeJaegerQueryUrl = !(SettingsState.DEFAULT_JAEGER_QUERY_URL.equalsIgnoreCase(SettingsState.getInstance().getJaegerQueryUrl()));
data.put(JAEGER_URL_PARAM_NAME, jaegerUIVirtualFile.getJaegerBaseUrl());
data.put(JAEGER_URL_FOR_LINK_PARAM_NAME, jaegerUIVirtualFile.getJaegerBaseUrl());
data.put(JAEGER_URL_FOR_API_PARAM_NAME, JaegerUIConstants.JAEGER_API_URL);
data.put(JAEGER_QUERY_URL_CHANGED_FROM_DEFAULT_PARAM_NAME, String.valueOf(didUserChangeJaegerQueryUrl));


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package org.digma.intellij.plugin.jaegerui;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.intellij.openapi.diagnostic.Logger;
import com.posthog.java.shaded.okhttp3.*;
import org.cef.callback.CefCallback;
import org.cef.handler.CefResourceHandler;
import org.cef.misc.*;
import org.cef.network.*;
import org.digma.intellij.plugin.auth.account.CredentialsHolder;
import org.digma.intellij.plugin.common.JsonUtilsKt;
import org.digma.intellij.plugin.log.Log;
import org.digma.intellij.plugin.ui.jcef.JCefException;
import org.jetbrains.annotations.*;

import java.net.*;
import java.util.*;
import java.util.stream.Collectors;

public class JaegerUiProxyResourceHandler implements CefResourceHandler {

private static final Logger LOGGER = Logger.getInstance(JaegerUiProxyResourceHandler.class);
private final OkHttpClient okHttpClient;
private final URL jaegerQueryUrl;
private Response okHttp3Response;

public JaegerUiProxyResourceHandler(URL jaegerQueryUrl){
this.jaegerQueryUrl = jaegerQueryUrl;
okHttpClient = new OkHttpClient.Builder().build();
}

public static boolean isJaegerQueryCall(URL url) {
return url.getPath().startsWith("/api/");
}

@Override
public boolean processRequest(CefRequest cefRequest, CefCallback callback) {
try {
var apiUrl = getApiUrl(cefRequest);
var headers = getHeaders(cefRequest);
var body = getBody(cefRequest, headers);
var okHttp3Request = new Request.Builder()
.method(cefRequest.getMethod(), body)
.headers(Headers.of(headers))
.url(apiUrl)
.build();
okHttp3Response = okHttpClient.newCall(okHttp3Request).execute();
var authFailureReason = getAuthFailureReason(okHttp3Response);
if (authFailureReason != null && !authFailureReason.isBlank()) {
okHttp3Response = buildAuthFailureResponse(okHttp3Request, okHttp3Response, authFailureReason);
}
callback.Continue();
return true;
} catch (Exception e) {
Log.warnWithException(LOGGER, e, "processRequest failed");
callback.cancel();
return false;
}
}

@Nullable
private static String getAuthFailureReason(Response response){
final var headerName = "X-Auth-Fail-Reason";
var reason = response.header(headerName);
if(reason == null && response.priorResponse() != null)
reason = response.priorResponse().header(headerName);
return reason;
}

private static Response buildAuthFailureResponse(Request okHttp3Request, Response response, String authFailureReason) {
return new Response(
okHttp3Request,
response.protocol(),
"Authentication failed", 401,
response.handshake(),
Headers.of(),
ResponseBody.create(
"Authentication failed: " + authFailureReason,
MediaType.parse("text/html")
),
null, null, null,
0, 0, null);
}

@NotNull
private URL getApiUrl(CefRequest cefRequest) throws MalformedURLException {
var requestUrl = new URL(cefRequest.getURL());
return new URL(jaegerQueryUrl.getProtocol(), jaegerQueryUrl.getHost(), jaegerQueryUrl.getPort(),
requestUrl.getPath() + "?" + requestUrl.getQuery());
}

@NotNull
private static HashMap<String, String> getHeaders(CefRequest cefRequest) throws JsonProcessingException {
var headers = new HashMap<String, String>();
cefRequest.getHeaderMap(headers);
var digmaCredentials = CredentialsHolder.INSTANCE.getDigmaCredentials();
if (digmaCredentials != null){
var digmaCredentialsJson = JsonUtilsKt.objectToJson(digmaCredentials);
headers.put("Cookie", "auth_token="+digmaCredentialsJson);
}
return headers;
}

@Nullable
private static RequestBody getBody(CefRequest cefRequest, HashMap<String, String> headers) {
return cefRequest.getPostData() != null
? RequestBody.create(cefRequest.getPostData().toString(), MediaType.parse(headers.get("Content-Type")))
: null;
}

@Override
public void getResponseHeaders(CefResponse cefResponse, IntRef responseLength, StringRef redirectUrl) {
if (okHttp3Response == null)
return;

cefResponse.setStatus(okHttp3Response.code());
var headersMap = okHttp3Response.headers().names().stream().collect(Collectors.toMap(s -> s, okHttp3Response::header));
cefResponse.setHeaderMap(headersMap);

var body = okHttp3Response.body();
if (body != null){
cefResponse.setMimeType(body.contentType().toString());
responseLength.set((int)body.contentLength());
}
}

@Override
public boolean readResponse(byte[] dataOut, int bytesToRead, IntRef bytesRead, CefCallback cefCallback) {
try{
var inputStream = okHttp3Response.body().byteStream();
var read = inputStream.read(dataOut, 0, bytesToRead);
if (read == -1) {
bytesRead.set(0);
inputStream.close();
return false;
}
bytesRead.set(read);
return true;
} catch (Exception e) {
Log.warnWithException(LOGGER, e, "exception readResponse");
throw new JCefException(e);
}
}

@Override
public void cancel() {
okHttp3Response.close();
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
package org.digma.intellij.plugin.jaegerui;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import org.cef.browser.CefBrowser;
import org.cef.handler.CefResourceHandler;
import org.digma.intellij.plugin.log.Log;
import org.digma.intellij.plugin.settings.SettingsState;
import org.digma.intellij.plugin.ui.jcef.BaseSchemeHandlerFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.*;

import java.net.*;

import static org.digma.intellij.plugin.jaegerui.JaegerUIConstants.JAEGER_UI_SCHEMA;

public class JaegerUiSchemeHandlerFactory extends BaseSchemeHandlerFactory {

private static final Logger LOGGER = Logger.getInstance(JaegerUiSchemeHandlerFactory.class);

@Nullable
@Override
public CefResourceHandler createProxyHandler(@NotNull Project project, @NotNull URL url) {
var jaegerQueryUrl = GetJaegerQueryUrlOrNull();
if (jaegerQueryUrl != null &&
JaegerUiProxyResourceHandler.isJaegerQueryCall(url)) {
return new JaegerUiProxyResourceHandler(jaegerQueryUrl);
}
return null;
}

@NotNull
@Override
Expand Down Expand Up @@ -37,4 +55,17 @@ public String getDomain() {
public String getResourceFolderName() {
return JaegerUIConstants.JAEGER_UI_RESOURCE_FOLDER_NAME;
}

private static URL GetJaegerQueryUrlOrNull(){
var urlStr = SettingsState.getInstance().getJaegerQueryUrl();
if(urlStr == null)
return null;

try {
return new URL(urlStr);
} catch (MalformedURLException e) {
Log.warnWithException(LOGGER, e, "JaegerQueryUrl parsing failed");
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.digma.intellij.plugin.ui.jcef

import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import org.cef.browser.CefBrowser
import org.cef.browser.CefFrame
import org.cef.callback.CefSchemeHandlerFactory
Expand Down Expand Up @@ -39,16 +40,14 @@ abstract class BaseSchemeHandlerFactory : CefSchemeHandlerFactory {
val url = getUrl(request)

if (url != null) {

val host = url.host
val file = url.file


if (ApiProxyResourceHandler.isApiProxyCall(url)) {
return ApiProxyResourceHandler(project)
val proxyHandler = createProxyHandler(project, url)
if (proxyHandler != null) {
return proxyHandler
}


if (getDomain() == host && getSchema() == schemeName) {
var resourceName = getResourceFolderName() + file
var resource = javaClass.getResource(resourceName)
Expand All @@ -73,7 +72,9 @@ abstract class BaseSchemeHandlerFactory : CefSchemeHandlerFactory {
}
}


protected open fun createProxyHandler(project: Project, url: URL): CefResourceHandler?{
return null
}
abstract fun createResourceHandler(resourceName: String, resourceExists: Boolean, browser: CefBrowser): CefResourceHandler
abstract fun getSchema(): String
abstract fun getDomain(): String
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package org.digma.intellij.plugin.ui.mainapp

import com.intellij.openapi.project.Project
import org.cef.browser.CefBrowser
import org.cef.handler.CefResourceHandler
import org.digma.intellij.plugin.ui.jcef.ApiProxyResourceHandler
import org.digma.intellij.plugin.ui.jcef.BaseSchemeHandlerFactory
import java.net.URL

class MainAppSchemeHandlerFactory : BaseSchemeHandlerFactory() {
override fun createProxyHandler(project: Project, url: URL): CefResourceHandler? {
if (ApiProxyResourceHandler.isApiProxyCall(url)) {
return ApiProxyResourceHandler(project)
}
return null
}

override fun createResourceHandler(resourceName: String, resourceExists: Boolean, browser: CefBrowser): CefResourceHandler {
return if (resourceExists) {
Expand Down
5 changes: 3 additions & 2 deletions src/main/resources/webview/jaegerui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
window.global = {};
</script>
<script>
window.baseUrl;
window.apiBaseUrl;
window.initialRoutePath;
window.embeddedMode;
Expand All @@ -48,7 +49,7 @@
window.platform;
window.isLoggingEnabled;
</script>
<script type="module" crossorigin src="./static/index-7d2ffb6b.js"></script>
<script type="module" crossorigin src="./static/index-a01b010b.js"></script>
<link rel="stylesheet" href="./static/index-3c425a02.css">
<script type="module">import.meta.url;import("_").catch(()=>1);async function* g(){};window.__vite_is_modern_browser=true;</script>
<script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy chunks, syntax error above and the same error below should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script>
Expand All @@ -63,6 +64,6 @@

<script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
<script nomodule crossorigin id="vite-legacy-polyfill" src="./static/polyfills-legacy-9486af1f.js"></script>
<script nomodule crossorigin id="vite-legacy-entry" data-src="./static/index-legacy-1ca9c6f5.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
<script nomodule crossorigin id="vite-legacy-entry" data-src="./static/index-legacy-7a251ba3.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
</body>
</html>
8 changes: 4 additions & 4 deletions src/main/resources/webview/jaegerui/jaegeruitemplate.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@
</script>
<script>
@GLOBAL_ENV_VARS@
window.apiBaseUrl = "${jaeger_url}";
window.baseUrl = "${BASE_URL_STRING_TO_OPEN_LINKS}";
window.apiBaseUrl = "${BASE_URL_STRING_TO_FORWARD_API_CALLS}";
window.initialRoutePath = "${initial_route}";
window.embeddedMode = true;
window.isUserDefinedJaegerQueryURL = ${isUserChangedJaegerQueryUrl};
window.staticPath;
window.enableZoomControls = true;
</script>
<script type="module" crossorigin src="./static/index-7d2ffb6b.js"></script>
<script type="module" crossorigin src="./static/index-a01b010b.js"></script>
<link rel="stylesheet" href="./static/index-3c425a02.css">
<script type="module">import.meta.url;import("_").catch(()=>1);async function* g(){};window.__vite_is_modern_browser=true;</script>
<script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy chunks, syntax error above and the same error below should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script>
Expand All @@ -63,6 +63,6 @@

<script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
<script nomodule crossorigin id="vite-legacy-polyfill" src="./static/polyfills-legacy-9486af1f.js"></script>
<script nomodule crossorigin id="vite-legacy-entry" data-src="./static/index-legacy-1ca9c6f5.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
<script nomodule crossorigin id="vite-legacy-entry" data-src="./static/index-legacy-7a251ba3.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
</body>
</html>

Large diffs are not rendered by default.

Large diffs are not rendered by default.

0 comments on commit 27e1739

Please sign in to comment.