Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
461a3de
fix #1750
isc-klu Apr 13, 2026
ff4d5a9
Merge branch 'intersystems-community:master' into master
isc-klu May 13, 2026
bbb6e86
Merge branch 'intersystems-community:master' into master
isc-klu May 27, 2026
4677311
Merge branch 'intersystems-community:master' into master
isc-klu Jun 18, 2026
2d6e248
working?
isc-klu Jun 23, 2026
d5c4b34
with-authorization-class
isc-klu Jun 30, 2026
db6015b
clean
isc-klu Jun 30, 2026
c033875
simp
isc-klu Jun 30, 2026
82b0800
simp
isc-klu Jun 30, 2026
70d638d
simp
isc-klu Jun 30, 2026
8d61380
simp
isc-klu Jun 30, 2026
3f812e2
draft (still buggy)
isc-klu Jul 2, 2026
5bae510
for diff
isc-klu Jul 2, 2026
255b724
fixing
isc-klu Jul 2, 2026
2d4a712
stage
isc-klu Jul 2, 2026
c6ac3a5
simp
isc-klu Jul 2, 2026
ea524a6
simp
isc-klu Jul 2, 2026
ff0e4bc
clean
isc-klu Jul 2, 2026
3b74eba
clean
isc-klu Jul 2, 2026
672d683
move types to server manager
isc-klu Jul 2, 2026
0e83f09
precisely typed
isc-klu Jul 7, 2026
89c8d03
beta server manager API
isc-klu Jul 8, 2026
3a0c457
Merge branch 'master' into oauth2
isc-klu Jul 8, 2026
71e33e2
upgrade types
isc-klu Jul 9, 2026
55cd8c3
Merge branch 'master' into oauth2
isc-klu Jul 24, 2026
087d191
lint
isc-klu Jul 24, 2026
ca07b90
Brett's feedback
isc-klu Jul 29, 2026
85b2915
Brett's feedback
isc-klu Aug 6, 2026
256a9eb
Brett's feedback
isc-klu Aug 6, 2026
521c646
Brett's feedback
isc-klu Aug 7, 2026
48ff973
trim
isc-klu Aug 7, 2026
3c0a9a5
upgrade servermanager types
isc-klu Aug 7, 2026
e5be7fb
Don't open low-code editors when not using Basic Auth
isc-klu Aug 7, 2026
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
8 changes: 4 additions & 4 deletions 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1718,7 +1718,7 @@
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@intersystems-community/intersystems-servermanager": "^3.10.2",
"@intersystems-community/intersystems-servermanager": "^3.14.0",
"@types/istextorbinary": "2.3.1",
"@types/minimatch": "6.0.0",
"@types/mocha": "^10.0.10",
Expand Down
72 changes: 34 additions & 38 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const DEFAULT_API_VERSION = 1;
const DEFAULT_SERVER_VERSION = "2016.2.0";
import * as Atelier from "./atelier";
import { isfsConfig } from "../utils/FileProviderUtil";
import { Authorization, IServerSpec } from "@intersystems-community/intersystems-servermanager";

// Map of the authRequest promises for each `username@http(s)://host:port/pathPrefix` target to avoid concurrency issues
const authRequestMap = new Map<string, Promise<any>>();
Expand Down Expand Up @@ -46,20 +47,19 @@ export async function logoutOfSessions(sessions?: string[]): Promise<void> {
).then(() => {}); // Returned object isn't needed
}

