import { Body, Controller, Delete, Get, Path, Post, Put, Query, Request, Route, Security, Tags, } from "tsoa"; import { addUserGroup, addUserRoles, createGroup, createUser, deleteGroup, deleteUser, editUser, getGroups, getRoles, getUser, getUserGroups, getUserList, removeUserGroup, removeUserRoles, getRoleMappings, getUserCount, enableStatus, getUserByUsername, changeUserPassword, resetPassword, } from "../keycloak"; import { AppDataSource } from "../database/data-source"; import { Profile } from "../entities/Profile"; import { ProfileEmployee } from "../entities/ProfileEmployee"; import { RequestWithUser } from "../middlewares/user"; import HttpSuccess from "../interfaces/http-success"; import { Brackets, In, IsNull, Not } from "typeorm"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import { RoleKeycloak } from "../entities/RoleKeycloak"; import { addLogSequence } from "../interfaces/utils"; import { OrgRevision } from "../entities/OrgRevision"; import { Uuid } from "@elastic/elasticsearch/lib/api/types"; // import * as io from "../lib/websocket"; // import elasticsearch from "../elasticsearch"; // import { StorageFolder } from "../interfaces/storage-fs"; // if (!process.env.MINIO_BUCKET) throw Error("Default MinIO bucket must be specified."); // if (!process.env.ELASTICSEARCH_INDEX) throw Error("Default ElasticSearch index must be specified."); // const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX; function stripLeadingSlash(str: string) { return str.replace(/^\//, ""); } @Route("api/v1/org/keycloak") @Tags("Single-Sign On") @Security("bearerAuth") export class KeycloakController extends Controller { private profileRepo = AppDataSource.getRepository(Profile); private profileEmpRepo = AppDataSource.getRepository(ProfileEmployee); private roleKeycloakRepo = AppDataSource.getRepository(RoleKeycloak); @Get("user/{id}") async getUser(@Path("id") id: string) { const userData = await getUser(id); if (!userData) { throw new Error("User not found"); } const rolesData = await getRoleMappings(id); if (!rolesData) { throw new Error("Role mappings not found"); } const userDataWithRoles = { ...userData, roles: rolesData, }; return userDataWithRoles; } @Post("user") @Security("bearerAuth", ["system", "admin"]) async createUser( @Request() request: { user: { sub: string; preferred_username: string } }, @Body() body: { username: string; password: string; firstName?: string; lastName?: string; email?: string; roles: string[]; profileId?: string; }, ) { const checkUser = await getUserByUsername(body.username); let userId: any = ""; if (checkUser.length == 0) { userId = await createUser(body.username, body.password, { firstName: body.firstName, lastName: body.lastName, // email: body.email, }); if (typeof userId !== "string") { throw new Error(userId.errorMessage); } } else { userId = checkUser[0].id; } const list = await getRoles(); if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); const result = await addUserRoles( userId, list.filter((v) => body.roles.includes(v.id)), ); if (!result) { throw new Error("Failed. Cannot set user's role."); } const profile = await this.profileRepo.findOne({ where: { id: body.profileId, }, }); if (profile) { let _null: any = null; if (typeof userId === "string") { profile.keycloak = userId; } profile.email = body.email == null ? _null : body.email; await this.profileRepo.save(profile); if (body.roles != null && body.roles.length > 0) { const roleKeycloak = await this.roleKeycloakRepo.find({ where: { id: In(body.roles) }, }); const _profile = await this.profileRepo.findOne({ where: { keycloak: userId }, relations: ["roleKeycloaks"], }); if (_profile) { _profile.roleKeycloaks = Array.from( new Set([..._profile.roleKeycloaks, ...roleKeycloak]), ); this.profileRepo.save(_profile); } } } return userId; } @Post("user-emp") @Security("bearerAuth", ["system", "admin"]) async createUserEmployee( @Request() request: { user: { sub: string; preferred_username: string } }, @Body() body: { username: string; password: string; firstName?: string; lastName?: string; email?: string; roles: string[]; profileId?: string; }, ) { const userId = await createUser(body.username, body.password, { firstName: body.firstName, lastName: body.lastName, // email: body.email, }); if (typeof userId !== "string") { throw new Error(userId.errorMessage); } const list = await getRoles(); if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); const profile = await this.profileEmpRepo.findOne({ where: { id: body.profileId, }, }); if (profile) { let _null: any = null; if (typeof userId === "string") { profile.keycloak = userId; } profile.email = body.email == null ? _null : body.email; await this.profileEmpRepo.save(profile); if (body.roles != null && body.roles.length > 0) { const roleKeycloak = await this.roleKeycloakRepo.find({ where: { id: In(body.roles) }, }); const _profile = await this.profileEmpRepo.findOne({ where: { keycloak: userId }, relations: ["roleKeycloaks"], }); if (_profile) { _profile.roleKeycloaks = Array.from( new Set([..._profile.roleKeycloaks, ...roleKeycloak]), ); this.profileEmpRepo.save(_profile); } } } return userId; } @Put("user/{userId}") async editUser( @Path() userId: string, @Body() body: { username?: string; password?: string; firstName?: string; lastName?: string; email?: string; attributes?: object; // roles?: string[]; }, ) { // return await editUser(userId, body); const chkUpdate = await editUser(userId, body); if (typeof chkUpdate !== "boolean") { throw new Error(chkUpdate.errorMessage); } // const _rolesUpdate = { // role: body.roles || [], // }; return chkUpdate; } @Delete("user/{userId}") async deleteUser(@Path() userId: string) { const result = await deleteUser(userId); if (!result) throw new Error("Failed. Cannot delete userId."); const profile = await this.profileRepo.findOne({ where: { keycloak: userId, }, relations: ["roleKeycloaks"], }); if (!profile) { const profileEmp = await this.profileEmpRepo.findOne({ where: { keycloak: userId, employeeClass: "PERM", }, relations: ["roleKeycloaks"], }); if (!profileEmp) { } else { const _null: any = null; profileEmp.keycloak = _null; profileEmp.roleKeycloaks = []; await this.profileEmpRepo.save(profileEmp); } } else { const _null: any = null; profile.keycloak = _null; profile.roleKeycloaks = []; await this.profileRepo.save(profile); return new HttpSuccess(); } } // @Security("bearerAuth", ["system", "admin"]) @Get("role") async getRole() { const role = await getRoles(); if (Array.isArray(role)) return role.filter( (a) => !["uma_authorization", "offline_access", "default-roles"].some((b) => a.name.includes(b)), ); throw new Error("Failed. Cannot get role."); } @Post("{userId}/role") async addRole(@Path() userId: string, @Body() body: { role: string[] }) { const list = await getRoles(); if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); const result = await addUserRoles( userId, list.filter((v) => body.role.includes(v.id)), ); if (!result) { throw new Error("Failed. Cannot set user's role."); } const roleKeycloak = await this.roleKeycloakRepo.find({ where: { id: In(body.role) }, }); const profile = await this.profileRepo.findOne({ where: { keycloak: userId }, relations: ["roleKeycloaks"], }); if (!profile) { const profileEmp = await this.profileEmpRepo.findOne({ where: { keycloak: userId, employeeClass: "PERM" }, relations: ["roleKeycloaks"], }); // if (!profileEmp) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); if (profileEmp) { profileEmp.roleKeycloaks = Array.from( new Set([...profileEmp.roleKeycloaks, ...roleKeycloak]), ); this.profileEmpRepo.save(profileEmp); } } else { profile.roleKeycloaks = Array.from(new Set([...profile.roleKeycloaks, ...roleKeycloak])); this.profileRepo.save(profile); } return new HttpSuccess(); } @Delete("{userId}/role/{roleId}") async deleteRole(@Path() userId: string, @Path() roleId: string) { const profile = await this.profileRepo.findOne({ where: { keycloak: userId }, relations: ["roleKeycloaks"], }); if (!profile) { const profileEmp = await this.profileEmpRepo.findOne({ where: { keycloak: userId, employeeClass: "PERM" }, relations: ["roleKeycloaks"], }); if (!profileEmp) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); profileEmp.roleKeycloaks = profileEmp.roleKeycloaks.filter((x) => x.id != roleId); this.profileEmpRepo.save(profileEmp); } else { profile.roleKeycloaks = profile.roleKeycloaks.filter((x) => x.id != roleId); this.profileRepo.save(profile); } const list = await getRoles(); if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); const result = await removeUserRoles( userId, list.filter((v) => roleId === v.id), ); if (!result) throw new Error("Failed. Cannot remove user's role."); return new HttpSuccess(); } /*@Get("user") async getUserList( @Request() request: RequestWithUser, @Query() first = "", @Query() max = "", @Query() search = "", ) { // let _data: any = { // root: null, // child1: null, // child2: null, // child3: null, // child4: null, // }; // if (!request.user.role.includes("SUPER_ADMIN")) { // _data = await new permission().PermissionOrgList(request, "SYS_ORG"); // } // const profiles = await this.profileRepo // .createQueryBuilder("profile") // .leftJoinAndSelect("profile.current_holders", "current_holders") // .leftJoinAndSelect("current_holders.orgRoot", "orgRoot") // .leftJoinAndSelect("current_holders.orgChild1", "orgChild1") // .leftJoinAndSelect("current_holders.orgChild2", "orgChild2") // .leftJoinAndSelect("current_holders.orgChild3", "orgChild3") // .leftJoinAndSelect("current_holders.orgChild4", "orgChild4") // .andWhere( // _data.root != undefined && _data.root != null // ? _data.root[0] != null // ? `current_holders.orgRootId IN (:...root)` // : `current_holders.orgRootId is null` // : "1=1", // { // root: _data.root, // }, // ) // .andWhere( // _data.child1 != undefined && _data.child1 != null // ? _data.child1[0] != null // ? `current_holders.orgChild1Id IN (:...child1)` // : `current_holders.orgChild1Id is null` // : "1=1", // { // child1: _data.child1, // }, // ) // .andWhere( // _data.child2 != undefined && _data.child2 != null // ? _data.child2[0] != null // ? `current_holders.orgChild2Id IN (:...child2)` // : `current_holders.orgChild2Id is null` // : "1=1", // { // child2: _data.child2, // }, // ) // .andWhere( // _data.child3 != undefined && _data.child3 != null // ? _data.child3[0] != null // ? `current_holders.orgChild3Id IN (:...child3)` // : `current_holders.orgChild3Id is null` // : "1=1", // { // child3: _data.child3, // }, // ) // .andWhere( // _data.child4 != undefined && _data.child4 != null // ? _data.child4[0] != null // ? `current_holders.orgChild4Id IN (:...child4)` // : `current_holders.orgChild4Id is null` // : "1=1", // { // child4: _data.child4, // }, // ) // .andWhere({ keycloak: Not(IsNull()) }) // .andWhere({ keycloak: Not("") }) // .select("profile.keycloak", "keycloak") // .getRawMany(); // let keycloakArray = profiles.map((p) => p.keycloak); // const total = await getUserCountOrg(first, max, search, keycloakArray); // const result = await getUserListOrg(first, max, search, keycloakArray); const total = await getUserCount(first, max, search); const result = await getUserList(first, max, search); if (Array.isArray(result)) { const mappedData = await Promise.all( result.map(async (x) => { const roles = await getRoleMappings(x.id); return { id: x.id, username: x.username, firstname: x.firstName, lastname: x.lastName, email: x.email, roles: roles, enabled: x.enabled, }; }), ); const _mapData = { data: mappedData, total: total, }; return _mapData; } throw new Error("Failed. Cannot get user list."); }*/ @Get("user") async listUserKeycloak( @Query("page") page: number = 1, @Query("pageSize") pageSize: number = 10, @Query() keyword: string = "", @Query() type: string = "", @Request() req: RequestWithUser, ) { let condition: any = {}; if (req.user.role.includes("ADMIN") && !req.user.role.includes("SUPER_ADMIN")) { const profile = await this.profileRepo.findOne({ relations: ["current_holders", "current_holders.orgRevision"], where: { keycloak: req.user.sub, current_holders: { orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false, }, }, }, }); if ( profile?.current_holders[0]?.orgRootId && profile?.current_holders[0]?.orgChild1Id == null ) { condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id IS NULL and current_holders.orgChild2Id IS NULL and current_holders.orgChild3Id IS NULL and current_holders.orgChild4Id IS NULL`; } else if ( profile?.current_holders[0]?.orgChild1Id && profile?.current_holders[0]?.orgChild2Id == null ) { condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' and current_holders.orgChild2Id IS NULL and current_holders.orgChild3Id IS NULL and current_holders.orgChild4Id IS NULL`; } else if ( profile?.current_holders[0]?.orgChild2Id && profile?.current_holders[0]?.orgChild3Id == null ) { condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' and current_holders.orgChild2Id = '${profile?.current_holders[0]?.orgChild2Id}' and current_holders.orgChild3Id IS NULL and current_holders.orgChild4Id IS NULL`; } else if ( profile?.current_holders[0]?.orgChild3Id && profile?.current_holders[0]?.orgChild4Id == null ) { condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' and current_holders.orgChild2Id = '${profile?.current_holders[0]?.orgChild2Id}' and current_holders.orgChild3Id = '${profile?.current_holders[0]?.orgChild3Id}' and current_holders.orgChild4Id IS NULL`; } else if (profile?.current_holders[0]?.orgChild4Id) { condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' and current_holders.orgChild2Id = '${profile?.current_holders[0]?.orgChild2Id}' and current_holders.orgChild3Id = '${profile?.current_holders[0]?.orgChild3Id}' and current_holders.orgChild4Id = '${profile?.current_holders[0]?.orgChild4Id}'`; } } let profiles: any = []; let total: any; if (type.trim().toUpperCase() == "OFFICER") { [profiles, total] = await this.profileRepo .createQueryBuilder("profile") .leftJoinAndSelect("profile.roleKeycloaks", "roleKeycloaks") .leftJoinAndSelect("profile.current_holders", "current_holders") .where("profile.keycloak IS NOT NULL AND profile.keycloak != ''") .andWhere(condition) .andWhere( new Brackets((qb) => { qb.orWhere( keyword != null && keyword != "" ? `profile.citizenId like '%${keyword}%'` : "1=1", ) .orWhere( keyword != null && keyword != "" ? `profile.email like '%${keyword}%'` : "1=1", ) .orWhere( keyword != null && keyword != "" ? `CONCAT(profile.prefix, profile.firstName," ",profile.lastName) like '%${keyword}%'` : "1=1", ); }), ) .orderBy("profile.citizenId", "ASC") .skip((page - 1) * pageSize) .take(pageSize) .getManyAndCount(); } else if (type.trim().toUpperCase() == "EMPLOYEE") { [profiles, total] = await this.profileEmpRepo .createQueryBuilder("profileEmployee") .leftJoinAndSelect("profileEmployee.roleKeycloaks", "roleKeycloaks") .leftJoinAndSelect("profileEmployee.current_holders", "current_holders") .where("profileEmployee.keycloak IS NOT NULL AND profileEmployee.keycloak != ''") .andWhere(condition) .andWhere({ employeeClass: "PERM" }) .andWhere( new Brackets((qb) => { qb.orWhere( keyword != null && keyword != "" ? `profileEmployee.citizenId like '%${keyword}%'` : "1=1", ) .orWhere( keyword != null && keyword != "" ? `profileEmployee.email like '%${keyword}%'` : "1=1", ) .orWhere( keyword != null && keyword != "" ? `CONCAT(profileEmployee.prefix, profileEmployee.firstName," ",profileEmployee.lastName) like '%${keyword}%'` : "1=1", ); }), ) .orderBy("profileEmployee.citizenId", "ASC") .skip((page - 1) * pageSize) .take(pageSize) .getManyAndCount(); } const _profiles = profiles.map((_data: any) => ({ id: _data.keycloak, firstname: _data.firstName, lastname: _data.lastName, email: _data.email, username: _data.citizenId, citizenId: _data.citizenId, roles: _data.roleKeycloaks, enabled: _data.isActive, })); return new HttpSuccess({ data: _profiles, total }); } @Get("group") async getGroup() { const group = await getGroups(); if (Array.isArray(group)) return group; throw new Error("Failed. Cannot get group."); } @Post("group") async createGroup(@Body() body: { name: string }) { const result = await createGroup(body.name); if (!result) throw new Error("Failed. Cannot create group."); } @Post("log/sso") async addLogSSO( @Request() req: RequestWithUser, @Body() body: { text: string; }, ) { try { addLogSequence(req, { action: "request", status: "success", description: "connected", request: { response: JSON.stringify(body.text), }, }); return new HttpSuccess(); } catch (error) { addLogSequence(req, { action: "request", status: "error", description: "unconnected", request: { response: JSON.stringify(error), }, }); throw error; } } @Post("user/emp") @Security("bearerAuth", ["system", "admin"]) async createUserEmp( @Request() request: { user: { sub: string; preferred_username: string } }, @Body() body: { username: string; password: string; firstName?: string; lastName?: string; email?: string; roles: string[]; profileId?: string; }, ) { const checkUser = await getUserByUsername(body.username); let userId: any = ""; if (checkUser.length == 0) { userId = await createUser(body.username, body.password, { firstName: body.firstName, lastName: body.lastName, // email: body.email, }); if (typeof userId !== "string") { throw new Error(userId.errorMessage); } } else { userId = checkUser[0].id; } const list = await getRoles(); if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); const result = await addUserRoles( userId, list.filter((v) => body.roles.includes(v.id)), ); if (!result) { throw new Error("Failed. Cannot set user's role."); } const profile = await this.profileEmpRepo.findOne({ where: { id: body.profileId, }, }); if (profile) { let _null: any = null; if (typeof userId === "string") { profile.keycloak = userId; } profile.email = body.email == null ? _null : body.email; await this.profileEmpRepo.save(profile); if (body.roles != null && body.roles.length > 0) { const roleKeycloak = await this.roleKeycloakRepo.find({ where: { id: In(body.roles) }, }); const _profile = await this.profileEmpRepo.findOne({ where: { keycloak: userId }, relations: ["roleKeycloaks"], }); if (_profile) { _profile.roleKeycloaks = Array.from( new Set([..._profile.roleKeycloaks, ...roleKeycloak]), ); this.profileEmpRepo.save(_profile); } } } return userId; } @Delete("group/{groupId}") async deleteGroup(@Path() groupId: string) { const result = await deleteGroup(groupId); if (!result) throw new Error("Failed. Cannot delete group."); } @Get("user/{userId}/group") async getUserGroup(@Path() userId: string) { const result = await getUserGroups(userId); if (!result) throw new Error("Failed. Cannot list group to user."); return result; } @Post("user/{userId}/group/{groupId}") async addUserGroup(@Path() userId: string, @Path() groupId: string) { const result = await addUserGroup(userId, groupId); if (!result) throw new Error("Failed. Cannot assign group to user."); } @Delete("user/{userId}/group/{groupId}") async removeUserGroup(@Path() userId: string, @Path() groupId: string) { const result = await removeUserGroup(userId, groupId); if (!result) throw new Error("Failed. Cannot remove group to user."); } @Get("user/role/{id}") async getRoleUser(@Path("id") id: string) { const profile = await this.profileRepo.findOne({ where: { keycloak: id }, relations: ["roleKeycloaks"], }); if (!profile) { const profileEmp = await this.profileEmpRepo.findOne({ where: { keycloak: id, employeeClass: "PERM" }, relations: ["roleKeycloaks"], }); if (!profileEmp) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); return profileEmp.roleKeycloaks; } // const result = await getRoleMappings(id); // if (!result) { // throw new Error("Role mappings not found"); // } return profile.roleKeycloaks; } @Get("user/username/{citizenId}") async getUserByUsername(@Path("citizenId") citizenId: string) { const userData = await getUserByUsername(citizenId); if (!userData || userData.length == 0) { throw new Error("User not found"); } const rolesData = await getRoleMappings(userData[0].id); if (!rolesData) { throw new Error("Role mappings not found"); } const userDataWithRoles = { ...userData, roles: rolesData, }; return userDataWithRoles; } @Put("user/{userId}/enableStatus/{status}") //#log? async changeEnableStatus(@Path() userId: string, @Path() status: boolean) { const profile = await this.profileRepo.findOne({ where: { keycloak: userId }, }); if (!profile) { const profileEmp = await this.profileEmpRepo.findOne({ where: { keycloak: userId, employeeClass: "PERM" }, }); if (!profileEmp) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); profileEmp.isActive = status; this.profileEmpRepo.save(profileEmp); } else { profile.isActive = status; this.profileRepo.save(profile); } const result = await enableStatus(userId, status); if (!result) { throw new Error("Failed. Cannot change enable status."); } return result; } @Post("user/change-password") async changeUserPassword( @Request() request: { user: { sub: string; preferred_username: string } }, @Body() body: { password: string; }, ) { const result = await changeUserPassword(request.user.sub, body.password); if (!result) { throw new Error("Failed. Cannot change password."); } return result; } @Post("user/create") @Security("bearerAuth", ["system", "admin"]) async createUserImport( @Request() request: { user: { sub: string; preferred_username: string } }, ) { const profiles = await this.profileRepo.find({ where: { keycloak: IsNull(), }, relations: ["roleKeycloaks"], }); for await (const _item of profiles) { let password = _item.citizenId; if (_item.birthDate != null) { const gregorianYear = _item.birthDate.getFullYear() + 543; const formattedDate = _item.birthDate.toISOString().slice(8, 10) + _item.birthDate.toISOString().slice(5, 7) + gregorianYear; password = formattedDate; } const checkUser = await getUserByUsername(_item.citizenId); let userId: any = ""; if (checkUser.length == 0) { userId = await createUser(_item.citizenId, password, { firstName: _item.firstName, lastName: _item.lastName, // email: _item.email, }); if (typeof userId !== "string") { throw new Error(userId.errorMessage); } } else { userId = checkUser[0].id; } const list = await getRoles(); if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); const result = await addUserRoles( userId, list.filter((v) => v.id == "f8619dc2-dc0d-4aab-957f-66bdf905e9d0"), ); if (!result) { throw new Error("Failed. Cannot set user's role."); } if (typeof userId === "string") { _item.keycloak = userId; } const roleKeycloak = await this.roleKeycloakRepo.find({ where: { id: "f8619dc2-dc0d-4aab-957f-66bdf905e9d0" }, }); if (_item) { _item.roleKeycloaks = Array.from(new Set([..._item.roleKeycloaks, ...roleKeycloak])); this.profileRepo.save(_item); } } return ""; } @Post("user/change-password-all") async changeUserPasswordAll( @Request() request: { user: { sub: string; preferred_username: string } }, ) { const profiles = await this.profileRepo.find({ where: { keycloak: Not(IsNull()), }, }); for await (const _item of profiles) { const result = await changeUserPassword(_item.keycloak, "P@ssw0rd"); if (!result) { continue; } } return; } }