✨New user provider for nextcloud migration (#337)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import express, { Express, Request, Response } from "express";
|
||||
import { NextcloudMigration, NextcloudMigrationConfiguration } from './nextcloud_migration.js';
|
||||
import { logger } from "./logger"
|
||||
import { UserProviderType } from "./user/user_privider";
|
||||
|
||||
const app: Express = express();
|
||||
const port = process.env.SERVER_PORT || 3000;
|
||||
@@ -13,6 +14,10 @@ const config: NextcloudMigrationConfiguration = {
|
||||
baseDn: process.env.LDAP_BASE!,
|
||||
url: process.env.LDAP_URL!,
|
||||
},
|
||||
lemon: {
|
||||
url: process.env.LEMON_USERS_URL!,
|
||||
auth: process.env.LEMON_USERS_AUTH!,
|
||||
},
|
||||
tmpDir: process.env.TMP_DIR || '/tmp',
|
||||
nextcloudUrl: process.env.NEXTCLOUD_URL!,
|
||||
drive: {
|
||||
@@ -21,7 +26,8 @@ const config: NextcloudMigrationConfiguration = {
|
||||
appId: process.env.TWAKE_DRIVE_APP_ID!,
|
||||
secret: process.env.TWAKE_DRIVE_SECRET!,
|
||||
}
|
||||
}
|
||||
},
|
||||
userProvider: process.env.USER_PROVIDER! as UserProviderType
|
||||
}
|
||||
|
||||
if (!config.ldap.baseDn) {
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import { exec } from 'child_process';
|
||||
// @ts-ignore
|
||||
import fs from 'fs';
|
||||
import { LdapUser } from './shell_ldap_user';
|
||||
import { User } from './ldap_user.js';
|
||||
import { ShellLdapUserProvider } from './user/shell_ldap_user';
|
||||
import { TwakeDriveClient, TwakeDriveUser } from './twake_client';
|
||||
import path from 'path';
|
||||
import { logger } from "./logger"
|
||||
import { User, UserProvider, UserProviderFactory, UserProviderType } from "./user/user_privider";
|
||||
|
||||
export type NextcloudMigrationConfiguration = {
|
||||
ldap: {
|
||||
export interface NextcloudMigrationConfiguration {
|
||||
shell: {
|
||||
baseDn: string,
|
||||
url: string,
|
||||
},
|
||||
lemon: {
|
||||
url: string,
|
||||
auth: string,
|
||||
}
|
||||
drive: {
|
||||
url: string,
|
||||
credentials: {
|
||||
@@ -21,19 +25,20 @@ export type NextcloudMigrationConfiguration = {
|
||||
},
|
||||
tmpDir: string,
|
||||
nextcloudUrl: string
|
||||
userProvider: UserProviderType
|
||||
}
|
||||
|
||||
export class NextcloudMigration {
|
||||
|
||||
private config: NextcloudMigrationConfiguration;
|
||||
|
||||
private ldap: LdapUser;
|
||||
private userProvider: UserProvider;
|
||||
|
||||
driveClient: TwakeDriveClient;
|
||||
|
||||
constructor(config: NextcloudMigrationConfiguration) {
|
||||
this.config = config;
|
||||
this.ldap = new LdapUser(config.ldap);
|
||||
this.userProvider = (new UserProviderFactory()).get(config.userProvider, config[config.userProvider]);
|
||||
this.driveClient = new TwakeDriveClient(this.config.drive);
|
||||
}
|
||||
|
||||
@@ -79,7 +84,7 @@ export class NextcloudMigration {
|
||||
}
|
||||
|
||||
async getLDAPUser(username: string): Promise<User> {
|
||||
const user = await this.ldap.find(username);
|
||||
const user = await this.userProvider.find(username);
|
||||
if (!user.email) {
|
||||
throw new Error(`User ${username} not found`);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import axios from 'axios';
|
||||
import { User } from './ldap_user';
|
||||
import FormData from 'form-data';
|
||||
// @ts-ignore
|
||||
import fs from 'fs';
|
||||
import { logger } from './logger';
|
||||
import { User } from "./user/user_privider";
|
||||
|
||||
type TwakeClientConfiguration = {
|
||||
url: string,
|
||||
@@ -15,7 +15,7 @@ type TwakeClientConfiguration = {
|
||||
|
||||
export class TwakeDriveClient {
|
||||
|
||||
private config: TwakeClientConfiguration;
|
||||
private readonly config: TwakeClientConfiguration;
|
||||
|
||||
constructor(config: TwakeClientConfiguration) {
|
||||
this.config = config;
|
||||
|
||||
+4
-42
@@ -1,20 +1,14 @@
|
||||
import ldap, { SearchEntry, SearchOptions } from 'ldapjs';
|
||||
import { logger } from "./logger"
|
||||
import ldap, { SearchOptions } from "ldapjs";
|
||||
import { logger } from "../logger";
|
||||
import { User, UserProvider } from "./user_privider";
|
||||
|
||||
export type LdapConfiguration = {
|
||||
url: string,
|
||||
baseDn: string,
|
||||
}
|
||||
|
||||
export type User = {
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
email: string,
|
||||
uid: string
|
||||
}
|
||||
|
||||
// Doesn't work, fix it later, somehow none of the events is called for the search request
|
||||
export class LdapUser {
|
||||
export class LdapUserProvider implements UserProvider {
|
||||
|
||||
private config: LdapConfiguration;
|
||||
|
||||
@@ -24,38 +18,6 @@ export class LdapUser {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
async auth(username: string, password: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client?.bind(username, password, (error) => {
|
||||
if (error) {
|
||||
reject(new Error("Authentication error"));
|
||||
} else {
|
||||
logger.info("Successfully authenticated in LDAP")
|
||||
resolve(this.client);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async connect() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.client) {
|
||||
this.client = ldap.createClient({
|
||||
url: this.config.url,
|
||||
reconnect: true
|
||||
});
|
||||
this.client.on('connect', (res) => {
|
||||
logger.info("Connected to LDAP")
|
||||
resolve(this.auth("", ""));
|
||||
})
|
||||
this.client.on('connectionError', (error) => {
|
||||
logger.info("Error connecting to LDAP")
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async find(username: string): Promise<User> {
|
||||
const search = await this.search(username);
|
||||
return new Promise<User>((resolve, reject) => {
|
||||
@@ -0,0 +1,45 @@
|
||||
import { User, UserProvider } from "./user_privider";
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
export interface LemonLdapUserProviderConfig {
|
||||
url: string,
|
||||
auth: string,
|
||||
}
|
||||
|
||||
export class LemonLdapUserProvider implements UserProvider {
|
||||
|
||||
private client: AxiosInstance;
|
||||
|
||||
constructor(private config: LemonLdapUserProviderConfig) {
|
||||
this.client = axios.create({
|
||||
baseURL: config.url,
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.auth}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async find(username: string): Promise<User> {
|
||||
const response = await this.client.post("", [username]);
|
||||
if (response.status == 200 && response.data) {
|
||||
const u: any = response.data[username];
|
||||
if (u && u.length > 0) {
|
||||
if (u) {
|
||||
const user = {
|
||||
firstName: u[0].filter(f => f[0] === "givenName").map(f => f[1])[0],
|
||||
lastName: u[0].filter(f => f[0] === "sn").map(f => f[1])[0],
|
||||
email: u[0].filter(f => f[0] === "mail").map(f => f[1])[0],
|
||||
uid: u[0].filter(f => f[0] === "uid").map(f => f[1])[0]
|
||||
}
|
||||
if (user.uid && !user.email) {
|
||||
user.email = `${user.uid}@cnb.linagora.com`;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {} as User;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,9 +1,10 @@
|
||||
import { LdapConfiguration, User } from './ldap_user';
|
||||
import { LdapConfiguration } from './ldap_user';
|
||||
import { exec } from 'child_process';
|
||||
import ldif from 'ldif';
|
||||
import { logger } from "./logger"
|
||||
import { logger } from "../logger"
|
||||
import { User, UserProvider } from "./user_privider";
|
||||
|
||||
export class LdapUser {
|
||||
export class ShellLdapUserProvider implements UserProvider {
|
||||
|
||||
private config: LdapConfiguration;
|
||||
|
||||
@@ -12,7 +13,7 @@ export class LdapUser {
|
||||
}
|
||||
|
||||
async find(username: string): Promise<User> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise<User>((resolve, reject) => {
|
||||
let cmd = `ldapsearch -x -H ${this.config.url} -b '${this.config.baseDn}' '(uid=${username})'`;
|
||||
logger.info("Executing command to get data from LDAP for " + username);
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ShellLdapUserProvider } from "./shell_ldap_user";
|
||||
import { LemonLdapUserProvider } from "./lemon_ldap_user_provider";
|
||||
import { LdapUserProvider } from "./ldap_user";
|
||||
|
||||
export type User = {
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
email: string,
|
||||
uid: string
|
||||
}
|
||||
|
||||
export type UserProviderType = "lemon" | "ldap" | "shell";
|
||||
|
||||
export interface UserProvider {
|
||||
find(username: string): Promise<User>
|
||||
}
|
||||
|
||||
export class UserProviderFactory {
|
||||
|
||||
get(type: UserProviderType, config: any) {
|
||||
switch (type) {
|
||||
case "shell":
|
||||
return new ShellLdapUserProvider(config)
|
||||
case "ldap":
|
||||
return new LdapUserProvider(config);
|
||||
case "lemon":
|
||||
return new LemonLdapUserProvider(config);
|
||||
default:
|
||||
return new ShellLdapUserProvider(config);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import {describe, expect, test} from '@jest/globals';
|
||||
import { LemonLdapUserProvider, LemonLdapUserProviderConfig } from "../src/user/lemon_ldap_user_provider";
|
||||
|
||||
//FOR LOCAL DEBUG PURPOSE ONLY, ITS NOT A TEST
|
||||
describe.skip('Lemon LDAP User Provider', () => {
|
||||
|
||||
test('ldap returns user info', async () => {
|
||||
const ldap = new LemonLdapUserProvider({
|
||||
url: "https://auth.avocat.fr/_getldapentries",
|
||||
auth: "isPnOTuBwjqAlIdCFGNY",
|
||||
});
|
||||
const user = await ldap.find("999248");
|
||||
expect(user.firstName).toBe("Xavier");
|
||||
expect(user.lastName).toBe("GUIMARD");
|
||||
expect(user.email).toBe("999248@cnb.linagora.com");
|
||||
expect(user.uid).toBe("999248");
|
||||
});
|
||||
|
||||
test('ldap returns user info with email', async () => {
|
||||
const ldap = new LemonLdapUserProvider({
|
||||
url: "https://auth.avocat.fr/_getldapentries",
|
||||
auth: "isPnOTuBwjqAlIdCFGNY",
|
||||
});
|
||||
const user = await ldap.find("999253");
|
||||
expect(user.firstName).toBe("Utilisateur4");
|
||||
expect(user.lastName).toBe("ONBOARDING4");
|
||||
expect(user.email).toBe("utilisateur4.onboarding4@avocat.fr");
|
||||
expect(user.uid).toBe("999253");
|
||||
});
|
||||
|
||||
test('ldap returns nothing', async () => {
|
||||
const ldap = new LemonLdapUserProvider({
|
||||
url: "https://auth.avocat.fr/_getldapentries",
|
||||
auth: "isPnOTuBwjqAlIdCFGNY",
|
||||
});
|
||||
const user = await ldap.find("NOTHING_HERE");
|
||||
expect(user.email).toBeUndefined();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -14,7 +14,12 @@ describe.skip('nextcloud migration module', () => {
|
||||
appId: "tdrive_onlyoffice",
|
||||
secret: "c1cc66db78e1d3bb4713c55d5ab2"
|
||||
}
|
||||
}
|
||||
},
|
||||
userProvider: "lemon",
|
||||
lemon: {
|
||||
url: "url",
|
||||
auth: "key"
|
||||
},
|
||||
} as NextcloudMigrationConfiguration);
|
||||
|
||||
let user = await subj.driveClient.createUser({firstName: "DWHO", lastName: "DWHO", email: "dwho@example.com", uid: "test"});
|
||||
|
||||
+6
-6
@@ -1,24 +1,24 @@
|
||||
import {describe, expect, test} from '@jest/globals';
|
||||
import { LdapUser } from '../src/shell_ldap_user';
|
||||
import { LdapConfiguration } from '../src/ldap_user';
|
||||
import { ShellLdapUserProvider } from '../src/user/shell_ldap_user';
|
||||
import { LdapConfiguration } from '../src/user/ldap_user';
|
||||
|
||||
//FOR LOCAL DEBUG PURPOSE ONLY, ITS NOT A TEST
|
||||
describe.skip('ldap module', () => {
|
||||
describe.skip('Shell LDAP User Provider', () => {
|
||||
|
||||
test('ldap returns user info', async () => {
|
||||
const ldap = new LdapUser({
|
||||
const ldap = new ShellLdapUserProvider({
|
||||
url: "ldap://auth.poc-mail-avocat.fr:389",
|
||||
baseDn: "ou=users,dc=example,dc=com",
|
||||
} as LdapConfiguration);
|
||||
const user = await ldap.find("999248");
|
||||
expect(user.firstName).toBe("Xavier");
|
||||
expect(user.lastName).toBe("GUIMARD");
|
||||
expect(user.email).toBe("xguimard@avocat.fr");
|
||||
expect(user.email).toBe("xguimard@preprod-avocat.fr");
|
||||
expect(user.uid).toBe("999248");
|
||||
});
|
||||
|
||||
test('ldap returns nothing', async () => {
|
||||
const ldap = new LdapUser({
|
||||
const ldap = new ShellLdapUserProvider({
|
||||
url: "ldap://auth.poc-mail-avocat.fr:389",
|
||||
baseDn: "ou=users,dc=example,dc=com",
|
||||
} as LdapConfiguration);
|
||||
Reference in New Issue
Block a user