interface ConnectionSettings {
export interface ConnectionSettings {
serverName: string;
active: boolean;
apiVersion: number;
serverVersion: string;
apiVersion?: number;
serverVersion?: string;
https: boolean;
host: string;
port: number;
superserverPort?: number;
pathPrefix: string;
pathPrefix?: string;
ns: string;
username: string;
password: string;
docker: boolean;
auth: Authorization;
docker?: boolean;
dockerService?: string;
}

Expand All @@ -79,15 +79,15 @@ export class AtelierAPI {
}

public get config(): ConnectionSettings {
const { serverName, active = false, https = false, pathPrefix = "", username } = this._config;
const { serverName, active = false, https = false, pathPrefix = "", auth } = this._config;
const ns = this.namespace || this._config.ns;
const wsKey = this.configName.toLowerCase();
const host = this.externalServer ? this._config.host : workspaceState.get(wsKey + ":host", this._config.host);
const port = this.externalServer ? this._config.port : workspaceState.get(wsKey + ":port", this._config.port);
const superserverPort = this.externalServer
? this._config.superserverPort
: workspaceState.get(wsKey + ":superserverPort", this._config.superserverPort);
const password = workspaceState.get(wsKey + ":password", this._config.password);
auth.resolve({ accessToken: workspaceState.get(wsKey + ":password", undefined) });
const apiVersion = workspaceState.get(wsKey + ":apiVersion", DEFAULT_API_VERSION);
const serverVersion = workspaceState.get(wsKey + ":serverVersion", DEFAULT_SERVER_VERSION);
const docker = workspaceState.get(wsKey + ":docker", false);
Expand All @@ -103,8 +103,7 @@ export class AtelierAPI {
superserverPort,
pathPrefix,
ns,
username,
password,
auth,
docker,
dockerService,
};
Expand Down Expand Up @@ -167,14 +166,12 @@ export class AtelierAPI {
* Manually set the connection spec for this object,
* where `connSpec` is the return value of `getResolvedConnectionSpec()`.
*/
public setConnSpec(serverName: string, connSpec: any): void {
public setConnSpec(serverName: string, connSpec: IServerSpec): void {
const {
webServer: { scheme, host, port, pathPrefix = "" },
username,
password,
auth,
} = connSpec;
this._config.username = username;
this._config.password = password;
this._config.auth = auth;
this._config.https = scheme == "https";
this._config.host = host;
this._config.port = port;
Expand Down Expand Up @@ -225,12 +222,12 @@ export class AtelierAPI {

/** Return the key for getting values from connection-specific Maps for this connection */
public mapKey(): string {
const { host, https, port, username } = this.config;
const { host, https, port, auth } = this.config;
let pathPrefix = this._config.pathPrefix || "";
if (pathPrefix.length && !pathPrefix.startsWith("/")) {
pathPrefix = "/" + pathPrefix;
}
return `${username}@http${https ? "s" : ""}://${host}:${port}${pathPrefix}`;
return `${auth.username}@http${https ? "s" : ""}://${host}:${port}${pathPrefix}`;
}

private setConnection(workspaceFolderName: string, namespace?: string): void {
Expand All @@ -255,8 +252,7 @@ export class AtelierAPI {
if (serverName !== "") {
const {
webServer: { scheme, host, port, pathPrefix = "" },
username,
password,
auth,
superServer,
} = getResolvedConnectionSpec(serverName, config("intersystems.servers", workspaceFolderName).get(serverName));
this._config = {
Expand All @@ -269,8 +265,7 @@ export class AtelierAPI {
host,
port,
superserverPort: superServer?.port,
username,
password,
auth,
pathPrefix,
docker: false,
};
Expand All @@ -280,8 +275,7 @@ export class AtelierAPI {
if (resolvedSpec) {
const {
webServer: { scheme, host, port, pathPrefix = "" },
username,
password,
auth,
superServer,
} = resolvedSpec;
this._config = {
Expand All @@ -294,21 +288,24 @@ export class AtelierAPI {
host,
port,
superserverPort: superServer?.port,
username,
password,
auth,
pathPrefix,
docker: true,
dockerService: conn["docker-compose"].service,
};
} else {
this._config = conn;
this._config.ns = ns;
this._config.serverName = "";
this._config = {
...conn,
ns,
serverName: "",
};
}
} else {
this._config = conn;
this._config.ns = ns;
this._config.serverName = "";
this._config = {
...conn,
ns,
serverName: "",
};
}
}

Expand All @@ -332,7 +329,7 @@ export class AtelierAPI {
headers?: any,
options?: any
): Promise<any> {
const { active, apiVersion, host, port, username, password, https } = this.config;
const { active, apiVersion, host, port, https } = this.config;
if (!active || !port || !host) {
return Promise.reject();
}
Expand Down Expand Up @@ -385,12 +382,11 @@ export class AtelierAPI {
let auth: Promise<any>;
let authRequest = authRequestMap.get(mapKey);
if (cookies.length || (method === "HEAD" && !originalPath)) {
auth = Promise.resolve(cookies);

// Only send basic authorization if username and password specified (including blank, for unauthenticated access)
if (typeof username === "string" && typeof password === "string") {
headers["Authorization"] = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
if (!cookies.length && this.config.auth.resolved()) {
headers["Authorization"] = this.config.auth.httpAuthorizationHeader;
Comment thread
isc-klu marked this conversation as resolved.
}
auth = Promise.resolve(cookies);
} else if (!cookies.length) {
if (!authRequest) {
// Recursion point
Expand Down Expand Up @@ -465,7 +461,7 @@ export class AtelierAPI {
if (this.wsOrFile && !checkingConnection) {
setTimeout(() => {
checkConnection(
password ? true : false,
this.config.auth.resolved(),
typeof this.wsOrFile === "object" ? this.wsOrFile : undefined,
true
);
Expand Down
4 changes: 2 additions & 2 deletions src/commands/connectFolderToServerNamespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
serverManagerApi,
resolveUsernameAndPassword,
} from "../extension";
import { handleError, isUnauthenticated, notIsfs, displayableUri } from "../utils";
import { handleError, notIsfs, displayableUri } from "../utils";

interface ConnSettings {
server: string;
Expand Down Expand Up @@ -80,7 +80,7 @@ export async function connectFolderToServerNamespace(): Promise<void> {
.serverInfo(false)
.then((data) => data.result.content.namespaces)
.catch(async (error) => {
if (error?.statusCode == 401 && isUnauthenticated(api.config.username)) {
if (error?.statusCode == 401 && !api.config.auth.resolved()) {
// Attempt to resolve username and password and try again
const newSpec = await resolveUsernameAndPassword(api.config.serverName, connSpec);
if (newSpec) {
Expand Down
15 changes: 3 additions & 12 deletions src/commands/restDebugPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,8 @@ export class RESTDebugPanel {
form.onchange = () => sendData(false);
button.onclick = () => sendData(true);
// Bubble change events up to the form
bodyContent.onchange = headersText.onchange =
paramsText.onchange = path.onchange =
bodyContent.onchange = headersText.onchange =
paramsText.onchange = path.onchange =
() => form.dispatchEvent(new Event("change"));
</script>
</body>
Expand Down Expand Up @@ -548,16 +548,7 @@ export class RESTDebugPanel {
.trim();
}
});
if (
headers["authorization"] == undefined &&
typeof api.config.username === "string" &&
typeof api.config.password === "string"
) {
// Use the server connection's auth if the user didn't specify any
headers["authorization"] = `Basic ${Buffer.from(`${api.config.username}:${api.config.password}`).toString(
"base64"
)}`;
}
headers["authorization"] = headers["authorization"] ?? (api.config.auth.httpAuthorizationHeader || "");
const hasBody =
typeof message.bodyContent == "string" && message.bodyContent != "" && message.bodyType != "No Body";
if (hasBody) {
Expand Down
9 changes: 6 additions & 3 deletions src/commands/serverActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type ServerAction = { detail: string; id: string; label: string; rawLink?: strin
export async function serverActions(): Promise<void> {
const { apiTarget, configName: workspaceFolder } = connectionTarget();
const api = new AtelierAPI(apiTarget);
const { active, host = "", ns = "", https, port = 0, pathPrefix, username, docker } = api.config;
const { active, host = "", ns = "", https, port = 0, pathPrefix, auth, docker } = api.config;
const explorerCount = (await explorerProvider.getChildren()).length;
if (!explorerCount && (!docker || host === "")) {
await vscode.commands.executeCommand("ObjectScriptExplorer.focus");
Expand Down Expand Up @@ -152,7 +152,7 @@ export async function serverActions(): Promise<void> {
.replace("${serverAuth}", "")
.replace("${ns}", nsEncoded)
.replace("${namespace}", ns == "%SYS" ? "sys" : nsEncoded.toLowerCase())
.replace("${username}", username)
.replace("${username}", auth.username)
.replace("${classname}", classname)
.replace("${classnameEncoded}", classnameEncoded)
.replace("${project}", project);
Expand Down Expand Up @@ -248,7 +248,10 @@ export async function serverActions(): Promise<void> {
if (addin) {
sendStudioAddinTelemetryEvent(addin.label);
let params = `Namespace=${nsEncoded}`;
params += `&User=${encodeURIComponent(username)}`;
const username = auth.username;
if (!username.includes("*")) {
params += `&User=${encodeURIComponent(username)}`;
}
if (project != "") {
params += `&Project=${encodeURIComponent(project)}`;
}
Expand Down
6 changes: 5 additions & 1 deletion src/commands/webSocketTerminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,12 @@ class WebSocketTerminal implements vscode.Pseudoterminal {
// Set terminal properties
this._hideCursorWrite("\x1b]633;P;HasRichCommandDetection=True\x07");
// Print the opening message
const username = api.config.auth.username;
const identity = username.includes("*")
? `using \x1b[0m\x1b[3m${username.slice(1, -1)}\x1b[0m\r\n`
: `as \x1b[0m\x1b[3m${username}\x1b[0m\r\n`;
this._hideCursorWrite(
`\x1b[32mConnected to \x1b[0m\x1b[4m${api.config.host}:${api.config.port}${api.config.pathPrefix}\x1b[0m\x1b[32m as \x1b[0m\x1b[3m${api.config.username}\x1b[0m\r\n`
`\x1b[32mConnected to \x1b[0m\x1b[4m${api.config.host}:${api.config.port}${api.config.pathPrefix}\x1b[0m\x1b[32m ${identity}`
);
// Add event handlers to the socket
this._socket
Expand Down
Loading