diff --git a/tdrive/backend/node/src/core/platform/services/webserver/error.ts b/tdrive/backend/node/src/core/platform/services/webserver/error.ts index f3a03279..b13bbaa0 100644 --- a/tdrive/backend/node/src/core/platform/services/webserver/error.ts +++ b/tdrive/backend/node/src/core/platform/services/webserver/error.ts @@ -9,7 +9,7 @@ function serverErrorHandler(server: FastifyInstance): void { ? { statusCode: reply.statusCode, error: "Internal Server Error", - message: "Something went wrong", + message: "Something went wrong, " + err.message, requestId: request.id, } : err, diff --git a/tdrive/backend/node/src/services/applications-api/web/controllers/index.ts b/tdrive/backend/node/src/services/applications-api/web/controllers/index.ts index 22daf1fd..8bee2d58 100644 --- a/tdrive/backend/node/src/services/applications-api/web/controllers/index.ts +++ b/tdrive/backend/node/src/services/applications-api/web/controllers/index.ts @@ -14,6 +14,7 @@ import { getApplicationObject, } from "../../../applications/entities/application"; import gr from "../../../global-resolver"; +import { logger } from "../../../../core/platform/framework/logger"; import { ApplicationApiExecutionContext, ApplicationLoginRequest, @@ -171,20 +172,10 @@ export class ApplicationsApiController { email: string; first_name: string; last_name: string; - application_id: string; - company_id: string; }; }>, ): Promise { const email = request.body.email.trim().toLocaleLowerCase(); - const checkApplication = gr.services.applications.companyApps.get({ - application_id: request.body.application_id, - company_id: request.body.company_id, - }); - - if (!checkApplication) { - throw new Error("Application is not allowed to sync users for this company."); - } if (await gr.services.users.getByEmail(email)) { throw new Error("This email is already used"); @@ -203,12 +194,17 @@ export class ApplicationsApiController { }); const user = await gr.services.users.create(newUser); - await gr.services.companies.setUserRole(request.body.company_id, user.entity.id, "admin"); + const company = await gr.services.companies.getCompany({ + id: "00000000-0000-4000-0000-000000000000", + }); + + await gr.services.companies.setUserRole(company.id, user.entity.id, "member"); await gr.services.users.save(user.entity, { user: { id: user.entity.id, server_request: true }, }); } catch (err) { + logger.error(err); throw new Error("An unknown error occured"); } return {}; diff --git a/tdrive/backend/node/src/services/console/clients/remote.ts b/tdrive/backend/node/src/services/console/clients/remote.ts index b2326895..932dfeab 100644 --- a/tdrive/backend/node/src/services/console/clients/remote.ts +++ b/tdrive/backend/node/src/services/console/clients/remote.ts @@ -1,4 +1,3 @@ -import { AxiosInstance } from "axios"; import { ConsoleServiceClient } from "../client-interface"; import { ConsoleCompany, @@ -26,7 +25,6 @@ import config from "config"; import { CompanyUserRole } from "src/services/user/web/types"; export class ConsoleRemoteClient implements ConsoleServiceClient { version: "1"; - client: AxiosInstance; private infos: ConsoleOptions; private verifier: OidcJwtVerifier; diff --git a/tdrive/backend/utils/ldap-sync/.env.example b/tdrive/backend/utils/ldap-sync/.env.example index d6942f2f..748360e8 100644 --- a/tdrive/backend/utils/ldap-sync/.env.example +++ b/tdrive/backend/utils/ldap-sync/.env.example @@ -4,3 +4,7 @@ LDAP_BIND_CREDENTIALS= LDAP_SEARCH_BASE=dc=example,dc=com LDAP_SEARCH_FILTER=(objectClass=inetorgperson) API_URL=http://tdrive:4000/api/sync +TDRIVE_URL=http://tdrive:4000/ +TDRIVE_CREDENTIALS_ID=application-name +TDRIVE_CREDENTIALS_SECRET=application-secret +LDAP_ATTRIBUTE_MAPPINGS={"firstName": "givenName", "lastName": "sn", "email": "mail"} diff --git a/tdrive/backend/utils/ldap-sync/package.json b/tdrive/backend/utils/ldap-sync/package.json index 2e803f7b..1ca7d880 100644 --- a/tdrive/backend/utils/ldap-sync/package.json +++ b/tdrive/backend/utils/ldap-sync/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "description": "", "main": "index.js", + "type": "module", "scripts": { "build": "npm run build:clean && npm run build:ts", "build:ts": "tsc", diff --git a/tdrive/backend/utils/ldap-sync/src/index.ts b/tdrive/backend/utils/ldap-sync/src/index.ts index 75930ae1..d1582698 100644 --- a/tdrive/backend/utils/ldap-sync/src/index.ts +++ b/tdrive/backend/utils/ldap-sync/src/index.ts @@ -1,5 +1,5 @@ import ldap from "ldapjs"; -import axios from "axios"; +import axios, { AxiosError } from "axios"; import dotenv from "dotenv"; interface UserAttributes { @@ -8,27 +8,93 @@ interface UserAttributes { email: string; } -dotenv.config(); +export interface IApiServiceApplicationTokenRequestParams { + id: string; + secret: string; +} +export interface IApiServiceApplicationTokenResponse { + resource: { + access_token: { + time: number; + expiration: number; + value: string; + type: string; + }; + }; +} + +dotenv.config(); console.log("Run script with the following env: "); console.log(process.env); -// LDAP server configuration const ldapConfig = { - url: process.env.LDAP_URL|| "localhost", + url: process.env.LDAP_URL || "localhost", bindDN: process.env.LDAP_BIND_DN || "", bindCredentials: process.env.LDAP_BIND_CREDENTIALS || "", searchBase: process.env.LDAP_SEARCH_BASE || "dc=example,dc=com", searchFilter: process.env.LDAP_SEARCH_FILTER || "(objectClass=inetorgperson)", + mappings: JSON.parse(process.env.LDAP_ATTRIBUTE_MAPPINGS || "{}"), timeout: 120, version: 3, }; +const tdriveConfig = { + url: process.env.TDRIVE_URL || "http://localhost:4000/)", + credentials: { + id: process.env.TDRIVE_CREDENTIALS_ID || "application-name", + secret: process.env.TDRIVE_CREDENTIALS_SECRET || "application-secret", + } +}; + +const refreshToken = async (): Promise => { + try { + const response = await axios.post( + `${tdriveConfig.url.replace(/\/$/, '')}/api/console/v1/login`, + { + id: tdriveConfig.credentials.id, + secret: tdriveConfig.credentials.secret, + }, + { + headers: { + Authorization: `Basic ${Buffer.from(`${tdriveConfig.credentials.id}:${tdriveConfig.credentials.secret}`).toString('base64')}`, + }, + }, + ); + + const { + resource: { + access_token: { value }, + }, + } = response.data; + + //axiosClient.interceptors.response.use(this.handleResponse, this.handleErrors); + + return value; + } catch (error) { + console.error('failed to get application token', error); + console.info('Using token ', tdriveConfig.credentials.id, tdriveConfig.credentials.secret); + console.info(`POST ${tdriveConfig.url.replace(/\/$/, '')}/api/console/v1/login`); + console.info(`Basic ${Buffer.from(`${tdriveConfig.credentials.id}:${tdriveConfig.credentials.secret}`).toString('base64')}`); + throw new Error("Unable to get access to token, see precious errors for details."); + } +}; + + // Create LDAP client const client = ldap.createClient({ url: ldapConfig.url, }); +const accessToken = await refreshToken() + +const axiosClient = axios.create({ + baseURL: tdriveConfig.url, + headers: { + Authorization: `Bearer ${accessToken}`, + }, +}); + // Bind to LDAP server client.bind(ldapConfig.bindDN, ldapConfig.bindCredentials, (err) => { if (err) { @@ -41,7 +107,7 @@ client.bind(ldapConfig.bindDN, ldapConfig.bindCredentials, (err) => { ldapConfig.searchBase, { filter: ldapConfig.searchFilter, - attributes: ["uid", "mail", "cn", "sn", "mobile"], + attributes: [ldapConfig.mappings.firstName, ldapConfig.mappings.lastName, ldapConfig.mappings.email], scope: "sub", derefAliases: 2, }, @@ -54,15 +120,24 @@ client.bind(ldapConfig.bindDN, ldapConfig.bindCredentials, (err) => { const apiRequests: Promise[] = []; searchRes.on("searchEntry", (entry: any) => { + console.log('Receive entry:: ' + JSON.stringify(entry.pojo)); + // Handle each search result entry const userAttributes: UserAttributes = { - first_name: entry.attributes[1]?.values[0], - last_name: entry.attributes[2]?.values[0], - email: entry.attributes[3]?.values[0], + first_name: entry.attributes[0]?.values[0], + last_name: entry.attributes[1]?.values[0], + email: entry.attributes[2]?.values[0], }; - // Make API call to tdrive backend with the userAttributes - apiRequests.push(axios.post(process.env.API_URL || "", userAttributes)); + if (userAttributes.email) { + //Make API call to tdrive backend with the userAttributes + apiRequests.push(axiosClient.post(process.env.API_URL || "", userAttributes) + .catch((e: AxiosError) => { + console.log(`Error for ${JSON.stringify(userAttributes)}: ${e.message}, body: ${e.response?.data?.message}`); + })); + } else { + console.log(`user ${JSON.stringify(userAttributes)} doesn't have an email`); + } }); searchRes.on("error", (err) => { @@ -75,19 +150,9 @@ client.bind(ldapConfig.bindDN, ldapConfig.bindCredentials, (err) => { if (unbindErr) { console.error("LDAP unbind error:", unbindErr); } else { - Promise.all(apiRequests) - .then((responses) => { - console.log( - "API responses:", - responses.map((r) => r.data) - ); - }) - .catch((error) => { - console.error("API error:", error); - }) - .finally(() => { - console.log("LDAP search completed successfully."); - }); + + Promise.allSettled(apiRequests) + .finally(() => console.log("LDAP search COMPLETED.")); } }); }); diff --git a/tdrive/backend/utils/ldap-sync/tsconfig.json b/tdrive/backend/utils/ldap-sync/tsconfig.json index fbac536e..d0ccc9de 100644 --- a/tdrive/backend/utils/ldap-sync/tsconfig.json +++ b/tdrive/backend/utils/ldap-sync/tsconfig.json @@ -1,10 +1,12 @@ { "compilerOptions": { - "target": "es6", - "module": "commonjs", + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", "outDir": "dist", "strict": true, - "esModuleInterop": true + "esModuleInterop": true, + "useUnknownInCatchVariables": false }, "include": ["src"] }