diff --git a/.github/workflows/ldap-sync.yml b/.github/workflows/ldap-sync.yml new file mode 100644 index 00000000..54088a89 --- /dev/null +++ b/.github/workflows/ldap-sync.yml @@ -0,0 +1,15 @@ +name: ldap-sync-build + +on: + pull_request: + branches: [main] + paths: + - "tdrive/backend/utils/**" + +jobs: + ldap-sync-build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Build ldap sync + run: cd tdrive/backend/utils/ldap-sync && npm i && npm run build \ No newline at end of file diff --git a/.github/workflows/publish-ldap-sync.yml b/.github/workflows/publish-ldap-sync.yml new file mode 100644 index 00000000..bfa4ad74 --- /dev/null +++ b/.github/workflows/publish-ldap-sync.yml @@ -0,0 +1,31 @@ +name: publish-ldap-sync + +on: + pull_request: + branches: [main] + paths: + - "tdrive/utils/ldap-sync/**" + +jobs: + publish-node: + runs-on: ubuntu-20.04 + steps: + - name: Set env to production + if: endsWith(github.ref, '/main') + run: 'echo "DOCKERTAG=latest" >> $GITHUB_ENV' + - name: "Push to the registry following labels:" + run: | + echo "${{ env.DOCKERTAG }},${{ env.DOCKERTAGVERSION }}" + - uses: actions/checkout@v2 + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: tdrive/tdrive-ldap-sync + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + workdir: tdrive + registry: docker-registry.linagora.com + context: . + target: production + buildoptions: "-t docker-registry.linagora.com/tdrive/tdrive-ldap-sync -f docker/tdrive-ldap-sync/Dockerfile" + tags: "${{ env.DOCKERTAG }},${{ env.DOCKERTAGVERSION }}" \ No newline at end of file diff --git a/tdrive/backend/utils/ldap-sync/.env.example b/tdrive/backend/utils/ldap-sync/.env.example new file mode 100644 index 00000000..9c2c3e54 --- /dev/null +++ b/tdrive/backend/utils/ldap-sync/.env.example @@ -0,0 +1,6 @@ +LDAP_URL=ldap://localhost:389 +LDAP_BIND_DN= +LDAP_BIND_CREDENTIALS= +LDAP_SEARCH_BASE=dc=example,dc=com +LDAP_SEARCH_FILTER=(objectClass=inetorgperson) +API_URL=http://tdrive:4000/api/sync \ No newline at end of file diff --git a/tdrive/backend/utils/ldap-sync/.nvmrc b/tdrive/backend/utils/ldap-sync/.nvmrc new file mode 100644 index 00000000..25bf17fc --- /dev/null +++ b/tdrive/backend/utils/ldap-sync/.nvmrc @@ -0,0 +1 @@ +18 \ No newline at end of file diff --git a/tdrive/backend/utils/ldap-sync/package.json b/tdrive/backend/utils/ldap-sync/package.json new file mode 100644 index 00000000..2e803f7b --- /dev/null +++ b/tdrive/backend/utils/ldap-sync/package.json @@ -0,0 +1,26 @@ +{ + "name": "ldap_project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "npm run build:clean && npm run build:ts", + "build:ts": "tsc", + "build:clean": "rimraf ./dist", + "sync": "node dist/index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^1.4.0", + "dotenv": "^16.0.3", + "ldapjs": "^3.0.2" + }, + "devDependencies": { + "@types/ldapjs": "^2.2.5", + "typescript": "^5.0.4", + "rimraf": "^3.0.2" + } +} diff --git a/tdrive/backend/utils/ldap-sync/src/index.ts b/tdrive/backend/utils/ldap-sync/src/index.ts new file mode 100644 index 00000000..58850a2e --- /dev/null +++ b/tdrive/backend/utils/ldap-sync/src/index.ts @@ -0,0 +1,96 @@ +import ldap from "ldapjs"; +import axios from "axios"; +import dotenv from "dotenv"; + +interface UserAttributes { + first_name: string; + last_name: string; + email: 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", + 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)", + timeout: 120, + version: 3, +}; + +// Create LDAP client +const client = ldap.createClient({ + url: ldapConfig.url, +}); + +// Bind to LDAP server +client.bind(ldapConfig.bindDN, ldapConfig.bindCredentials, (err) => { + if (err) { + console.error("LDAP bind error:", err); + return; + } + + // Perform search + client.search( + ldapConfig.searchBase, + { + filter: ldapConfig.searchFilter, + attributes: ["uid", "mail", "cn", "sn", "mobile"], + scope: "sub", + derefAliases: 2, + }, + (searchErr, searchRes) => { + if (searchErr) { + console.error("LDAP search error:", searchErr); + return; + } + + const apiRequests: Promise[] = []; + + searchRes.on("searchEntry", (entry: any) => { + // 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], + }; + + // Make API call to tdrive backend with the userAttributes + apiRequests.push(axios.post(process.env.API_URL || "", userAttributes)); + }); + + searchRes.on("error", (err) => { + console.error("LDAP search result error:", err); + }); + + searchRes.on("end", () => { + // Unbind from LDAP server after search is complete + client.unbind((unbindErr) => { + 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."); + }); + } + }); + }); + } + ); +}); diff --git a/tdrive/backend/utils/ldap-sync/tsconfig.json b/tdrive/backend/utils/ldap-sync/tsconfig.json new file mode 100644 index 00000000..fbac536e --- /dev/null +++ b/tdrive/backend/utils/ldap-sync/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "outDir": "dist", + "strict": true, + "esModuleInterop": true + }, + "include": ["src"] +} + \ No newline at end of file diff --git a/tdrive/docker/tdrive-ldap-sync/Dockerfile b/tdrive/docker/tdrive-ldap-sync/Dockerfile new file mode 100644 index 00000000..f67c71ee --- /dev/null +++ b/tdrive/docker/tdrive-ldap-sync/Dockerfile @@ -0,0 +1,15 @@ +# Use an official Node.js runtime as the base image +FROM node:lts-alpine + +# Set the working directory inside the container +WORKDIR /usr/src/app + +# Copy app +COPY backend/utils/ldap-sync/*.json ./ +COPY backend/utils/ldap-sync/src/** ./src/ +COPY backend/utils/ldap-sync/.nvmrc ./ + +RUN npm i && npm run build + +# Run the Node.js application +CMD ["npm", "run", "sync"]