Revert "fix: sync and script keycloak"
All checks were successful
Build & Deploy on Dev / build (push) Successful in 2m11s
All checks were successful
Build & Deploy on Dev / build (push) Successful in 2m11s
This reverts commit d667ad9173.
This commit is contained in:
parent
d667ad9173
commit
c5c19b6d5e
12 changed files with 33 additions and 2444 deletions
|
|
@ -4,8 +4,8 @@ const KC_URL = process.env.KC_URL;
|
|||
const KC_REALMS = process.env.KC_REALMS;
|
||||
const KC_CLIENT_ID = process.env.KC_SERVICE_ACCOUNT_CLIENT_ID;
|
||||
const KC_SECRET = process.env.KC_SERVICE_ACCOUNT_SECRET;
|
||||
// const AUTH_ACCOUNT_SECRET = process.env.AUTH_ACCOUNT_SECRET;
|
||||
// const API_KEY = process.env.API_KEY;
|
||||
const AUTH_ACCOUNT_SECRET = process.env.AUTH_ACCOUNT_SECRET;
|
||||
const API_KEY = process.env.API_KEY;
|
||||
|
||||
let token: string | null = null;
|
||||
let decoded: DecodedJwt | null = null;
|
||||
|
|
@ -165,119 +165,16 @@ export async function getUserList(first = "", max = "", search = "") {
|
|||
|
||||
if (!res) return false;
|
||||
if (!res.ok) {
|
||||
const errorText = await res.text();
|
||||
return Boolean(console.error("Keycloak Error Response: ", errorText));
|
||||
return Boolean(console.error("Keycloak Error Response: ", await res.json()));
|
||||
}
|
||||
|
||||
// Get raw text first to handle potential JSON parsing errors
|
||||
const rawText = await res.text();
|
||||
|
||||
// Log response size for debugging
|
||||
console.log(`[getUserList] Response size: ${rawText.length} bytes`);
|
||||
|
||||
try {
|
||||
const data = JSON.parse(rawText) as any[];
|
||||
return data.map((v: Record<string, string>) => ({
|
||||
id: v.id,
|
||||
username: v.username,
|
||||
firstName: v.firstName,
|
||||
lastName: v.lastName,
|
||||
email: v.email,
|
||||
enabled: v.enabled,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error(`[getUserList] Failed to parse JSON response:`);
|
||||
console.error(`[getUserList] Response preview (first 500 chars):`, rawText.substring(0, 500));
|
||||
console.error(`[getUserList] Response preview (last 200 chars):`, rawText.slice(-200));
|
||||
throw new Error(
|
||||
`Failed to parse Keycloak response as JSON. Response may be truncated or malformed.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all keycloak users with pagination to avoid response size limits
|
||||
*
|
||||
* Client must have permission to manage realm's user
|
||||
*
|
||||
* @returns user list if success, false otherwise.
|
||||
*/
|
||||
export async function getAllUsersPaginated(
|
||||
search: string = "",
|
||||
batchSize: number = 100,
|
||||
): Promise<
|
||||
| Array<{
|
||||
id: string;
|
||||
username: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
email?: string;
|
||||
enabled: boolean;
|
||||
}>
|
||||
| false
|
||||
> {
|
||||
const allUsers: any[] = [];
|
||||
let first = 0;
|
||||
let hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
const res = await fetch(
|
||||
`${KC_URL}/admin/realms/${KC_REALMS}/users?first=${first}&max=${batchSize}${search ? `&search=${search}` : ""}`,
|
||||
{
|
||||
headers: {
|
||||
authorization: `Bearer ${await getToken()}`,
|
||||
"content-type": `application/json`,
|
||||
},
|
||||
},
|
||||
).catch((e) => console.log("Keycloak Error: ", e));
|
||||
|
||||
if (!res) return false;
|
||||
if (!res.ok) {
|
||||
const errorText = await res.text();
|
||||
console.error("Keycloak Error Response: ", errorText);
|
||||
return false;
|
||||
}
|
||||
|
||||
const rawText = await res.text();
|
||||
|
||||
try {
|
||||
const batch = JSON.parse(rawText) as any[];
|
||||
|
||||
if (batch.length === 0) {
|
||||
hasMore = false;
|
||||
} else {
|
||||
allUsers.push(...batch);
|
||||
first += batch.length;
|
||||
hasMore = batch.length === batchSize;
|
||||
|
||||
// Log progress for large datasets
|
||||
if (allUsers.length % 500 === 0) {
|
||||
console.log(`[getAllUsersPaginated] Fetched ${allUsers.length} users so far...`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[getAllUsersPaginated] Failed to parse JSON response at offset ${first}:`);
|
||||
console.error(
|
||||
`[getAllUsersPaginated] Response preview (first 500 chars):`,
|
||||
rawText.substring(0, 500),
|
||||
);
|
||||
console.error(
|
||||
`[getAllUsersPaginated] Response preview (last 200 chars):`,
|
||||
rawText.slice(-200),
|
||||
);
|
||||
throw new Error(`Failed to parse Keycloak response as JSON at batch starting at ${first}.`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[getAllUsersPaginated] Total users fetched: ${allUsers.length}`);
|
||||
|
||||
return allUsers.map((v: any) => ({
|
||||
return ((await res.json()) as any[]).map((v: Record<string, string>) => ({
|
||||
id: v.id,
|
||||
username: v.username,
|
||||
firstName: v.firstName,
|
||||
lastName: v.lastName,
|
||||
email: v.email,
|
||||
enabled: v.enabled === true || v.enabled === "true",
|
||||
enabled: v.enabled,
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -323,34 +220,17 @@ export async function getUserListOrg(first = "", max = "", search = "", userIds:
|
|||
|
||||
if (!res) return false;
|
||||
if (!res.ok) {
|
||||
const errorText = await res.text();
|
||||
return Boolean(console.error("Keycloak Error Response: ", errorText));
|
||||
return Boolean(console.error("Keycloak Error Response: ", await res.json()));
|
||||
}
|
||||
|
||||
// Get raw text first to handle potential JSON parsing errors
|
||||
const rawText = await res.text();
|
||||
|
||||
try {
|
||||
const data = JSON.parse(rawText) as any[];
|
||||
return data.map((v: Record<string, string>) => ({
|
||||
id: v.id,
|
||||
username: v.username,
|
||||
firstName: v.firstName,
|
||||
lastName: v.lastName,
|
||||
email: v.email,
|
||||
enabled: v.enabled,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error(`[getUserListOrg] Failed to parse JSON response:`);
|
||||
console.error(
|
||||
`[getUserListOrg] Response preview (first 500 chars):`,
|
||||
rawText.substring(0, 500),
|
||||
);
|
||||
console.error(`[getUserListOrg] Response preview (last 200 chars):`, rawText.slice(-200));
|
||||
throw new Error(
|
||||
`Failed to parse Keycloak response as JSON. Response may be truncated or malformed.`,
|
||||
);
|
||||
}
|
||||
return ((await res.json()) as any[]).map((v: Record<string, string>) => ({
|
||||
id: v.id,
|
||||
username: v.username,
|
||||
firstName: v.firstName,
|
||||
lastName: v.lastName,
|
||||
email: v.email,
|
||||
enabled: v.enabled,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function getUserCountOrg(first = "", max = "", search = "", userIds: string[] = []) {
|
||||
|
|
@ -564,12 +444,10 @@ export async function getRoles(name?: string, token?: string) {
|
|||
}));
|
||||
}
|
||||
|
||||
// Return single role object
|
||||
return {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
};
|
||||
// return {
|
||||
// id: data.id,
|
||||
// name: data.name,
|
||||
// };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -894,73 +772,6 @@ export async function changeUserPassword(userId: string, newPassword: string) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user attributes in Keycloak
|
||||
*
|
||||
* @param userId - Keycloak user ID
|
||||
* @param attributes - Object containing attribute names and their values (as arrays)
|
||||
* @returns true if success, false otherwise
|
||||
*/
|
||||
export async function updateUserAttributes(
|
||||
userId: string,
|
||||
attributes: Record<string, string[]>,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
// Get existing user data to preserve other attributes
|
||||
const existingUser = await getUser(userId);
|
||||
|
||||
if (!existingUser) {
|
||||
console.error(`User ${userId} not found in Keycloak`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Merge existing attributes with new attributes
|
||||
// IMPORTANT: Spread all existing user fields to preserve firstName, lastName, email, etc.
|
||||
// The Keycloak PUT endpoint performs a full update, so we must include all fields
|
||||
const updatedAttributes = {
|
||||
...existingUser,
|
||||
attributes: {
|
||||
...(existingUser.attributes || {}),
|
||||
...attributes,
|
||||
},
|
||||
};
|
||||
|
||||
console.log(
|
||||
`[updateUserAttributes] Sending to Keycloak:`,
|
||||
JSON.stringify(updatedAttributes, null, 2),
|
||||
);
|
||||
|
||||
const res = await fetch(`${KC_URL}/admin/realms/${KC_REALMS}/users/${userId}`, {
|
||||
headers: {
|
||||
authorization: `Bearer ${await getToken()}`,
|
||||
"content-type": "application/json",
|
||||
},
|
||||
method: "PUT",
|
||||
body: JSON.stringify(updatedAttributes),
|
||||
}).catch((e) => {
|
||||
console.error(`[updateUserAttributes] Network error:`, e);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (!res) {
|
||||
console.error(`[updateUserAttributes] No response from Keycloak`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const errorText = await res.text();
|
||||
console.error(`[updateUserAttributes] Keycloak Error (${res.status}):`, errorText);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(`[updateUserAttributes] Successfully updated attributes for user ${userId}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`[updateUserAttributes] Error updating attributes for user ${userId}:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to reset password
|
||||
export async function resetPassword(username: string) {
|
||||
try {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue