mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Prevent slash duplication in request URLs
Closes #44269 Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
parent
68cfb8d720
commit
f7e4b78f1d
@ -40,7 +40,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"camelize-ts": "^3.0.0",
|
||||
"url-join": "^5.0.0",
|
||||
"url-template": "^3.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import urlJoin from "url-join";
|
||||
import { parseTemplate } from "url-template";
|
||||
import type { KeycloakAdminClient } from "../client.js";
|
||||
import {
|
||||
@ -6,6 +5,7 @@ import {
|
||||
NetworkError,
|
||||
parseResponse,
|
||||
} from "../utils/fetchWithError.js";
|
||||
import { joinPath } from "../utils/joinPath.js";
|
||||
import { stringifyQueryParams } from "../utils/stringifyQueryParams.js";
|
||||
|
||||
// constants
|
||||
@ -53,7 +53,7 @@ export class Agent {
|
||||
#client: KeycloakAdminClient;
|
||||
#basePath: string;
|
||||
#getBaseParams?: () => Record<string, any>;
|
||||
#getBaseUrl?: () => string;
|
||||
#getBaseUrl: () => string;
|
||||
|
||||
constructor({
|
||||
client,
|
||||
@ -199,12 +199,6 @@ export class Agent {
|
||||
returnResourceIdInLocationHeader?: { field: string };
|
||||
headers?: [string, string][] | Record<string, string> | Headers;
|
||||
}) {
|
||||
const newPath = urlJoin(this.#basePath, path);
|
||||
|
||||
// Parse template and replace with values from urlParams
|
||||
const pathTemplate = parseTemplate(newPath);
|
||||
const parsedPath = pathTemplate.expand(urlParams);
|
||||
const url = new URL(`${this.#getBaseUrl?.() ?? ""}${parsedPath}`);
|
||||
const requestOptions = { ...this.#client.getRequestOptions() };
|
||||
const requestHeaders = new Headers([
|
||||
...new Headers(requestOptions.headers).entries(),
|
||||
@ -243,6 +237,10 @@ export class Agent {
|
||||
Object.assign(searchParams, queryParams);
|
||||
}
|
||||
|
||||
const url = new URL(this.#getBaseUrl());
|
||||
const pathTemplate = parseTemplate(joinPath(this.#basePath, path));
|
||||
|
||||
url.pathname = joinPath(url.pathname, pathTemplate.expand(urlParams));
|
||||
url.search = stringifyQueryParams(searchParams);
|
||||
|
||||
try {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import camelize from "camelize-ts";
|
||||
import { parseTemplate } from "url-template";
|
||||
import { defaultBaseUrl, defaultRealm } from "./constants.js";
|
||||
import { fetchWithError } from "./fetchWithError.js";
|
||||
import { joinPath } from "./joinPath.js";
|
||||
import { stringifyQueryParams } from "./stringifyQueryParams.js";
|
||||
|
||||
export type GrantTypes = "client_credentials" | "password" | "refresh_token";
|
||||
@ -68,10 +70,17 @@ const encodeFormURIComponent = (data: string) =>
|
||||
encodeRFC3986URIComponent(data).replaceAll("%20", "+");
|
||||
|
||||
export const getToken = async (settings: Settings): Promise<TokenResponse> => {
|
||||
// Construct URL
|
||||
const baseUrl = settings.baseUrl || defaultBaseUrl;
|
||||
const realmName = settings.realmName || defaultRealm;
|
||||
const url = `${baseUrl}/realms/${realmName}/protocol/openid-connect/token`;
|
||||
const url = new URL(settings.baseUrl ?? defaultBaseUrl);
|
||||
const pathTemplate = parseTemplate(
|
||||
"/realms/{realmName}/protocol/openid-connect/token",
|
||||
);
|
||||
|
||||
url.pathname = joinPath(
|
||||
url.pathname,
|
||||
pathTemplate.expand({
|
||||
realmName: settings.realmName ?? defaultRealm,
|
||||
}),
|
||||
);
|
||||
|
||||
// Prepare credentials for openid-connect token request
|
||||
// ref: http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
||||
|
||||
22
js/libs/keycloak-admin-client/src/utils/joinPath.ts
Normal file
22
js/libs/keycloak-admin-client/src/utils/joinPath.ts
Normal file
@ -0,0 +1,22 @@
|
||||
const PATH_SEPARATOR = "/";
|
||||
|
||||
export function joinPath(...paths: string[]) {
|
||||
const normalizedPaths = paths.map((path, index) => {
|
||||
const isFirst = index === 0;
|
||||
const isLast = index === paths.length - 1;
|
||||
|
||||
// Strip out any leading slashes from the path.
|
||||
if (!isFirst && path.startsWith(PATH_SEPARATOR)) {
|
||||
path = path.slice(1);
|
||||
}
|
||||
|
||||
// Strip out any trailing slashes from the path.
|
||||
if (!isLast && path.endsWith(PATH_SEPARATOR)) {
|
||||
path = path.slice(0, -1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}, []);
|
||||
|
||||
return normalizedPaths.join(PATH_SEPARATOR);
|
||||
}
|
||||
@ -5,8 +5,8 @@ import { KeycloakAdminClient } from "../src/client.js";
|
||||
import type ClientRepresentation from "../src/defs/clientRepresentation.js";
|
||||
import type GroupRepresentation from "../src/defs/groupRepresentation.js";
|
||||
import type RoleRepresentation from "../src/defs/roleRepresentation.js";
|
||||
import type { SubGroupQuery } from "../src/resources/groups.js";
|
||||
import { credentials } from "./constants.js";
|
||||
import { SubGroupQuery } from "../src/resources/groups.js";
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
|
||||
23
js/libs/keycloak-admin-client/test/joinPaths.spec.ts
Normal file
23
js/libs/keycloak-admin-client/test/joinPaths.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { expect } from "chai";
|
||||
import { joinPath } from "../lib/utils/joinPath.js";
|
||||
|
||||
describe("joinPath", () => {
|
||||
it("returns an empty string when no paths are provided", () => {
|
||||
expect(joinPath()).to.equal("");
|
||||
});
|
||||
|
||||
it("joins paths", () => {
|
||||
expect(joinPath("foo", "bar", "baz")).to.equal("foo/bar/baz");
|
||||
expect(joinPath("foo", "/bar", "baz")).to.equal("foo/bar/baz");
|
||||
expect(joinPath("foo", "bar/", "baz")).to.equal("foo/bar/baz");
|
||||
expect(joinPath("foo", "/bar/", "baz")).to.equal("foo/bar/baz");
|
||||
});
|
||||
|
||||
it("joins paths with leading slashes", () => {
|
||||
expect(joinPath("/foo", "bar", "baz")).to.equal("/foo/bar/baz");
|
||||
});
|
||||
|
||||
it("joins paths with trailing slashes", () => {
|
||||
expect(joinPath("foo", "bar", "baz/")).to.equal("foo/bar/baz/");
|
||||
});
|
||||
});
|
||||
9
js/pnpm-lock.yaml
generated
9
js/pnpm-lock.yaml
generated
@ -331,9 +331,6 @@ importers:
|
||||
camelize-ts:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
url-join:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
url-template:
|
||||
specifier: ^3.1.1
|
||||
version: 3.1.1
|
||||
@ -4206,10 +4203,6 @@ packages:
|
||||
uri-js@4.4.1:
|
||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||
|
||||
url-join@5.0.0:
|
||||
resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
url-template@3.1.1:
|
||||
resolution: {integrity: sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
@ -8702,8 +8695,6 @@ snapshots:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
url-join@5.0.0: {}
|
||||
|
||||
url-template@3.1.1: {}
|
||||
|
||||
use-react-router-breadcrumbs@4.0.1(react-router-dom@6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user