✨ feat: add phone to cozy instance creation script (#904)
<!--- Provide a general summary of your changes in the Title above --> ## Description <!--- Describe your changes in detail --> - When creating cozy instances in the migration script, we search for the phone number in LDAP and insert it in the request.
This commit is contained in:
@@ -69,6 +69,7 @@
|
|||||||
"@types/fluent-ffmpeg": "^2.1.20",
|
"@types/fluent-ffmpeg": "^2.1.20",
|
||||||
"@types/html-to-text": "^8.1.1",
|
"@types/html-to-text": "^8.1.1",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
|
"@types/ldapjs": "^3.0.6",
|
||||||
"@types/lodash": "^4.14.165",
|
"@types/lodash": "^4.14.165",
|
||||||
"@types/node": "^20.17.16",
|
"@types/node": "^20.17.16",
|
||||||
"@types/node-cron": "^3.0.0",
|
"@types/node-cron": "^3.0.0",
|
||||||
@@ -142,6 +143,7 @@
|
|||||||
"cozy-intent": "^2.30.0",
|
"cozy-intent": "^2.30.0",
|
||||||
"cozy-logger": "^1.17.0",
|
"cozy-logger": "^1.17.0",
|
||||||
"deep-object-diff": "^1.1.0",
|
"deep-object-diff": "^1.1.0",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
"emoji-name-map": "^1.2.9",
|
"emoji-name-map": "^1.2.9",
|
||||||
"eta": "^2.2.0",
|
"eta": "^2.2.0",
|
||||||
"fastify": "^4.27.0",
|
"fastify": "^4.27.0",
|
||||||
@@ -156,6 +158,7 @@
|
|||||||
"i18n": "^0.15.1",
|
"i18n": "^0.15.1",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"jwks-rsa": "^3.0.1",
|
"jwks-rsa": "^3.0.1",
|
||||||
|
"ldapjs": "^3.0.7",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"loupe": "^3.1.0",
|
"loupe": "^3.1.0",
|
||||||
"minio": "^7.1.3",
|
"minio": "^7.1.3",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import globalResolver from "../../../services/global-resolver";
|
|||||||
import User from "../../../services/user/entities/user";
|
import User from "../../../services/user/entities/user";
|
||||||
import yargs from "yargs";
|
import yargs from "yargs";
|
||||||
import { createCozyInstance } from "./utils";
|
import { createCozyInstance } from "./utils";
|
||||||
|
import { getLDAPUserMobile } from "./utils/ldap";
|
||||||
|
|
||||||
const migrateUsersCommand: yargs.CommandModule<unknown, unknown> = {
|
const migrateUsersCommand: yargs.CommandModule<unknown, unknown> = {
|
||||||
command: "migrate-users",
|
command: "migrate-users",
|
||||||
@@ -59,12 +60,14 @@ const migrateUsersCommand: yargs.CommandModule<unknown, unknown> = {
|
|||||||
|
|
||||||
for (const user of usersToMigrate) {
|
for (const user of usersToMigrate) {
|
||||||
const userId = user.email_canonical.split("@")[0];
|
const userId = user.email_canonical.split("@")[0];
|
||||||
|
const phone = await getLDAPUserMobile(userId);
|
||||||
const userObject = {
|
const userObject = {
|
||||||
_id: user.id,
|
_id: user.id,
|
||||||
id: userId,
|
id: userId,
|
||||||
email: user.email_canonical,
|
email: user.email_canonical,
|
||||||
name: `${user.first_name} ${user.last_name}`,
|
name: `${user.first_name} ${user.last_name}`,
|
||||||
locale: user.preferences?.locale || "fr",
|
locale: user.preferences?.locale || "fr",
|
||||||
|
phone,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!dryRun) {
|
if (!dryRun) {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export async function createCozyInstance(user: {
|
|||||||
name: string;
|
name: string;
|
||||||
_id: string;
|
_id: string;
|
||||||
locale: string;
|
locale: string;
|
||||||
|
phone: string;
|
||||||
}) {
|
}) {
|
||||||
console.log(`🚀 Creating Cozy instance for ${user.email}...`);
|
console.log(`🚀 Creating Cozy instance for ${user.email}...`);
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ export async function createCozyInstance(user: {
|
|||||||
public_name: user.name,
|
public_name: user.name,
|
||||||
locale: user.locale,
|
locale: user.locale,
|
||||||
oidc: user.id,
|
oidc: user.id,
|
||||||
|
phone: user.phone,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import ldap, { type SearchEntry, type Client } from "ldapjs";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
interface LdapConfig {
|
||||||
|
url: string;
|
||||||
|
bindDN: string;
|
||||||
|
bindCredentials: string;
|
||||||
|
searchBase: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the LDAP configuration from environment variables.
|
||||||
|
*
|
||||||
|
* @returns {LdapConfig} The LDAP configuration object.
|
||||||
|
*/
|
||||||
|
function getLdapConfig(): LdapConfig {
|
||||||
|
return {
|
||||||
|
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",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates a ldap client and binds to the ldap server
|
||||||
|
*
|
||||||
|
* @param {LdapConfig} config - the LDAP connection config
|
||||||
|
* @returns {Promise<Client>} A promise that resolves to the LDAP client instance.
|
||||||
|
*/
|
||||||
|
function createLdapClient(config: LdapConfig): Promise<Client> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const client = ldap.createClient({ url: config.url });
|
||||||
|
client.bind(config.bindDN, config.bindCredentials, err => {
|
||||||
|
if (err) {
|
||||||
|
client.unbind();
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(client);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for a user in LDAP by username and returns their mobile number.
|
||||||
|
*
|
||||||
|
* @param {Client} client The LDAP client instance.
|
||||||
|
* @param {LdapConfig} config The LDAP configuration.
|
||||||
|
* @param {string} username The username to search for.
|
||||||
|
* @returns A promise that resolves to the user's mobile number, or null if not found.
|
||||||
|
*/
|
||||||
|
function searchForUserMobile(
|
||||||
|
client: Client,
|
||||||
|
config: LdapConfig,
|
||||||
|
username: string,
|
||||||
|
): Promise<string | null> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
client.search(
|
||||||
|
config.searchBase,
|
||||||
|
{
|
||||||
|
filter: `(&(objectClass=inetorgperson)(uid=${username}))`,
|
||||||
|
attributes: ["mobile"],
|
||||||
|
scope: "sub",
|
||||||
|
},
|
||||||
|
(searchErr, searchResult) => {
|
||||||
|
if (searchErr) return reject(searchErr);
|
||||||
|
|
||||||
|
let found = false;
|
||||||
|
|
||||||
|
searchResult.on("searchEntry", (entry: SearchEntry) => {
|
||||||
|
const mobile = entry.attributes.find(a => a.type === "mobile")?.vals[0];
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
resolve(mobile || null);
|
||||||
|
});
|
||||||
|
|
||||||
|
searchResult.on("error", reject);
|
||||||
|
searchResult.on("end", () => !found && resolve(null));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the mobile number for a given username from LDAP.
|
||||||
|
*
|
||||||
|
* @param {string} username The username to search for in LDAP.
|
||||||
|
* @returns A promise that resolves to the user's mobile number, or null if not found.
|
||||||
|
*/
|
||||||
|
export async function getLDAPUserMobile(username: string): Promise<string | null> {
|
||||||
|
const config = getLdapConfig();
|
||||||
|
const client = await createLdapClient(config);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await searchForUserMobile(client, config, username);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("LDAP search error", error);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
client.unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
+1601
-375
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user