แก้ปัญหา password inconsistency ระหว่างการสร้าง user ใหม่กับ reset-password endpoint โดยใช้ birthDate จาก profile ที่ save แล้วแทน input data
All checks were successful
Build & Deploy on Dev / build (push) Successful in 1m0s

This commit is contained in:
harid 2026-07-14 11:41:04 +07:00
parent af88dffbbe
commit cd057bf81d
2 changed files with 246 additions and 136 deletions

View file

@ -38,15 +38,11 @@ import {
removeProfileInOrganize,
setLogDataDiff,
} from "../interfaces/utils";
import { getRoles } from "../keycloak";
import {
addUserRoles,
createUser,
getRoleMappings,
getRoles,
getUserByUsername,
removeUserRoles,
updateUserAttributes,
} from "../keycloak";
syncKeycloakForProfile,
handleKeycloakError,
} from "../keycloak/user-operations";
import { CreatePosMasterHistoryOfficer } from "./PositionService";
import { getOrgFullName, getPosMasterNo } from "../utils/org-formatting";
import CallAPI from "../interfaces/call-api";
@ -141,8 +137,8 @@ export interface ExecutionContext {
* return result success count
*
* Keycloak: operations (createUser/addUserRoles/removeUserRoles/updateUserAttributes)
* transaction preserve behavior Keycloak rollback
* DB rollback Keycloak operation Keycloak
* DB transaction profile.birthDate save
* Keycloak rollback - sync profile Keycloak user
*
* Design note: แก้ปัญหา "Circular Dependency" API Org API
* resultData Org profile
@ -485,117 +481,6 @@ export class ExecuteOfficerProfileService {
? _null
: calculateRetireLaw(item.bodyProfile.birthDate);
let userKeycloakId: any;
let result: any;
console.log(
"[ExecuteOfficerProfileService] Checking Keycloak user for citizenId:",
item.bodyProfile.citizenId,
);
const checkUser = await getUserByUsername(item.bodyProfile.citizenId);
console.log(
"[ExecuteOfficerProfileService] Keycloak user exists:",
checkUser.length > 0,
);
if (checkUser.length == 0) {
console.log("[ExecuteOfficerProfileService] Creating new Keycloak user");
let password = item.bodyProfile.citizenId;
if (item.bodyProfile.birthDate != null) {
const _date = new Date(item.bodyProfile.birthDate.toDateString())
.getDate()
.toString()
.padStart(2, "0");
const _month = (
new Date(item.bodyProfile.birthDate.toDateString()).getMonth() + 1
)
.toString()
.padStart(2, "0");
const _year = new Date(item.bodyProfile.birthDate.toDateString()).getFullYear() + 543;
password = `${_date}${_month}${_year}`;
}
console.log(
"[ExecuteOfficerProfileService] Calling createUser for:",
item.bodyProfile.citizenId,
);
console.log(
"[ExecuteOfficerProfileService] createUser data - firstName:",
item.bodyProfile.firstName,
"lastName:",
item.bodyProfile.lastName,
);
// กรอง "." ออกจาก firstName ก่อนส่งไป keycloak (ป้องกัน . หรืออักขระอื่นๆ)
const sanitizedFirstName = item.bodyProfile.firstName?.replace(/\./g, "") ?? "";
userKeycloakId = await createUser(item.bodyProfile.citizenId, password, {
firstName: sanitizedFirstName,
lastName: item.bodyProfile.lastName,
});
if (
userKeycloakId &&
typeof userKeycloakId === "object" &&
userKeycloakId.errorMessage
) {
console.error(
"[ExecuteOfficerProfileService] createUser FAILED - field:",
userKeycloakId.field,
"errorMessage:",
userKeycloakId.errorMessage,
"params:",
userKeycloakId.params,
);
throw new HttpError(
HttpStatusCode.BAD_REQUEST,
`Keycloak validation failed: ${userKeycloakId.field} - ${userKeycloakId.errorMessage}`,
);
}
console.log(
"[ExecuteOfficerProfileService] User created in Keycloak, userKeycloakId:",
userKeycloakId,
);
result = await addUserRoles(
userKeycloakId,
list
.filter((v) => v.name === "USER")
.map((x) => ({
id: x.id,
name: x.name,
})),
);
console.log(
"[ExecuteOfficerProfileService] USER role assigned to new user, result:",
result,
);
} else {
console.log("[ExecuteOfficerProfileService] Updating existing Keycloak user");
userKeycloakId = checkUser[0].id;
console.log(
"[ExecuteOfficerProfileService] Existing userKeycloakId:",
userKeycloakId,
);
const rolesData = await getRoleMappings(userKeycloakId);
if (rolesData) {
const _delRole = rolesData.map((x: any) => ({
id: x.id,
name: x.name,
}));
console.log(
"[ExecuteOfficerProfileService] Removing old roles:",
_delRole.length,
);
await removeUserRoles(userKeycloakId, _delRole);
}
result = await addUserRoles(
userKeycloakId,
list
.filter((v) => v.name === "USER")
.map((x) => ({
id: x.id,
name: x.name,
})),
);
console.log(
"[ExecuteOfficerProfileService] USER role assigned to existing user",
);
}
let profile: any = await profileRepository.findOne({
where: { citizenId: item.bodyProfile.citizenId /*, isActive: true */ },
relations: ["roleKeycloaks", "profileInsignias", "profileAvatars"],
@ -673,9 +558,8 @@ export class ExecuteOfficerProfileService {
profile = Object.assign({ ...item.bodyProfile, ...meta });
profile.dateRetire = _dateRetire;
profile.dateRetireLaw = _dateRetireLaw;
profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : [];
profile.keycloak =
userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : "";
profile.roleKeycloaks = roleKeycloak ? [roleKeycloak] : [];
profile.keycloak = ""; // ว่างไว้ก่อน - จะ sync Keycloak หลังจาก save แล้ว
profile.registrationAddress = item.bodyProfile.registrationAddress;
profile.registrationProvinceId = registrationProvinceId
? registrationProvinceId.id
@ -718,12 +602,6 @@ export class ExecuteOfficerProfileService {
"[ExecuteOfficerProfileService] New profile saved, profileId:",
profile.id,
);
// update user attribute in keycloak
await updateUserAttributes(profile.keycloak ?? "", {
profileId: [profile.id],
prefix: [profile.prefix || ""],
});
console.log("[ExecuteOfficerProfileService] Keycloak attributes updated");
setLogDataDiff(req, { before, after: profile });
}
//ขรก.ในระบบ หรือ ขรก.ในระบบที่สถานะพ้นจากราชการ
@ -753,9 +631,8 @@ export class ExecuteOfficerProfileService {
profile = Object.assign({ ...item.bodyProfile, ...meta });
profile.dateRetire = _dateRetire;
profile.dateRetireLaw = _dateRetireLaw;
profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : [];
profile.keycloak =
userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : "";
profile.roleKeycloaks = roleKeycloak ? [roleKeycloak] : [];
profile.keycloak = ""; // ว่างไว้ก่อน - จะ sync Keycloak หลังจาก save แล้ว
profile.registrationAddress = item.bodyProfile.registrationAddress;
profile.registrationProvinceId = registrationProvinceId
? registrationProvinceId.id
@ -800,9 +677,8 @@ export class ExecuteOfficerProfileService {
console.log(
"[ExecuteOfficerProfileService] Updating existing active profile",
);
profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : [];
profile.keycloak =
userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : "";
profile.roleKeycloaks = roleKeycloak ? [roleKeycloak] : [];
profile.keycloak = ""; // ว่างไว้ก่อน - จะ sync Keycloak หลังจาก save แล้ว
profile.isProbation = item.bodyProfile.isProbation;
profile.isLeave = item.bodyProfile.isLeave;
profile.isRetirement = false;
@ -894,6 +770,36 @@ export class ExecuteOfficerProfileService {
}
}
// ─────────────────────────────────────────────────────────────
// Keycloak Operations - ทำหลังจาก profile save สำเร็จแล้ว
// ใช้ profile.birthDate ที่ save แล้ว เพื่อให้ตรงกับ reset-password endpoint
// ─────────────────────────────────────────────────────────────
if (profile && profile.id && !profile.keycloak) {
console.log(
"[ExecuteOfficerProfileService] Syncing Keycloak user for profileId:",
profile.id,
);
try {
const userKeycloakId = await syncKeycloakForProfile(profile, list);
profile.keycloak = userKeycloakId;
await profileRepository.save(profile);
console.log(
"[ExecuteOfficerProfileService] Keycloak sync completed, profile.keycloak updated:",
profile.keycloak,
);
} catch (error) {
console.error(
`[ExecuteOfficerProfileService] Keycloak sync failed for profileId: ${profile.id}, citizenId: ${profile.citizenId}`,
error,
);
throw handleKeycloakError(error, {
citizenId: profile.citizenId,
profileId: profile.id,
operation: "sync",
});
}
}
if (profile && profile.id) {
console.log(
"[ExecuteOfficerProfileService] Processing additional data for profileId:",