Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jaeger auth support #2612

Merged
merged 5 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

Loading