mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
chore(site): add generateConnectionSessionId helper function (#27935)
- adds a `generateConnectionSessionId` helper function which reuses our existing `generateRandomString` logic - adds tests for `generateConnectionSessionId` in utils/random.test.ts (utils/random.ts was previously untested) - renames our existing `generateRandomString` function to be `generateRandomBase64String` additional context in https://github.com/coder/coder/pull/27671#discussion_r3731213459 This PR doesn't change any of our UIs. The new `generateConnectionSessionId` function is a piece of frontend plumbing related to DEVEX-663. I originally implemented #27677 so that web terminal connections to workspaces would be identified by uuids--but according to the RFC for DEVEX-663, those connection session ids should be lowercase hexadecimal strings (not uuids).
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useReducer, useState } from "react";
|
||||
import { API } from "#/api/api";
|
||||
import type { Region } from "#/api/typesGenerated";
|
||||
import { generateRandomString } from "#/utils/random";
|
||||
import { generateRandomBase64String } from "#/utils/random";
|
||||
|
||||
const proxyIntervalSeconds = 30; // seconds
|
||||
|
||||
@@ -133,7 +133,7 @@ export const useProxyLatency = (
|
||||
// Add a random query param to the url to make sure we don't get a cached response.
|
||||
// This is important in case there is some caching layer between us and the proxy.
|
||||
const url = new URL(
|
||||
`/latency-check?cache_bust=${generateRandomString(6)}`,
|
||||
`/latency-check?cache_bust=${generateRandomBase64String(6)}`,
|
||||
proxy.path_app_url,
|
||||
);
|
||||
acc[url.toString()] = proxy;
|
||||
|
||||
@@ -25,7 +25,7 @@ import { shouldShowAISeatColumn } from "#/modules/dashboard/entitlements";
|
||||
import { useDashboard } from "#/modules/dashboard/useDashboard";
|
||||
import { RoleSelectorDialog } from "#/modules/roles/RoleSelectorDialog";
|
||||
import { pageTitle } from "#/utils/page";
|
||||
import { generateRandomString } from "#/utils/random";
|
||||
import { generateRandomBase64String } from "#/utils/random";
|
||||
import { ResetPasswordDialog } from "./ResetPasswordDialog";
|
||||
import { UsersPageView } from "./UsersPageView";
|
||||
|
||||
@@ -122,7 +122,7 @@ const UsersPage: React.FC = () => {
|
||||
newPassword:
|
||||
process.env.STORYBOOK === "true"
|
||||
? "hello-storybook"
|
||||
: generateRandomString(12),
|
||||
: generateRandomBase64String(12),
|
||||
});
|
||||
}}
|
||||
onSuspendUser={setUserToSuspend}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { generateConnectionSessionId } from "#/utils/random";
|
||||
|
||||
describe("generateConnectionSessionId", () => {
|
||||
it("is 32 characters long", () => {
|
||||
const id = generateConnectionSessionId();
|
||||
expect(id.length).toBe(32);
|
||||
});
|
||||
|
||||
it("outputs a hexadecimal string", () => {
|
||||
const id = generateConnectionSessionId();
|
||||
const hexRegex = /^[\da-f]+$/;
|
||||
expect(hexRegex.test(id)).toBe(true);
|
||||
});
|
||||
|
||||
it("generates unique values across calls", () => {
|
||||
const numValues = 1000;
|
||||
const ids = new Set(
|
||||
Array.from({ length: numValues }, () => generateConnectionSessionId()),
|
||||
);
|
||||
expect(ids.size).toBe(numValues);
|
||||
});
|
||||
});
|
||||
+20
-10
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* Generate a random hexadecimal string from the specified number of bytes.
|
||||
*/
|
||||
const generateRandomString = (bytes: number): string => {
|
||||
const byteArr = crypto.getRandomValues(new Uint8Array(bytes));
|
||||
return [...byteArr]
|
||||
.map((byte) => byte.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a cryptographically secure random string using the specified number
|
||||
* of bytes then encode with base64.
|
||||
@@ -6,14 +16,14 @@
|
||||
* equal the number of randomly generated bytes.
|
||||
* @see <https://developer.mozilla.org/en-US/docs/Glossary/Base64#encoded_size_increase>
|
||||
*/
|
||||
export const generateRandomString = (bytes: number): string => {
|
||||
const byteArr = window.crypto.getRandomValues(new Uint8Array(bytes));
|
||||
// The types for `map` don't seem to support mapping from one array type to
|
||||
// another and `String.fromCharCode.apply` wants `number[]` so loop like this
|
||||
// instead.
|
||||
const strArr: string[] = [];
|
||||
for (const byte of byteArr) {
|
||||
strArr.push(String.fromCharCode(byte));
|
||||
}
|
||||
return btoa(strArr.join(""));
|
||||
export const generateRandomBase64String = (bytes: number): string => {
|
||||
return btoa(generateRandomString(bytes));
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a 16-byte (32-character) hexadecimal string for identifying
|
||||
* workspace connection sessions.
|
||||
*/
|
||||
export const generateConnectionSessionId = (): string => {
|
||||
return generateRandomString(16);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user