Skip to content
Open
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
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ ENV cwms.dataapi.access.providers="KeyAccessManager,OpenID"
ENV cwms.dataapi.access.openid.wellKnownUrl="https://<prefix>/.well-known/openid-configuration"
ENV cwms.dataapi.access.openid.issuer="<issuer>"
ENV cwms.dataapi.access.openid.timeout="604800"
# Putting default values here to easy configuration
ENV cwms.dataapi.access.openid.clientId=cwms
ENV cwms.dataapi.access.openid.idpHint=federation-eams
#ENV cwms.dataapi.access.openid.altAuthUrl="https://identityc-test.cwbi.us/auth/realms/cwbi"

# used to simplify redeploy in certain contexts. Update to match -<marker> in image label
Expand Down
19 changes: 15 additions & 4 deletions cda-gui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cda-gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"react-dom": "^18.2.0",
"react-icons": "^5.0.1",
"react-router-dom": "^7.1.2",
"swagger-ui-dist": "^5.17.7",
"swagger-ui-dist": "^5.29.5",
"use-debounce": "^10.0.5"
},
"devDependencies": {
Expand Down
6 changes: 6 additions & 0 deletions cda-gui/public/oauth2-redirect.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!doctype html>
<html lang="en-US">
<body>
<script src="oauth2-redirect.js"></script>
</body>
</html>
69 changes: 69 additions & 0 deletions cda-gui/public/oauth2-redirect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"use strict"
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2
var sentState = oauth2.state
var redirectUrl = oauth2.redirectUrl
var isValid, qp, arr

if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1).replace("?", "&")
} else {
qp = location.search.substring(1)
}

arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace("=", '":"') + '"' })
qp = qp ? JSON.parse("{" + arr.join() + "}",
function (key, value) {
return key === "" ? value : decodeURIComponent(value)
}
) : {}

isValid = qp.state === sentState

if ((
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
oauth2.auth.schema.get("flow") === "authorization_code"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
})
}

if (qp.code) {
delete oauth2.state
oauth2.auth.code = qp.code
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl})
} else {
let oauthErrorMsg
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "")
}

oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
})
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl})
}
window.close()
}

if( document.readyState !== "loading" ) {
run()
} else {
document.addEventListener("DOMContentLoaded", function () {
run()
})
}
47 changes: 39 additions & 8 deletions cda-gui/src/pages/swagger-ui/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,54 @@ export default function SwaggerUI() {
document.title = "CWMS Data API for Data Retrieval - Swagger UI";
// Begin Swagger UI call region
// TODO: add endpoint that dynamic returns swagger generated doc
SwaggerUIBundle({

const ui = SwaggerUIBundle({
url: getBasePath() + "/swagger-docs",

dom_id: "#swagger-ui",
deepLinking: false,
presets: [SwaggerUIBundle.presets.apis],
plugins: [SwaggerUIBundle.plugins.DownloadUrl],
requestInterceptor: (req) => {
// Add a cache-busting query param
const sep = req.url.includes("?") ? "&" : "?";
req.url = `${req.url}${sep}_cb=${Date.now()}`;

// Also ask intermediaries not to serve from cache
req.headers["Cache-Control"] = "no-cache, no-store, max-age=0";
req.headers["Pragma"] = "no-cache";
// Add a cache-busting query param... but only if it's to our api. Some
// external systems, like keycloak, don't allow random unknown parameters.
const origin = window.location.origin;
const re = new RegExp(`^${origin}.*`)
if (re.test(req.url))
{
const sep = req.url.includes("?") ? "&" : "?";
req.url = `${req.url}${sep}_cb=${Date.now()}`;

// Also ask intermediaries not to serve from cache
req.headers["Cache-Control"] = "no-cache, no-store, max-age=0";
req.headers["Pragma"] = "no-cache";
}
return req;
},
onComplete: () => {
const spec = JSON.parse(ui.spec().get("spec"));
for (const schemeName in spec.components.securitySchemes) {
const scheme = spec.components.securitySchemes[schemeName];
if (scheme.type === "openIdConnect") {
let additionalParams = null;
let hints = scheme["x-kc_idp_hint"];
if (hints) {
additionalParams = {
// Since getting the interface to allow users to choose
// is likely impossible, we will assume the first in the list
// is the "primary" auth system
"kc_idp_hint": hints.values[0]
};
}
ui.initOAuth({
clientId: scheme["x-oidc-client-id"],
usePkceWithAuthorizationCodeGrant: true,
additionalQueryStringParams: additionalParams,
});
break;
}
}
},
});
}, []);

Expand Down
16 changes: 13 additions & 3 deletions cda-gui/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import react from "@vitejs/plugin-react";

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
// const env = loadEnv(mode, process.cwd(), "");
const env = loadEnv(mode, process.cwd(), "");
Copy link
Collaborator

@krowvin krowvin Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works with Node, but you should also see

https://vite.dev/guide/env-and-mode

i.e.

const CUSTOM_VAR = import.meta.env.VITE_CUSTOM_VAR

Where you prefix with VITE_ to avoid accidental env var injection.. from your .env files

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, was wondering why that that was comment out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, that doesn't actually work within the vite.config.js

// const BASE_PATH = env?.BASE_PATH ?? "/cwms-data";
return {
base: "/cwms-data",
Expand All @@ -14,12 +14,22 @@ export default defineConfig(({ mode }) => {
server: {
proxy: {
"^/cwms-data/timeseries/.*": {
target: "https://cwms-data.usace.army.mil",
target: env.CDA_API_ROOT,
changeOrigin: true,
secure: false,
},
"^/cwms-data/catalog/.*": {
target: "https://cwms-data.usace.army.mil",
target: env.CDA_API_ROOT,
changeOrigin: true,
secure: false,
},
"^/cwms-data/auth/.*": {
target: env.CDA_API_ROOT,
changeOrigin: true,
secure: false,
},
"^/cwms-data/swagger-docs$": {
target: env.CDA_API_ROOT,
changeOrigin: true,
secure: false,
},
Expand Down
3 changes: 2 additions & 1 deletion compose_files/keycloak/realm.json
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,8 @@
"clientAuthenticatorType": "client-secret",
"redirectUris": [
"https://cwms-data.test:8444/*",
"https://localhost:5010/*"
"https://localhost:5010/*",
"http://localhost:*"
],
"webOrigins": [
"*"
Expand Down
1 change: 1 addition & 0 deletions cwms-data-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ task run(type: JavaExec) {
mainClass = "fixtures.TomcatServer"
systemProperties += project.properties.findAll { k, v -> k.startsWith("RADAR") }
systemProperties += project.properties.findAll { k, v -> k.startsWith("CDA") }
systemProperties += project.properties.findAll { k, v -> k.startsWith("cwms") }

def context = project.findProperty("cda.war.context") ?: "spk-data"

Expand Down
Loading
Loading