9709d_all.patch
| new/src/com/goldencode/p2j/main/WebClientSpawner.java 2026-03-27 07:11:05 +0000 | ||
|---|---|---|
| 38 | 38 |
** 020 TJD 20250807 Added SNI_HOST_CHECK option to control Jetty's SNIHostCheck feature |
| 39 | 39 |
** 021 SBI 20251019 Reused ConfigItem.BROKER_DEDICATED_MODE. |
| 40 | 40 |
** 022 TG 20260121 Added support for quoted cfg overrides. |
| 41 |
** 023 SBI 20260325 Provided WebTokensService parameter to startupServer. |
|
| 41 | 42 |
*/ |
| 42 | 43 |
/* |
| 43 | 44 |
** This program is free software: you can redistribute it and/or modify |
| ... | ... | |
| 411 | 412 |
config.setConfigItem("security", "trust_mgr", "disable", "false");
|
| 412 | 413 |
config.setConfigItem("security", "transport", "refresh", "true");
|
| 413 | 414 | |
| 415 |
WebTokensService webTokensService = null; |
|
| 416 |
try |
|
| 417 |
{
|
|
| 418 |
// create WebTokenService |
|
| 419 |
webTokensService = new WebTokensService(); |
|
| 420 |
} |
|
| 421 |
catch (Exception e1) |
|
| 422 |
{
|
|
| 423 |
LOG.severe("new WebTokensService() failed", e1);
|
|
| 424 |
throw new RuntimeException(e1); |
|
| 425 |
} |
|
| 426 |
|
|
| 414 | 427 |
// get an explicit port and host set by the client |
| 415 | 428 |
int port = config.getInt(ConfigItem.PORT, 0); |
| 416 | 429 |
String host = config.getString(ConfigItem.HOST, null); |
| ... | ... | |
| 430 | 443 |
ScreenDriver<?> driver = gui ? new GuiWebDriver(config) : new ChuiWebDriver(config); |
| 431 | 444 |
driver.init(); |
| 432 | 445 |
|
| 433 |
String uri = ((EmbeddedWebServer) driver).startupServer(keyStore, config, host, port); |
|
| 446 |
String uri = ((EmbeddedWebServer) driver).startupServer(keyStore, config, host, port, webTokensService);
|
|
| 434 | 447 |
|
| 435 | 448 |
String forwardedHost = config.getString(ConfigItem.PROXY_HOST, null); |
| 436 | 449 |
|
| new/src/com/goldencode/p2j/ui/chui/ThinClient.java 2026-03-27 07:12:44 +0000 | ||
|---|---|---|
| 3182 | 3182 |
** PMP 20260223 Prevented focus reset also for (forward) TAB. Refs: #11085-8. |
| 3183 | 3183 |
** 1083 DMM 20260127 Do not prevent MOUSE-PRESSED event for the drop-down of the active combo-box. |
| 3184 | 3184 |
** 1084 SP 20260226 Added sendSsoReauth() for oidc SSO re-authentication. |
| 3185 |
** 1085 SBI 20260325 Provided WebTokensService to web client. |
|
| 3185 | 3186 |
*/ |
| 3186 | 3187 | |
| 3187 | 3188 |
/* |
| ... | ... | |
| 3262 | 3263 |
import com.goldencode.p2j.ui.client.chui.driver.batch.*; |
| 3263 | 3264 |
import com.goldencode.p2j.ui.client.driver.*; |
| 3264 | 3265 |
import com.goldencode.p2j.ui.client.driver.ScreenDriver.*; |
| 3266 |
import com.goldencode.p2j.ui.client.driver.web.WebTokensService; |
|
| 3265 | 3267 |
import com.goldencode.p2j.ui.client.event.*; |
| 3266 | 3268 |
import com.goldencode.p2j.ui.client.event.FocusEvent; |
| 3267 | 3269 |
import com.goldencode.p2j.ui.client.event.InvocationEvent; |
| ... | ... | |
| 18409 | 18411 |
} |
| 18410 | 18412 | |
| 18411 | 18413 |
/** |
| 18414 |
* Expose the web tokens service for the client usages. |
|
| 18415 |
* |
|
| 18416 |
* @return The web tokens service. |
|
| 18417 |
*/ |
|
| 18418 |
public WebTokensService getWebTokensService() |
|
| 18419 |
{
|
|
| 18420 |
ScreenDriver<?> driver = tk.getDriver(); |
|
| 18421 |
if (driver.isWeb()) |
|
| 18422 |
{
|
|
| 18423 |
return ((IWebTokensServiceProvider) driver).getWebTokensService(); |
|
| 18424 |
} |
|
| 18425 |
return null; |
|
| 18426 |
} |
|
| 18427 | ||
| 18428 |
/** |
|
| 18412 | 18429 |
* Get <code>Component</code> instance for a given ID. |
| 18413 | 18430 |
* |
| 18414 | 18431 |
* @param componentId |
| new/src/com/goldencode/p2j/ui/client/chui/driver/web/ChuiWebDriver.java 2026-03-25 20:23:26 +0000 | ||
|---|---|---|
| 51 | 51 |
** 031 SBI 20250128 Removed usages of getKeyboardLayoutsSettings() as a part of removed code related to |
| 52 | 52 |
** keyboard layouts implementation. |
| 53 | 53 |
** 032 SP 20260226 Added sendSsoReauth for SSO re-authentication. |
| 54 |
** 033 SBI 20260325 Provided WebTokensService parameter to startupServer. |
|
| 54 | 55 |
*/ |
| 55 | 56 | |
| 56 | 57 |
/* |
| ... | ... | |
| 137 | 138 |
public class ChuiWebDriver |
| 138 | 139 |
extends AbstractChuiDriver |
| 139 | 140 |
implements EmbeddedWebServer, |
| 140 |
ScreenWebDriver |
|
| 141 |
ScreenWebDriver, |
|
| 142 |
IWebTokensServiceProvider |
|
| 141 | 143 |
{
|
| 142 | 144 |
/** Driver name. */ |
| 143 | 145 |
public static final String NAME = "web-chui"; |
| ... | ... | |
| 169 | 171 |
/** Web-Socket which provides protocol for communication with the browser. */ |
| 170 | 172 |
private StorageMessagingWebSocket websock; |
| 171 | 173 | |
| 174 |
/** The web tokens service */ |
|
| 175 |
private WebTokensService webTokensService; |
|
| 176 | ||
| 172 | 177 |
/** |
| 173 | 178 |
* Constructor. |
| 174 | 179 |
* |
| ... | ... | |
| 533 | 538 |
* The exported server KeyStore. |
| 534 | 539 |
* @param config |
| 535 | 540 |
* Client configuration. |
| 536 |
* @param port |
|
| 537 |
* An explicit port to start the web server or 0 to let the OS assign one. |
|
| 538 | 541 |
* @param host |
| 539 | 542 |
* An explicit host to start the web server or <code>null</code> to use the server's |
| 540 | 543 |
* host. |
| 544 |
* @param port |
|
| 545 |
* An explicit port to start the web server or 0 to let the OS assign one. |
|
| 546 |
* @param webTokensService |
|
| 547 |
* The web tokens service |
|
| 541 | 548 |
* |
| 542 | 549 |
* @return The embedded web server's URI. |
| 543 | 550 |
*/ |
| 544 | 551 |
@Override |
| 545 |
public String startupServer(ServerKeyStore keyStore, BootstrapConfig config, String host, int port) |
|
| 552 |
public String startupServer(ServerKeyStore keyStore, BootstrapConfig config, String host, int port, WebTokensService webTokensService)
|
|
| 546 | 553 |
{
|
| 547 |
return websrv.startupServer(keyStore, config, host, port); |
|
| 554 |
this.webTokensService = webTokensService; |
|
| 555 |
return websrv.startupServer(keyStore, config, host, port, webTokensService); |
|
| 548 | 556 |
} |
| 549 | 557 |
|
| 550 | 558 |
/** |
| ... | ... | |
| 740 | 748 |
simulator.setCursor(col, row); |
| 741 | 749 |
} |
| 742 | 750 |
} |
| 751 | ||
| 752 |
/** |
|
| 753 |
* Return web tokens service. |
|
| 754 |
* |
|
| 755 |
* @return Web tokens service |
|
| 756 |
*/ |
|
| 757 |
@Override |
|
| 758 |
public WebTokensService getWebTokensService() |
|
| 759 |
{
|
|
| 760 |
return this.webTokensService; |
|
| 761 |
} |
|
| 743 | 762 |
} |
| new/src/com/goldencode/p2j/ui/client/driver/IWebTokensServiceProvider.java 2026-03-25 20:20:13 +0000 | ||
|---|---|---|
| 1 |
/* |
|
| 2 |
** Module : IWebTokensServiceProvide.java |
|
| 3 |
** Abstract : Provides methods to get web tokens service. |
|
| 4 |
** |
|
| 5 |
** Copyright (c) 2026, Golden Code Development Corporation. |
|
| 6 |
** |
|
| 7 |
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------- |
|
| 8 |
** 001 SBI 20260122 Created initial version. |
|
| 9 |
*/ |
|
| 10 |
/* |
|
| 11 |
** This program is free software: you can redistribute it and/or modify |
|
| 12 |
** it under the terms of the GNU Affero General Public License as |
|
| 13 |
** published by the Free Software Foundation, either version 3 of the |
|
| 14 |
** License, or (at your option) any later version. |
|
| 15 |
** |
|
| 16 |
** This program is distributed in the hope that it will be useful, |
|
| 17 |
** but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 18 |
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 19 |
** GNU Affero General Public License for more details. |
|
| 20 |
** |
|
| 21 |
** You may find a copy of the GNU Affero GPL version 3 at the following |
|
| 22 |
** location: https://www.gnu.org/licenses/agpl-3.0.en.html |
|
| 23 |
** |
|
| 24 |
** Additional terms under GNU Affero GPL version 3 section 7: |
|
| 25 |
** |
|
| 26 |
** Under Section 7 of the GNU Affero GPL version 3, the following additional |
|
| 27 |
** terms apply to the works covered under the License. These additional terms |
|
| 28 |
** are non-permissive additional terms allowed under Section 7 of the GNU |
|
| 29 |
** Affero GPL version 3 and may not be removed by you. |
|
| 30 |
** |
|
| 31 |
** 0. Attribution Requirement. |
|
| 32 |
** |
|
| 33 |
** You must preserve all legal notices or author attributions in the covered |
|
| 34 |
** work or Appropriate Legal Notices displayed by works containing the covered |
|
| 35 |
** work. You may not remove from the covered work any author or developer |
|
| 36 |
** credit already included within the covered work. |
|
| 37 |
** |
|
| 38 |
** 1. No License To Use Trademarks. |
|
| 39 |
** |
|
| 40 |
** This license does not grant any license or rights to use the trademarks |
|
| 41 |
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks |
|
| 42 |
** of Golden Code Development Corporation. You are not authorized to use the |
|
| 43 |
** name Golden Code, FWD, or the names of any author or contributor, for |
|
| 44 |
** publicity purposes without written authorization. |
|
| 45 |
** |
|
| 46 |
** 2. No Misrepresentation of Affiliation. |
|
| 47 |
** |
|
| 48 |
** You may not represent yourself as Golden Code Development Corporation or FWD. |
|
| 49 |
** |
|
| 50 |
** You may not represent yourself for publicity purposes as associated with |
|
| 51 |
** Golden Code Development Corporation, FWD, or any author or contributor to |
|
| 52 |
** the covered work, without written authorization. |
|
| 53 |
** |
|
| 54 |
** 3. No Misrepresentation of Source or Origin. |
|
| 55 |
** |
|
| 56 |
** You may not represent the covered work as solely your work. All modified |
|
| 57 |
** versions of the covered work must be marked in a reasonable way to make it |
|
| 58 |
** clear that the modified work is not originating from Golden Code Development |
|
| 59 |
** Corporation or FWD. All modified versions must contain the notices of |
|
| 60 |
** attribution required in this license. |
|
| 61 |
*/ |
|
| 62 | ||
| 63 |
package com.goldencode.p2j.ui.client.driver; |
|
| 64 | ||
| 65 |
import com.goldencode.p2j.ui.client.driver.web.WebTokensService; |
|
| 66 | ||
| 67 |
public interface IWebTokensServiceProvider |
|
| 68 |
{
|
|
| 69 |
/** |
|
| 70 |
* Return web tokens service. |
|
| 71 |
* |
|
| 72 |
* @return Web tokens service |
|
| 73 |
*/ |
|
| 74 |
WebTokensService getWebTokensService(); |
|
| 75 |
} |
|
| new/src/com/goldencode/p2j/ui/client/driver/web/AuthHandler.java 2026-03-27 07:34:36 +0000 | ||
|---|---|---|
| 2 | 2 |
** Module : AuthHandler.java |
| 3 | 3 |
** Abstract : web authorization |
| 4 | 4 |
** |
| 5 |
** Copyright (c) 2019-2025, Golden Code Development Corporation.
|
|
| 5 |
** Copyright (c) 2019-2026, Golden Code Development Corporation.
|
|
| 6 | 6 |
** |
| 7 | 7 |
** -#- -I- --Date-- ---------------------------------Description---------------------------------- |
| 8 | 8 |
** 001 HC 20190910 Initial version. |
| ... | ... | |
| 13 | 13 |
** 004 GBB 20230908 Auth cookie name to contain port to be identifiable by JS. |
| 14 | 14 |
** 005 GBB 20240529 Auth cookie to be secure, same-site and http only. |
| 15 | 15 |
** 006 TJD 20250316 Upgrade to Jetty 12 |
| 16 |
** 007 SBI 20260301 Set cross-site partitioned cookie. |
|
| 17 |
** 20260326 Added /open/resource and /document handlers to provide |
|
| 18 |
** these resource to authorized users. |
|
| 16 | 19 |
*/ |
| 17 | 20 |
/* |
| 18 | 21 |
** This program is free software: you can redistribute it and/or modify |
| ... | ... | |
| 70 | 73 |
package com.goldencode.p2j.ui.client.driver.web; |
| 71 | 74 | |
| 72 | 75 |
import com.goldencode.p2j.main.*; |
| 76 |
import com.goldencode.p2j.ui.client.gui.driver.OpenResourceTemplate; |
|
| 77 |
import com.goldencode.p2j.ui.client.gui.driver.web.DocumentOutputHandler; |
|
| 73 | 78 |
import com.goldencode.p2j.util.logging.*; |
| 74 | 79 |
import org.apache.commons.codec.digest.*; |
| 75 | 80 |
import org.eclipse.jetty.http.*; |
| ... | ... | |
| 93 | 98 |
/** Logger. */ |
| 94 | 99 |
private static final CentralLogger LOG = CentralLogger.get(AuthHandler.class.getName()); |
| 95 | 100 | |
| 101 |
/** |
|
| 102 |
* The secure host prefix used with CHIPS. See |
|
| 103 |
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#cookie_prefixes |
|
| 104 |
*/ |
|
| 105 |
private static final String HOST_SECURE_CHIPS_PREFIX = "__Host-"; |
|
| 106 | ||
| 107 |
/** The local documents handler */ |
|
| 108 |
private static final String DOCUMENT_HANDLER = "/document/"; |
|
| 109 |
/** The embedded resource handler */ |
|
| 110 |
private static final String EMBEDDED_RESOURCE_HANDLER = OpenResourceTemplate.REQUEST; |
|
| 111 |
/** API token name*/ |
|
| 112 |
public static final String API_TOKEN_NAME = "api_token"; |
|
| 113 | ||
| 96 | 114 |
/** Authorization cookie name */ |
| 97 | 115 |
private String authorizationCookieName; |
| 98 | 116 | |
| ... | ... | |
| 102 | 120 |
/** Authorization token */ |
| 103 | 121 |
private String authorizationToken; |
| 104 | 122 | |
| 123 |
/** The web tokens service */ |
|
| 124 |
private final WebTokensService webTokensService; |
|
| 125 | ||
| 105 | 126 |
/** |
| 106 | 127 |
* Constructor. |
| 107 | 128 |
* |
| 108 | 129 |
* @param authorizationToken |
| 109 | 130 |
* Authorization token. |
| 110 | 131 |
*/ |
| 111 |
public AuthHandler(String authorizationToken) |
|
| 132 |
public AuthHandler(String authorizationToken, WebTokensService webTokensService)
|
|
| 112 | 133 |
{
|
| 113 | 134 |
this.authorizationToken = authorizationToken; |
| 135 |
this.webTokensService = webTokensService; |
|
| 114 | 136 |
} |
| 115 |
|
|
| 137 | ||
| 116 | 138 |
/** |
| 117 | 139 |
* Handle the request. |
| 118 | 140 |
* |
| ... | ... | |
| 161 | 183 |
{
|
| 162 | 184 |
return false; |
| 163 | 185 |
} |
| 164 | ||
| 165 |
return (cookieToken == null) ? checkQueryParam(request, response) : checkCookies(request); |
|
| 186 |
|
|
| 187 |
return (cookieToken == null) ? checkQueryParam(request, response) : checkCookies(request, response);
|
|
| 166 | 188 |
} |
| 167 | 189 |
|
| 168 | 190 |
/** |
| ... | ... | |
| 174 | 196 |
* Request instance. |
| 175 | 197 |
* @param response |
| 176 | 198 |
* Response instance. |
| 177 |
*
|
|
| 199 |
* |
|
| 178 | 200 |
* @return <code>true</code> if authorized <code>false</code> otherwise. |
| 179 | 201 |
*/ |
| 180 | 202 |
@SuppressWarnings("deprecation")
|
| ... | ... | |
| 191 | 213 |
if (match) |
| 192 | 214 |
{
|
| 193 | 215 |
// set a cookie using a new generated token |
| 194 |
authorizationCookieName = "Auth-" + authorizationToken + "-" + UUID.randomUUID().toString(); |
|
| 216 |
authorizationCookieName = HOST_SECURE_CHIPS_PREFIX + "Auth-" + authorizationToken + "-" + UUID.randomUUID().toString();
|
|
| 195 | 217 |
cookieToken = DigestUtils.shaHex(UUID.randomUUID().toString()); |
| 196 | 218 |
HttpCookie cookie = HttpCookie.build(authorizationCookieName, cookieToken). |
| 197 | 219 |
path("/").
|
| 198 | 220 |
secure(true). |
| 199 | 221 |
httpOnly(true). |
| 200 |
sameSite(HttpCookie.SameSite.STRICT). |
|
| 222 |
sameSite(HttpCookie.SameSite.NONE). |
|
| 223 |
partitioned(true). |
|
| 201 | 224 |
build(); |
| 202 | 225 |
Response.addCookie(response, cookie); |
| 203 | 226 |
} |
| ... | ... | |
| 211 | 234 |
* |
| 212 | 235 |
* @param request |
| 213 | 236 |
* HttpServletRequest instance. |
| 214 |
* |
|
| 237 |
* @param response |
|
| 238 |
* Response instance. |
|
| 239 |
* |
|
| 215 | 240 |
* @return <code>true</code> if authorized <code>false</code> otherwise. |
| 216 | 241 |
*/ |
| 217 |
private boolean checkCookies(Request request) |
|
| 242 |
private boolean checkCookies(Request request, Response response)
|
|
| 218 | 243 |
{
|
| 219 | 244 |
// check browser cookies |
| 220 | 245 |
List<HttpCookie> cookies = Request.getCookies(request); |
| 221 |
|
|
| 246 |
|
|
| 222 | 247 |
if (cookies != null) |
| 223 | 248 |
{
|
| 224 | 249 |
// search for authorization cookie |
| ... | ... | |
| 233 | 258 |
} |
| 234 | 259 |
} |
| 235 | 260 |
} |
| 261 |
Fields queryParameters = Request.extractQueryParameters(request, StandardCharsets.UTF_8); |
|
| 262 |
String target = request.getContext().getPathInContext(request.getHttpURI().getPath()); |
|
| 236 | 263 |
|
| 264 |
String jwe = queryParameters.getValue(API_TOKEN_NAME); |
|
| 265 |
if (jwe != null && !jwe.isEmpty()) |
|
| 266 |
{
|
|
| 267 |
String subject = null; |
|
| 268 |
if (target != null && target.startsWith(DOCUMENT_HANDLER)) |
|
| 269 |
{
|
|
| 270 |
subject = DocumentOutputHandler.getUUID(target); |
|
| 271 |
} |
|
| 272 |
else if (target != null && target.startsWith(EMBEDDED_RESOURCE_HANDLER)) |
|
| 273 |
{
|
|
| 274 |
subject = queryParameters.getValue(OpenResourceTemplate.DOCUMENT_PATH); |
|
| 275 |
} |
|
| 276 |
|
|
| 277 |
// validate access rights |
|
| 278 |
if (subject != null) |
|
| 279 |
{
|
|
| 280 |
return webTokensService.validateWebTokenValue(jwe, subject, null); |
|
| 281 |
} |
|
| 282 |
} |
|
| 237 | 283 |
return false; |
| 238 | 284 |
} |
| 239 | 285 |
} |
| new/src/com/goldencode/p2j/ui/client/driver/web/EmbeddedWebServer.java 2026-03-25 19:45:15 +0000 | ||
|---|---|---|
| 2 | 2 |
** Module : EmbeddedWebServer.java |
| 3 | 3 |
** Abstract : embedded web server contract |
| 4 | 4 |
** |
| 5 |
** Copyright (c) 2013-2023, Golden Code Development Corporation.
|
|
| 5 |
** Copyright (c) 2013-2026, Golden Code Development Corporation.
|
|
| 6 | 6 |
** |
| 7 | 7 |
** -#- -I- --Date-- ---------------------------------Description---------------------------------- |
| 8 | 8 |
** 001 MAG 20131218 Created initial version. |
| ... | ... | |
| 13 | 13 |
** that it has finished initialized. |
| 14 | 14 |
** 006 HC 20190911 Implemented ad-hoc handler registration. |
| 15 | 15 |
** 007 SBI 20230505 Fixed startupServer java doc. |
| 16 |
** 008 SBI 20260325 Provided WebTokensService parameter to startupServer. |
|
| 16 | 17 |
*/ |
| 17 | 18 |
/* |
| 18 | 19 |
** This program is free software: you can redistribute it and/or modify |
| ... | ... | |
| 73 | 74 |
import com.goldencode.p2j.main.*; |
| 74 | 75 |
import com.goldencode.p2j.util.*; |
| 75 | 76 |
import org.eclipse.jetty.server.*; |
| 76 |
import org.eclipse.jetty.util.resource.*; |
|
| 77 | 77 | |
| 78 | 78 |
/** |
| 79 | 79 |
* Embedded web server API. |
| ... | ... | |
| 88 | 88 |
* The exported server KeyStore. |
| 89 | 89 |
* @param config |
| 90 | 90 |
* Client configuration. |
| 91 |
* @param port |
|
| 92 |
* An explicit port to start the web server or 0 to let the OS assign one. |
|
| 93 | 91 |
* @param host |
| 94 | 92 |
* An explicit host to start the web server or <code>null</code> to use the server's |
| 95 | 93 |
* host. |
| 94 |
* @param port |
|
| 95 |
* An explicit port to start the web server or 0 to let the OS assign one. |
|
| 96 |
* @param webTokensService |
|
| 97 |
* The web tokens service |
|
| 96 | 98 |
* |
| 97 | 99 |
* @return The embedded web server's URI. |
| 98 | 100 |
*/ |
| 99 |
public String startupServer(ServerKeyStore keyStore, BootstrapConfig config, String host, int port); |
|
| 101 |
public String startupServer(ServerKeyStore keyStore, BootstrapConfig config, String host, int port, WebTokensService webTokensService);
|
|
| 100 | 102 | |
| 101 | 103 |
/** |
| 102 | 104 |
* Shutdown the embedded web server. |
| new/src/com/goldencode/p2j/ui/client/driver/web/EmbeddedWebServerImpl.java 2026-03-25 19:59:43 +0000 | ||
|---|---|---|
| 2 | 2 |
** Module : EmbeddedWebServerImpl.java |
| 3 | 3 |
** Abstract : embedded web server shared implementation |
| 4 | 4 |
** |
| 5 |
** Copyright (c) 2013-2025, Golden Code Development Corporation.
|
|
| 5 |
** Copyright (c) 2013-2026, Golden Code Development Corporation.
|
|
| 6 | 6 |
** |
| 7 | 7 |
** -#- -I- --Date-- ---------------------------------Description---------------------------------- |
| 8 | 8 |
** 001 GES 20150312 Created initial version by moving code from the ChUI web driver and making it |
| ... | ... | |
| 26 | 26 |
** 010 TJD 20250314 Migration to Jetty 12 |
| 27 | 27 |
** TJD 20250807 Added SNI_HOST_CHECK option to control Jetty's SNIHostCheck feature |
| 28 | 28 |
** 011 DDF 20250519 Use FileExtensions for all file extensions. |
| 29 |
** 012 SBI 20260325 Provided WebTokensService parameter to startupServer. |
|
| 29 | 30 |
*/ |
| 30 | 31 | |
| 31 | 32 |
/* |
| ... | ... | |
| 175 | 176 |
* The exported server KeyStore. |
| 176 | 177 |
* @param config |
| 177 | 178 |
* Client configuration. |
| 178 |
* @param port |
|
| 179 |
* An explicit port to start the web server or 0 to let the OS assign one. |
|
| 180 | 179 |
* @param host |
| 181 | 180 |
* An explicit host to start the web server or <code>null</code> to use the server's |
| 182 | 181 |
* host. |
| 182 |
* @param port |
|
| 183 |
* An explicit port to start the web server or 0 to let the OS assign one. |
|
| 184 |
* @param webTokensService |
|
| 185 |
* The web tokens service |
|
| 183 | 186 |
* |
| 184 | 187 |
* @return The embedded web server's URI. |
| 185 | 188 |
*/ |
| 186 | 189 |
@Override |
| 187 |
public String startupServer(ServerKeyStore keyStore, BootstrapConfig config, String host, int port) |
|
| 190 |
public String startupServer(ServerKeyStore keyStore, BootstrapConfig config, String host, int port, WebTokensService webTokensService)
|
|
| 188 | 191 |
{
|
| 189 | 192 |
externalHost = host; |
| 190 | 193 |
|
| ... | ... | |
| 201 | 204 |
server.setBootstrapConfig(config); |
| 202 | 205 |
|
| 203 | 206 |
// add authorization handler |
| 204 |
server.addHandler(new AuthHandler(token)); |
|
| 207 |
server.addHandler(new AuthHandler(token, webTokensService));
|
|
| 205 | 208 | |
| 206 | 209 |
// process each handler |
| 207 | 210 |
for (int i = 0; i < hdlrs.length; i++) |
| new/src/com/goldencode/p2j/ui/client/driver/web/WebTokensService.java 2026-03-27 06:48:24 +0000 | ||
|---|---|---|
| 1 |
/* |
|
| 2 |
** Module : WebTokensService.java |
|
| 3 |
** Abstract : Provides methods to generate and validate web tokens. |
|
| 4 |
** |
|
| 5 |
** Copyright (c) 2026, Golden Code Development Corporation. |
|
| 6 |
** |
|
| 7 |
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------- |
|
| 8 |
** 001 SBI 20260325 Created initial version. |
|
| 9 |
*/ |
|
| 10 |
/* |
|
| 11 |
** This program is free software: you can redistribute it and/or modify |
|
| 12 |
** it under the terms of the GNU Affero General Public License as |
|
| 13 |
** published by the Free Software Foundation, either version 3 of the |
|
| 14 |
** License, or (at your option) any later version. |
|
| 15 |
** |
|
| 16 |
** This program is distributed in the hope that it will be useful, |
|
| 17 |
** but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 18 |
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 19 |
** GNU Affero General Public License for more details. |
|
| 20 |
** |
|
| 21 |
** You may find a copy of the GNU Affero GPL version 3 at the following |
|
| 22 |
** location: https://www.gnu.org/licenses/agpl-3.0.en.html |
|
| 23 |
** |
|
| 24 |
** Additional terms under GNU Affero GPL version 3 section 7: |
|
| 25 |
** |
|
| 26 |
** Under Section 7 of the GNU Affero GPL version 3, the following additional |
|
| 27 |
** terms apply to the works covered under the License. These additional terms |
|
| 28 |
** are non-permissive additional terms allowed under Section 7 of the GNU |
|
| 29 |
** Affero GPL version 3 and may not be removed by you. |
|
| 30 |
** |
|
| 31 |
** 0. Attribution Requirement. |
|
| 32 |
** |
|
| 33 |
** You must preserve all legal notices or author attributions in the covered |
|
| 34 |
** work or Appropriate Legal Notices displayed by works containing the covered |
|
| 35 |
** work. You may not remove from the covered work any author or developer |
|
| 36 |
** credit already included within the covered work. |
|
| 37 |
** |
|
| 38 |
** 1. No License To Use Trademarks. |
|
| 39 |
** |
|
| 40 |
** This license does not grant any license or rights to use the trademarks |
|
| 41 |
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks |
|
| 42 |
** of Golden Code Development Corporation. You are not authorized to use the |
|
| 43 |
** name Golden Code, FWD, or the names of any author or contributor, for |
|
| 44 |
** publicity purposes without written authorization. |
|
| 45 |
** |
|
| 46 |
** 2. No Misrepresentation of Affiliation. |
|
| 47 |
** |
|
| 48 |
** You may not represent yourself as Golden Code Development Corporation or FWD. |
|
| 49 |
** |
|
| 50 |
** You may not represent yourself for publicity purposes as associated with |
|
| 51 |
** Golden Code Development Corporation, FWD, or any author or contributor to |
|
| 52 |
** the covered work, without written authorization. |
|
| 53 |
** |
|
| 54 |
** 3. No Misrepresentation of Source or Origin. |
|
| 55 |
** |
|
| 56 |
** You may not represent the covered work as solely your work. All modified |
|
| 57 |
** versions of the covered work must be marked in a reasonable way to make it |
|
| 58 |
** clear that the modified work is not originating from Golden Code Development |
|
| 59 |
** Corporation or FWD. All modified versions must contain the notices of |
|
| 60 |
** attribution required in this license. |
|
| 61 |
*/ |
|
| 62 | ||
| 63 |
package com.goldencode.p2j.ui.client.driver.web; |
|
| 64 | ||
| 65 |
import java.security.SecureRandom; |
|
| 66 |
import java.util.Date; |
|
| 67 |
import java.util.Map; |
|
| 68 | ||
| 69 |
import javax.crypto.KeyGenerator; |
|
| 70 |
import javax.crypto.SecretKey; |
|
| 71 | ||
| 72 | ||
| 73 |
import com.goldencode.p2j.util.logging.CentralLogger; |
|
| 74 | ||
| 75 |
import io.jsonwebtoken.Claims; |
|
| 76 |
import io.jsonwebtoken.ExpiredJwtException; |
|
| 77 |
import io.jsonwebtoken.InvalidClaimException; |
|
| 78 |
import io.jsonwebtoken.Jwe; |
|
| 79 |
import io.jsonwebtoken.Jwts; |
|
| 80 | ||
| 81 |
/** |
|
| 82 |
* The web client web tokens service |
|
| 83 |
*/ |
|
| 84 |
public class WebTokensService |
|
| 85 |
{
|
|
| 86 |
/** The class logger */ |
|
| 87 |
private static final CentralLogger LOG = CentralLogger.get(WebTokensService.class); |
|
| 88 |
/** The encryption algorithm */ |
|
| 89 |
private static final String ALGORITHM = "AES"; |
|
| 90 |
/** |
|
| 91 |
* Cypher internal parameter Jwts.ENC.A128GCM for external usages |
|
| 92 |
* String CIPHER_SPEC = "AES/GCM/NoPadding"; |
|
| 93 |
* int GCM_IV_LENGTH = 12; // 96 = 12 * 8 bits |
|
| 94 |
* int GCM_TAG_LENGTH = 16; // 128 = 16 * 8 bits, standard default |
|
| 95 |
*/ |
|
| 96 |
/** The key length in bits */ |
|
| 97 |
private static final int AES_KEY_LENGTH = 128; // 128, 192, or 256 bits |
|
| 98 |
/** The default validity interval in milliseconds */ |
|
| 99 |
private static long VALIDITITY_IN_MILLISECONDS = 60000; |
|
| 100 |
|
|
| 101 |
/** The secret key */ |
|
| 102 |
private final SecretKey secretKey; |
|
| 103 |
|
|
| 104 |
/** The token validity interval in milliseconds */ |
|
| 105 |
private final long validityInterval; |
|
| 106 |
|
|
| 107 |
/** |
|
| 108 |
* Creates new cryptographic secret key for AES. |
|
| 109 |
* |
|
| 110 |
* @return The secret key |
|
| 111 |
* |
|
| 112 |
* @throws Exception |
|
| 113 |
* It can be an invalid parameter exception or a runtime exception thrown during |
|
| 114 |
* the key generation. |
|
| 115 |
*/ |
|
| 116 |
public static SecretKey generateKey() |
|
| 117 |
throws Exception |
|
| 118 |
{
|
|
| 119 |
KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM); |
|
| 120 |
keyGen.init(AES_KEY_LENGTH, SecureRandom.getInstanceStrong()); |
|
| 121 |
return keyGen.generateKey(); |
|
| 122 |
} |
|
| 123 |
|
|
| 124 |
/** |
|
| 125 |
* Create the web tokens service instance. |
|
| 126 |
* |
|
| 127 |
* @throws Exception |
|
| 128 |
* The secret key generation exception |
|
| 129 |
*/ |
|
| 130 |
public WebTokensService() |
|
| 131 |
throws Exception |
|
| 132 |
{
|
|
| 133 |
this(generateKey(), VALIDITITY_IN_MILLISECONDS); |
|
| 134 |
} |
|
| 135 |
|
|
| 136 |
/** |
|
| 137 |
* Create the web tokens service instance for the given secret key and |
|
| 138 |
* validity interval. |
|
| 139 |
* |
|
| 140 |
* @param secretKey |
|
| 141 |
* The secret key |
|
| 142 |
* @param validityInterval |
|
| 143 |
* The validity interval |
|
| 144 |
*/ |
|
| 145 |
public WebTokensService(SecretKey secretKey, long validityInterval) |
|
| 146 |
{
|
|
| 147 |
this.secretKey = secretKey; |
|
| 148 |
this.validityInterval = validityInterval; |
|
| 149 |
} |
|
| 150 | ||
| 151 |
/** |
|
| 152 |
* Validate the encrypted web token. |
|
| 153 |
* |
|
| 154 |
* @param value |
|
| 155 |
* The string value of the encrypted web token |
|
| 156 |
* @param subject |
|
| 157 |
* The value of "sub" key |
|
| 158 |
* @param claims |
|
| 159 |
* The additional values |
|
| 160 |
* |
|
| 161 |
* @return true if the token is valid, otherwise false |
|
| 162 |
*/ |
|
| 163 |
public boolean validateWebTokenValue(String value, String subject, Map<String, String> claims) |
|
| 164 |
{
|
|
| 165 |
try |
|
| 166 |
{
|
|
| 167 |
Jwe<Claims> jwe = Jwts.parser() |
|
| 168 |
.requireSubject(subject) |
|
| 169 |
.decryptWith(secretKey) |
|
| 170 |
.build() |
|
| 171 |
.parseEncryptedClaims(value); |
|
| 172 | ||
| 173 |
Claims payload = jwe.getPayload(); |
|
| 174 |
if (claims != null) |
|
| 175 |
{
|
|
| 176 |
for(Map.Entry<String, String> entry : claims.entrySet()) |
|
| 177 |
{
|
|
| 178 |
String claimValue = payload.get(entry.getKey(), String.class); |
|
| 179 |
if (claimValue == null || !claimValue.equals(entry.getValue())) |
|
| 180 |
{
|
|
| 181 |
return false; |
|
| 182 |
} |
|
| 183 |
} |
|
| 184 |
} |
|
| 185 |
|
|
| 186 |
return true; |
|
| 187 |
} |
|
| 188 |
catch (io.jsonwebtoken.security.SecurityException e1) |
|
| 189 |
{
|
|
| 190 |
LOG.severe("", e1);
|
|
| 191 |
return false; |
|
| 192 |
} |
|
| 193 |
catch (ExpiredJwtException e2) |
|
| 194 |
{
|
|
| 195 |
LOG.severe("", e2);
|
|
| 196 |
return false; |
|
| 197 |
} |
|
| 198 |
catch(InvalidClaimException e3) |
|
| 199 |
{
|
|
| 200 |
LOG.severe("", e3);
|
|
| 201 |
return false; |
|
| 202 |
} |
|
| 203 |
catch (Exception e4) |
|
| 204 |
{
|
|
| 205 |
LOG.severe("", e4);
|
|
| 206 |
return false; |
|
| 207 |
} |
|
| 208 |
} |
|
| 209 |
|
|
| 210 |
/** |
|
| 211 |
* Generate the encrypted web token. |
|
| 212 |
* |
|
| 213 |
* @param subject |
|
| 214 |
* The value of "sub" key |
|
| 215 |
* @param claims |
|
| 216 |
* The additional values |
|
| 217 |
* |
|
| 218 |
* @return The string presentation of the encrypted web token |
|
| 219 |
* |
|
| 220 |
* @throws Exception |
|
| 221 |
* The exception during this operation |
|
| 222 |
*/ |
|
| 223 |
public String generateWebTokenValue(String subject, Map<String, String> claims) |
|
| 224 |
throws Exception |
|
| 225 |
{
|
|
| 226 |
String jwe = Jwts.builder() |
|
| 227 |
.header() |
|
| 228 |
.and() |
|
| 229 |
.subject(subject) |
|
| 230 |
.expiration(new Date(System.currentTimeMillis() + validityInterval)) |
|
| 231 |
.issuedAt(new Date()) |
|
| 232 |
.claims(claims) |
|
| 233 |
// Encrypt using Direct encryption (dir) and AES-128-GCM |
|
| 234 |
.encryptWith(secretKey, Jwts.ENC.A128GCM) |
|
| 235 |
.compact(); |
|
| 236 |
return jwe; |
|
| 237 |
} |
|
| 238 |
} |
|
| new/src/com/goldencode/p2j/ui/client/driver/web/res/p2j.js 2026-03-27 06:50:22 +0000 | ||
|---|---|---|
| 2 | 2 |
** Module : p2j.js |
| 3 | 3 |
** Abstract : top-level web client java script object |
| 4 | 4 |
** |
| 5 |
** Copyright (c) 2014-2025, Golden Code Development Corporation.
|
|
| 5 |
** Copyright (c) 2014-2026, Golden Code Development Corporation.
|
|
| 6 | 6 |
** |
| 7 | 7 |
** -#- -I- --Date-- ------------------------------Description---------------------------------- |
| 8 | 8 |
** 001 MAG 20140110 First version. |
| ... | ... | |
| 72 | 72 |
** 029 PMP 20250916 Initialized networkSocket module. |
| 73 | 73 |
** 030 SB 20251113 Remove the cursor overlay on mouse clicks as well if there was no previous mouse move. |
| 74 | 74 |
** 031 SBI 20251210 Added repeat key.(refs: #10984, #10982, #10836) |
| 75 |
** 032 SBI 20260326 Refactored to protect openMimeResource access. |
|
| 75 | 76 |
*/ |
| 76 | 77 |
/* |
| 77 | 78 |
** This program is free software: you can redistribute it and/or modify |
| ... | ... | |
| 172 | 173 |
/** The dialog identifiers */ |
| 173 | 174 |
const DIALOGS = { OPENPOPUP : "openMimeResource"}
|
| 174 | 175 |
|
| 175 |
/** The instance of the unique copy/cut/paste notification dialog. */ |
|
| 176 |
var dialog; |
|
| 177 |
|
|
| 178 |
/** The open popup dialog */ |
|
| 179 |
var openPopupDialog; |
|
| 176 |
/** The set of displayed ModalDialogs */ |
|
| 177 |
var dialogs = new Set(); |
|
| 180 | 178 |
|
| 181 | 179 |
/** The notification templates to prompt a user to do an action */ |
| 182 | 180 |
var prompts = {};
|
| ... | ... | |
| 218 | 216 |
i18n = window.i18n(); |
| 219 | 217 |
|
| 220 | 218 |
Object.defineProperty( |
| 221 |
p2j,
|
|
| 219 |
me,
|
|
| 222 | 220 |
"i18n", |
| 223 | 221 |
{
|
| 224 | 222 |
get: function () |
| 225 | 223 |
{
|
| 226 | 224 |
return i18n; |
| 227 |
} |
|
| 225 |
}, |
|
| 226 |
enumerable : false, |
|
| 227 |
configurable : false |
|
| 228 | 228 |
}); |
| 229 | 229 |
var lang = cfg["lang"]; |
| 230 | 230 |
if (lang) |
| 231 | 231 |
{
|
| 232 |
p2j.addTranslations(lang, cfg["translations"]);
|
|
| 232 |
me.addTranslations(lang, cfg["translations"]);
|
|
| 233 | 233 |
} |
| 234 | 234 |
/** |
| 235 | 235 |
* Prevent back navigation. |
| ... | ... | |
| 261 | 261 |
container = document.getElementById(cfg.container) || document.body; |
| 262 | 262 |
|
| 263 | 263 |
// save off our client mode type |
| 264 |
p2j.isGui = cfg.isGui;
|
|
| 264 |
me.isGui = cfg.isGui;
|
|
| 265 | 265 |
|
| 266 | 266 |
// set the web client embedded mode and define a "get" access only |
| 267 | 267 |
Object.defineProperty( |
| 268 |
p2j,
|
|
| 268 |
me,
|
|
| 269 | 269 |
"embedded", |
| 270 | 270 |
{
|
| 271 | 271 |
get: function () |
| 272 | 272 |
{
|
| 273 | 273 |
return cfg.embedded; |
| 274 |
} |
|
| 274 |
}, |
|
| 275 |
enumerable : false, |
|
| 276 |
configurable : false |
|
| 275 | 277 |
}); |
| 276 | 278 |
/** the body html css class name to display wait cursor */ |
| 277 |
p2j.waitCursorClass = "wait";
|
|
| 279 |
me.waitCursorClass = "wait";
|
|
| 278 | 280 |
|
| 279 | 281 |
/** the body html css class name to display default cursor */ |
| 280 |
p2j.bodyHtmlClass = document.body.className;
|
|
| 282 |
me.bodyHtmlClass = document.body.className;
|
|
| 281 | 283 |
|
| 282 | 284 |
/** to display the wait cursor for the document */ |
| 283 |
p2j.displayWaitCursor = function ()
|
|
| 285 |
me.displayWaitCursor = function ()
|
|
| 284 | 286 |
{
|
| 285 |
if (document.body.className === p2j.bodyHtmlClass)
|
|
| 287 |
if (document.body.className === me.bodyHtmlClass)
|
|
| 286 | 288 |
{
|
| 287 |
document.body.className += (" " + p2j.waitCursorClass);
|
|
| 289 |
document.body.className += (" " + me.waitCursorClass);
|
|
| 288 | 290 |
} |
| 289 | 291 |
} |
| 290 | 292 |
|
| 291 | 293 |
/** to restore the cursor for the document */ |
| 292 |
p2j.restoreCursor = function ()
|
|
| 294 |
me.restoreCursor = function ()
|
|
| 293 | 295 |
{
|
| 294 |
document.body.className = p2j.bodyHtmlClass;
|
|
| 296 |
document.body.className = me.bodyHtmlClass;
|
|
| 295 | 297 |
} |
| 296 | 298 | |
| 297 | 299 |
/** |
| ... | ... | |
| 300 | 302 |
* @param cursorStyle |
| 301 | 303 |
* The id of the cursor style. |
| 302 | 304 |
*/ |
| 303 |
p2j.setGlobalCursor = function(cursorStyle)
|
|
| 305 |
me.setGlobalCursor = function(cursorStyle)
|
|
| 304 | 306 |
{
|
| 305 | 307 |
var body = document.body; |
| 306 | 308 |
var style = p2j.screen.calculateCursorStyle(cursorStyle); |
| ... | ... | |
| 366 | 368 |
/** |
| 367 | 369 |
* Remove the globally assigned cursor. |
| 368 | 370 |
*/ |
| 369 |
p2j.removeGlobalCursor = function()
|
|
| 371 |
me.removeGlobalCursor = function()
|
|
| 370 | 372 |
{
|
| 371 | 373 |
var body = document.body; |
| 372 | 374 |
var bodyOverlay = document.getElementById("body-overlay");
|
| ... | ... | |
| 382 | 384 |
* @param {String} rgb
|
| 383 | 385 |
* The CSS color string. |
| 384 | 386 |
*/ |
| 385 |
p2j.setDefaultDesktopBgcolor = function(rgb)
|
|
| 387 |
me.setDefaultDesktopBgcolor = function(rgb)
|
|
| 386 | 388 |
{
|
| 387 |
p2j.defaultDesktopBgcolor = rgb;
|
|
| 389 |
me.defaultDesktopBgcolor = rgb;
|
|
| 388 | 390 |
}; |
| 389 | 391 |
|
| 390 | 392 |
/** |
| ... | ... | |
| 393 | 395 |
* @param {String} rgb
|
| 394 | 396 |
* The CSS color string. |
| 395 | 397 |
*/ |
| 396 |
p2j.setDesktopBgColor = function (rgb)
|
|
| 398 |
me.setDesktopBgColor = function (rgb)
|
|
| 397 | 399 |
{
|
| 398 |
if (p2j.embedded)
|
|
| 400 |
if (me.embedded)
|
|
| 399 | 401 |
{
|
| 400 |
if (p2j.defaultDesktopBgcolor)
|
|
| 402 |
if (me.defaultDesktopBgcolor)
|
|
| 401 | 403 |
{
|
| 402 |
rgb = p2j.defaultDesktopBgcolor;
|
|
| 404 |
rgb = me.defaultDesktopBgcolor;
|
|
| 403 | 405 |
} |
| 404 | 406 | |
| 405 | 407 |
setStyleForElement(document.body, {background : rgb});
|
| 406 | 408 |
} |
| 407 |
} |
|
| 408 |
|
|
| 409 |
};
|
|
| 410 |
// initialize constants |
|
| 409 | 411 |
Object.defineProperty( |
| 410 |
p2j,
|
|
| 412 |
me,
|
|
| 411 | 413 |
"prompts", |
| 412 | 414 |
{
|
| 413 | 415 |
get: function () |
| ... | ... | |
| 421 | 423 |
|
| 422 | 424 |
return prompt; |
| 423 | 425 |
} |
| 424 |
} |
|
| 426 |
}, |
|
| 427 |
enumerable : false, |
|
| 428 |
configurable : false |
|
| 425 | 429 |
}); |
| 426 | 430 |
|
| 427 | 431 |
Object.defineProperty( |
| 428 |
p2j,
|
|
| 432 |
me,
|
|
| 429 | 433 |
"hints", |
| 430 | 434 |
{
|
| 431 | 435 |
get: function () |
| ... | ... | |
| 439 | 443 |
|
| 440 | 444 |
return hint; |
| 441 | 445 |
} |
| 442 |
} |
|
| 446 |
}, |
|
| 447 |
enumerable : false, |
|
| 448 |
configurable : false |
|
| 443 | 449 |
}); |
| 444 | 450 |
|
| 445 | 451 |
Object.defineProperty( |
| 446 |
p2j,
|
|
| 452 |
me,
|
|
| 447 | 453 |
"commandSupported", |
| 448 | 454 |
{
|
| 449 | 455 |
get: function () |
| ... | ... | |
| 452 | 458 |
{
|
| 453 | 459 |
return queryCommandSupported[cmd]; |
| 454 | 460 |
} |
| 455 |
} |
|
| 461 |
}, |
|
| 462 |
enumerable : false, |
|
| 463 |
configurable : false |
|
| 456 | 464 |
}); |
| 457 | 465 |
|
| 458 | 466 |
Object.defineProperty( |
| 459 |
p2j,
|
|
| 467 |
me,
|
|
| 460 | 468 |
"webRoot", |
| 461 | 469 |
{
|
| 462 | 470 |
get: function () |
| 463 | 471 |
{
|
| 464 | 472 |
return cfg.webRoot; |
| 465 |
} |
|
| 473 |
}, |
|
| 474 |
enumerable : false, |
|
| 475 |
configurable : false |
|
| 466 | 476 |
}); |
| 467 | 477 |
|
| 468 | 478 |
Object.defineProperty( |
| 469 |
p2j,
|
|
| 479 |
me,
|
|
| 470 | 480 |
"disablePixelManipulation", |
| 471 | 481 |
{
|
| 472 | 482 |
get: function () |
| 473 | 483 |
{
|
| 474 | 484 |
return cfg.disablePixelManipulation; |
| 475 |
} |
|
| 485 |
}, |
|
| 486 |
enumerable : false, |
|
| 487 |
configurable : false |
|
| 476 | 488 |
}); |
| 477 | 489 |
|
| 478 | 490 |
/** The container for application windows and tool bar */ |
| 479 | 491 |
Object.defineProperty( |
| 480 |
p2j,
|
|
| 492 |
me,
|
|
| 481 | 493 |
"container", |
| 482 | 494 |
{
|
| 483 | 495 |
get: function () |
| 484 | 496 |
{
|
| 485 | 497 |
return container; |
| 486 |
} |
|
| 498 |
}, |
|
| 499 |
enumerable : false, |
|
| 500 |
configurable : false |
|
| 487 | 501 |
}); |
| 488 | 502 |
|
| 489 | 503 |
//Dialog help messages |
| 490 | 504 |
const pressEscape = "Press ESCAPE to dismiss this action."; |
| 491 | 505 |
Object.defineProperty( |
| 492 |
p2j,
|
|
| 506 |
me,
|
|
| 493 | 507 |
"pressEscape", |
| 494 | 508 |
{
|
| 495 | 509 |
get: function () |
| 496 | 510 |
{
|
| 497 | 511 |
return i18n.gettext(pressEscape); |
| 498 |
} |
|
| 512 |
}, |
|
| 513 |
enumerable : false, |
|
| 514 |
configurable : false |
|
| 499 | 515 |
}); |
| 500 | 516 |
|
| 501 | 517 |
const pressAnyKey = "Press any key to dismiss this action."; |
| 502 | 518 |
Object.defineProperty( |
| 503 |
p2j,
|
|
| 519 |
me,
|
|
| 504 | 520 |
"pressAnyKey", |
| 505 | 521 |
{
|
| 506 | 522 |
get: function () |
| 507 | 523 |
{
|
| 508 | 524 |
return i18n.gettext(pressAnyKey); |
| 509 |
} |
|
| 510 |
}); |
|
| 511 |
|
|
| 525 |
}, |
|
| 526 |
enumerable : false, |
|
| 527 |
configurable : false |
|
| 528 |
}); |
|
| 529 |
Object.defineProperty( |
|
| 530 |
me, |
|
| 531 |
"dialogs", |
|
| 532 |
{
|
|
| 533 |
get: function () |
|
| 534 |
{
|
|
| 535 |
return function(id) |
|
| 536 |
{
|
|
| 537 |
return DIALOGS[id]; |
|
| 538 |
} |
|
| 539 |
}, |
|
| 540 |
enumerable : false, |
|
| 541 |
configurable : false |
|
| 542 |
}); |
|
| 543 |
Object.defineProperty( |
|
| 544 |
me, |
|
| 545 |
"OPENPOPUP", |
|
| 546 |
{
|
|
| 547 |
get: function () |
|
| 548 |
{
|
|
| 549 |
return OPENPOPUP; |
|
| 550 |
}, |
|
| 551 |
enumerable : false, |
|
| 552 |
configurable : false |
|
| 553 |
}); |
|
| 512 | 554 |
var progressBarControlFunction; |
| 513 | 555 |
dojo.require("dijit.ProgressBar");
|
| 514 | 556 |
|
| ... | ... | |
| 516 | 558 |
{
|
| 517 | 559 |
progressBarControlFunction = createProgressBar("webClientLoadingProgressBar", [0, 50, 100]);
|
| 518 | 560 |
Object.defineProperty( |
| 519 |
p2j,
|
|
| 561 |
me,
|
|
| 520 | 562 |
"setLoadingCheckpoint", |
| 521 | 563 |
{
|
| 522 | 564 |
get: function () |
| 523 | 565 |
{
|
| 524 | 566 |
return progressBarControlFunction; |
| 525 |
} |
|
| 567 |
}, |
|
| 568 |
enumerable : false, |
|
| 569 |
configurable : false |
|
| 526 | 570 |
}); |
| 527 | 571 |
}); |
| 528 | 572 |
|
| ... | ... | |
| 1181 | 1225 |
me.getCodePoint = getCodePoint; |
| 1182 | 1226 |
|
| 1183 | 1227 |
/** |
| 1184 |
* Creates and displays the application modal dialog. |
|
| 1185 |
* |
|
| 1186 |
* @param {String} id
|
|
| 1187 |
* The dialog unique identifier. |
|
| 1188 |
* @param {HtmlElement} container
|
|
| 1189 |
* The container element. |
|
| 1190 |
* @param {String} text
|
|
| 1191 |
* The dialog content message. |
|
| 1192 |
* @param {String} tooltip
|
|
| 1193 |
* The dialog tooltip message. |
|
| 1194 |
* @param {Function} keyDownHandler
|
|
| 1195 |
* The "keydown" event's handler. |
|
| 1196 |
* |
|
| 1197 |
* @return The modal dialog. |
|
| 1198 |
*/ |
|
| 1199 |
function createInternalDialog(id, container, text, tooltip, keyDownHandler) |
|
| 1200 |
{
|
|
| 1201 |
var dlg = new ModalDialog(id, container, text, tooltip, false); |
|
| 1202 |
|
|
| 1203 |
if (keyDownHandler) |
|
| 1204 |
{
|
|
| 1205 |
dlg.addTrigger("keydown", keyDownHandler, true);
|
|
| 1206 |
} |
|
| 1207 |
|
|
| 1208 |
return dlg; |
|
| 1209 |
}; |
|
| 1210 |
|
|
| 1211 |
/** |
|
| 1212 | 1228 |
* Tests if the clipboard dialog is active. |
| 1213 | 1229 |
*/ |
| 1214 | 1230 |
function isDialogDisplayed() |
| 1215 | 1231 |
{
|
| 1216 |
return (dialog != null) && (dialog != undefined);
|
|
| 1232 |
return dialogs.size > 0;
|
|
| 1217 | 1233 |
} |
| 1218 | 1234 |
|
| 1219 | 1235 |
me.isDialogDisplayed = isDialogDisplayed; |
| 1220 | 1236 |
|
| 1221 |
/** |
|
| 1222 |
* Creates and displays the open popup operation dialog. |
|
| 1223 |
* |
|
| 1224 |
* @param {Function} keyDownHandler
|
|
| 1225 |
* The "keydown" event's handler. |
|
| 1226 |
* |
|
| 1227 |
* @return The modal dialog. |
|
| 1228 |
*/ |
|
| 1229 |
function createOpenPopupDialog(keyDownHandler) |
|
| 1230 |
{
|
|
| 1231 |
if (!openPopupDialog || openPopupDialog.disposed) |
|
| 1232 |
{
|
|
| 1233 |
openPopupDialog = createInternalDialog(DIALOGS[OPENPOPUP], |
|
| 1234 |
document.body, |
|
| 1235 |
p2j.prompts(OPENPOPUP), |
|
| 1236 |
p2j.hints(OPENPOPUP), |
|
| 1237 |
keyDownHandler); |
|
| 1238 |
} |
|
| 1239 |
else |
|
| 1240 |
{
|
|
| 1241 |
openPopupDialog.addTrigger("keydown", keyDownHandler, true);
|
|
| 1242 |
openPopupDialog.show(); |
|
| 1243 |
} |
|
| 1244 |
|
|
| 1245 |
return openPopupDialog; |
|
| 1246 |
}; |
|
| 1247 |
|
|
| 1248 |
/** |
|
| 1249 |
* Returns the reference to the window with the target page if this operation is succedded, |
|
| 1250 |
* otherwise null. |
|
| 1251 |
* |
|
| 1252 |
* @param url |
|
| 1253 |
* The target page |
|
| 1254 |
*/ |
|
| 1255 |
function openNewWindow(url) |
|
| 1256 |
{
|
|
| 1257 |
var newWindow; |
|
| 1258 |
|
|
| 1259 |
try |
|
| 1260 |
{
|
|
| 1261 |
newWindow = window.open(url, "_blank"); |
|
| 1262 |
} |
|
| 1263 |
catch(ex) |
|
| 1264 |
{
|
|
| 1265 |
console.error(ex); |
|
| 1266 |
} |
|
| 1267 |
|
|
| 1268 |
if (newWindow && !newWindow.closed) |
|
| 1269 |
{
|
|
| 1270 |
return newWindow; |
|
| 1271 |
} |
|
| 1272 |
|
|
| 1273 |
return null; |
|
| 1274 |
} |
|
| 1275 |
|
|
| 1276 |
/** |
|
| 1277 |
* Retrieve filename from content-disposition header if there is filename key, otherwise |
|
| 1278 |
* returns the provided default file name. |
|
| 1279 |
* |
|
| 1280 |
* @param response |
|
| 1281 |
* The response object |
|
| 1282 |
* @param defaultFileName |
|
| 1283 |
* The provided default file name |
|
| 1284 |
* |
|
| 1285 |
* @returns The value of filename key, otherwise the provided default file name. |
|
| 1286 |
*/ |
|
| 1287 |
function getFileName(response, defaultFileName) |
|
| 1288 |
{
|
|
| 1289 |
var contentDisposition = response.headers.get("Content-disposition");
|
|
| 1290 |
|
|
| 1291 |
if (contentDisposition) |
|
| 1292 |
{
|
|
| 1293 |
var index = contentDisposition.indexOf("filename=");
|
|
| 1294 |
if (index > 0) |
|
| 1295 |
{
|
|
| 1296 |
var filename = contentDisposition.substring(index + 9); |
|
| 1297 |
// remove quotes |
|
| 1298 |
if (filename.length > 0) |
|
| 1299 |
{
|
|
| 1300 |
var startQuote = filename.charAt(0); |
|
| 1301 |
var endQuote = filename.charAt(filename.length - 1); |
|
| 1302 |
if (startQuote == "\"" && endQuote == "\"") |
|
| 1303 |
{
|
|
| 1304 |
filename = filename.substring(1, filename.length - 1); |
|
| 1305 |
} |
|
| 1306 |
|
|
| 1307 |
return filename; |
|
| 1308 |
} |
|
| 1309 |
} |
|
| 1310 |
} |
|
| 1311 |
|
|
| 1312 |
return defaultFileName; |
|
| 1313 |
} |
|
| 1314 |
|
|
| 1315 |
/** |
|
| 1316 |
* Opens a target resource given by its url. |
|
| 1317 |
* |
|
| 1318 |
* @param url |
|
| 1319 |
* The target url |
|
| 1320 |
* @param mimeType |
|
| 1321 |
* The resource mime type |
|
| 1322 |
* @param openInNewWindow |
|
| 1323 |
* The flag indicating that the document should be opened in new tab or window |
|
| 1324 |
*/ |
|
| 1325 |
function openMimeResource(url, mimeType, openInNewWindow) |
|
| 1326 |
{
|
|
| 1327 |
if (openInNewWindow) |
|
| 1328 |
{
|
|
| 1329 |
var newWindow = openNewWindow(url); |
|
| 1330 |
|
|
| 1331 |
if (!newWindow) |
|
| 1332 |
{
|
|
| 1333 |
createOpenPopupDialog( |
|
| 1334 |
function (evt) |
|
| 1335 |
{
|
|
| 1336 |
if (evt.keyCode === keys.ENTER) |
|
| 1337 |
{
|
|
| 1338 |
newWindow = openNewWindow(url); |
|
| 1339 |
|
|
| 1340 |
if (!newWindow) |
|
| 1341 |
{
|
|
| 1342 |
console.error("Failed to open " + url);
|
|
| 1343 |
} |
|
| 1344 |
|
|
| 1345 |
if (openPopupDialog) |
|
| 1346 |
{
|
|
| 1347 |
openPopupDialog.close(); |
|
| 1348 |
} |
|
| 1349 |
} |
|
| 1350 |
}); |
|
| 1351 |
} |
|
| 1352 |
} |
|
| 1353 |
else |
|
| 1354 |
{
|
|
| 1355 |
fetch(url).then(function(response) |
|
| 1356 |
{
|
|
| 1357 |
if (response.ok) |
|
| 1358 |
{
|
|
| 1359 |
var filename = getFileName(response, "filename"); |
|
| 1360 |
response.blob().then(blob => |
|
| 1361 |
{
|
|
| 1362 |
saveBlobAs(filename, new File([blob], filename, {type: mimeType}));
|
|
| 1363 |
}); |
|
| 1364 |
} |
|
| 1365 |
else |
|
| 1366 |
{
|
|
| 1367 |
console.error("Failed to fetch " + url + " with status code " + response.status);
|
|
| 1368 |
} |
|
| 1369 |
}); |
|
| 1370 |
} |
|
| 1371 |
} |
|
| 1372 |
|
|
| 1373 |
me.openMimeResource = openMimeResource; |
|
| 1374 | 1237 |
|
| 1375 | 1238 |
/** |
| 1376 | 1239 |
* Calculates the maximal z-index on the current page. |
| ... | ... | |
| 1815 | 1678 |
|
| 1816 | 1679 |
me.ScreenManager = ScreenManager; |
| 1817 | 1680 |
|
| 1818 |
/** |
|
| 1819 |
* Export the given content to the file on the client file system. |
|
| 1820 |
* |
|
| 1821 |
* @param {String} mimeType
|
|
| 1822 |
* The content mime type |
|
| 1823 |
* @param {String} fileName
|
|
| 1824 |
* The target file name |
|
| 1825 |
* @param {Array} content
|
|
| 1826 |
* The array of strings that represents the exported content. |
|
| 1827 |
*/ |
|
| 1828 |
function saveAs(mimeType, fileName, content) |
|
| 1829 |
{
|
|
| 1830 |
var blob = new File(content, fileName, {type: mimeType});
|
|
| 1831 |
saveBlobAs(fileName, blob) |
|
| 1832 |
} |
|
| 1833 |
|
|
| 1834 |
/** |
|
| 1835 |
* Export the content of the given blob to the file on the client file system. |
|
| 1836 |
* |
|
| 1837 |
* @param {String} fileName
|
|
| 1838 |
* The target file name |
|
| 1839 |
* @param {Blob} blob
|
|
| 1840 |
* The blob that represents the exported content. |
|
| 1841 |
*/ |
|
| 1842 |
function saveBlobAs(fileName, blob) |
|
| 1843 |
{
|
|
| 1844 |
var object_url = URL.createObjectURL(blob); |
|
| 1845 |
var save_link = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
|
|
| 1846 |
var can_use_save_link = "download" in save_link; |
|
| 1847 |
|
|
| 1848 |
if (can_use_save_link) |
|
| 1849 |
{
|
|
| 1850 |
save_link.href = object_url; |
|
| 1851 |
save_link.target = "_blank"; |
|
| 1852 |
save_link.download = fileName; |
|
| 1853 |
save_link.click(); |
|
| 1854 |
} |
|
| 1855 |
else |
|
| 1856 |
{
|
|
| 1857 |
var opened = window.open(object_url, "_blank"); |
|
| 1858 |
if (!opened) |
|
| 1859 |
{
|
|
| 1860 |
window.location.href = object_url; |
|
| 1861 |
} |
|
| 1862 |
} |
|
| 1863 |
|
|
| 1864 |
URL.revokeObjectURL(object_url); |
|
| 1865 |
} |
|
| 1866 |
|
|
| 1867 |
/** Export the given content to the file on the client file system. */ |
|
| 1868 |
me.saveAs = saveAs; |
|
| 1869 |
|
|
| 1870 |
/** Export the content of the given blob to the file on the client file system. */ |
|
| 1871 |
me.saveBlobAs = saveBlobAs; |
|
| 1681 |
// Define protected methods |
|
| 1682 |
(function() |
|
| 1683 |
{
|
|
| 1684 |
/** |
|
| 1685 |
* Export the given content to the file on the client file system. |
|
| 1686 |
* |
|
| 1687 |
* @param {String} mimeType
|
|
| 1688 |
* The content mime type |
|
| 1689 |
* @param {String} fileName
|
|
| 1690 |
* The target file name |
|
| 1691 |
* @param {Array} content
|
|
| 1692 |
* The array of strings that represents the exported content. |
|
| 1693 |
*/ |
|
| 1694 |
function saveAs(mimeType, fileName, content) |
|
| 1695 |
{
|
|
| 1696 |
var blob = new File(content, fileName, {type: mimeType});
|
|
| 1697 |
saveBlobAs(fileName, blob) |
|
| 1698 |
} |
|
| 1699 |
|
|
| 1700 |
/** |
|
| 1701 |
* Export the content of the given blob to the file on the client file system. |
|
| 1702 |
* |
|
| 1703 |
* @param {String} fileName
|
|
| 1704 |
* The target file name |
|
| 1705 |
* @param {Blob} blob
|
|
| 1706 |
* The blob that represents the exported content. |
|
| 1707 |
*/ |
|
| 1708 |
function saveBlobAs(fileName, blob) |
|
| 1709 |
{
|
|
| 1710 |
var object_url = URL.createObjectURL(blob); |
|
| 1711 |
var save_link = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
|
|
| 1712 |
var can_use_save_link = "download" in save_link; |
|
| 1713 |
|
|
| 1714 |
if (can_use_save_link) |
|
| 1715 |
{
|
|
| 1716 |
save_link.href = object_url; |
|
| 1717 |
save_link.target = "_blank"; |
|
| 1718 |
save_link.download = fileName; |
|
| 1719 |
save_link.click(); |
|
| 1720 |
} |
|
| 1721 |
else |
|
| 1722 |
{
|
|
| 1723 |
var opened = window.open(object_url, "_blank"); |
|
| 1724 |
if (!opened) |
|
| 1725 |
{
|
|
| 1726 |
window.location.href = object_url; |
|
| 1727 |
} |
|
| 1728 |
} |
|
| 1729 |
|
|
| 1730 |
URL.revokeObjectURL(object_url); |
|
| 1731 |
} |
|
| 1732 |
|
|
| 1733 |
/** Export the given content to the file on the client file system. */ |
|
| 1734 |
Object.defineProperty( |
|
| 1735 |
me, |
|
| 1736 |
"saveAs", |
|
| 1737 |
{
|
|
| 1738 |
get: function () |
|
| 1739 |
{
|
|
| 1740 |
return saveAs; |
|
| 1741 |
}, |
|
| 1742 |
enumerable : false, |
|
| 1743 |
configurable : false |
|
| 1744 |
}); |
|
| 1745 |
|
|
| 1746 |
/** Export the content of the given blob to the file on the client file system. */ |
|
| 1747 |
Object.defineProperty( |
|
| 1748 |
me, |
|
| 1749 |
"saveBlobAs", |
|
| 1750 |
{
|
|
| 1751 |
get: function () |
|
| 1752 |
{
|
|
| 1753 |
return saveBlobAs; |
|
| 1754 |
}, |
|
| 1755 |
enumerable : false, |
|
| 1756 |
configurable : false |
|
| 1757 |
}); |
|
| 1758 |
})(); |
|
| 1872 | 1759 |
|
| 1873 | 1760 |
/** Defines chained listeners */ |
| 1874 | 1761 |
function ChainedListeners(head, tail) |
| ... | ... | |
| 2026 | 1913 |
changeDisplayStyle(overlay, "block"); |
| 2027 | 1914 |
changeVisibilityStyle(dialog, "visible"); |
| 2028 | 1915 |
setVisible(true); |
| 1916 |
dialogs.add(dialog); |
|
| 2029 | 1917 |
} |
| 2030 | 1918 |
} |
| 2031 | 1919 |
|
| ... | ... | |
| 2038 | 1926 |
changeDisplayStyle(overlay, "none"); |
| 2039 | 1927 |
changeVisibilityStyle(dialog, "collapse"); |
| 2040 | 1928 |
setVisible(false); |
| 1929 |
dialogs.delete(dialog); |
|
| 2041 | 1930 |
var listener = dialog.listeners.get("onHide");
|
| 2042 | 1931 |
if (listener) |
| 2043 | 1932 |
{
|
| ... | ... | |
| 2379 | 2268 |
}); |
| 2380 | 2269 |
|
| 2381 | 2270 |
dialog.disposed = true; |
| 2382 |
|
|
| 2271 |
dialogs.delete(dialog); |
|
| 2383 | 2272 |
if (listener) |
| 2384 | 2273 |
{
|
| 2385 | 2274 |
listener(); |
| new/src/com/goldencode/p2j/ui/client/driver/web/res/p2j.socket.js 2026-03-27 06:37:29 +0000 | ||
|---|---|---|
| 200 | 200 |
** 20260312 Exposed doRedirectToLogoutPage as me.redirectToLogout for use by SSO re-auth module. |
| 201 | 201 |
** Added me.loginPage and me.logoutPage accessors to expose the configured page URLs. |
| 202 | 202 |
** 080 SB 20260310 When safely closing a websocket use code Normal_Closure. |
| 203 |
** 081 SBI 20260326 Refactored to protect openMimeResource access. |
|
| 203 | 204 |
*/ |
| 204 | 205 | |
| 205 | 206 |
/* |
| ... | ... | |
| 4408 | 4409 |
|
| 4409 | 4410 |
var anchor = message[offset + 2]; |
| 4410 | 4411 |
|
| 4411 |
var url = p2j.socket.buildOpenResourceUrl("document", id, mimeType, embedded, local, anchor);
|
|
| 4412 |
offset = offset + 3; |
|
| 4413 |
var webTokenForEmbedded = null; |
|
| 4414 |
if (embedded || local) |
|
| 4415 |
{
|
|
| 4416 |
textLength = me.readInt32BinaryMessage(message, offset); |
|
| 4417 |
offset = offset + 4; |
|
| 4418 |
webTokenForEmbedded = me.readStringBinaryMessageByLength(message, offset, textLength); |
|
| 4419 |
offset = offset + 2 * textLength; |
|
| 4420 |
} |
|
| 4421 |
var url = buildOpenResourceUrl("document", id, mimeType, embedded, local,
|
|
| 4422 |
anchor, webTokenForEmbedded); |
|
| 4412 | 4423 |
|
| 4413 | 4424 |
if (url != null) |
| 4414 | 4425 |
{
|
| ... | ... | |
| 4421 | 4432 |
return element.toLowerCase() == mimeType.toLowerCase(); |
| 4422 | 4433 |
}).length > 0; |
| 4423 | 4434 |
} |
| 4424 |
p2j.openMimeResource(url, mimeType, !fetch);
|
|
| 4435 |
openMimeResource(url, mimeType, !fetch); |
|
| 4425 | 4436 |
} |
| 4426 | 4437 |
|
| 4427 | 4438 |
break; |
| ... | ... | |
| 4854 | 4865 |
* |
| 4855 | 4866 |
* @return The target resource url. |
| 4856 | 4867 |
*/ |
| 4857 |
me.buildOpenResourceUrl = function(handler, path, mimeType, embedded, local, anchor) |
|
| 4868 |
function buildOpenResourceUrl(handler, path, mimeType, embedded, local, |
|
| 4869 |
anchor, webTokenForEmbedded) |
|
| 4858 | 4870 |
{
|
| 4859 | 4871 |
var url = p2j.webRoot; |
| 4860 | 4872 |
|
| ... | ... | |
| 4871 | 4883 |
|
| 4872 | 4884 |
if (local) |
| 4873 | 4885 |
{
|
| 4874 |
url += "&stream=yes"; |
|
| 4886 |
url += "&local=yes"; |
|
| 4887 |
} |
|
| 4888 |
if (webTokenForEmbedded) |
|
| 4889 |
{
|
|
| 4890 |
url = url.concat("&", "api_token", "=", encodeURIComponent(webTokenForEmbedded));
|
|
| 4875 | 4891 |
} |
| 4876 | 4892 |
} |
| 4877 | 4893 |
else |
| ... | ... | |
| 4879 | 4895 |
if (local) |
| 4880 | 4896 |
{
|