diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 85f27bc..b5c64d3 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -110,7 +110,7 @@ This is a **modular LDAP gateway** built with Node.js and ldapjs that bridges LD - **Backend separation**: Always implement both auth and directory providers (see `npm/src/interfaces/`) - **Dynamic backend loading**: Backends can be loaded from `server/backends/*.js` at runtime without rebuilding (see `server/utils/backendLoader.js`) - **Provider factory**: Use `ProviderFactory` to instantiate backends; supports both compiled and dynamic providers -- **Environment config**: All settings via `.env` (e.g., `AUTH_BACKEND=db`, `DIRECTORY_BACKEND=mysql`, `BACKEND_DIR=/custom/path`) +- **Environment config**: All settings via `.env` (e.g., `AUTH_BACKENDS=sql`, `DIRECTORY_BACKEND=sql`, `BACKEND_DIR=/custom/path`, `SQL_URL=mysql://user:pass@host:port/db`) - **LDAP entry mapping**: Use `@ldap-gateway/core` utilities to create entries with standard attributes (posixAccount, inetOrgPerson) - **Database queries**: Use connection pooling in drivers (`server/src/db/drivers/`); groups store `member_uids` as JSON arrays - **UID/GID mapping**: For WebChart, `uidNumber` from "LDAP UID Number" observation or `user_id + 10000`; `gidNumber` from `realms.id` diff --git a/MIGRATION.md b/MIGRATION.md index 0f45eee..39add28 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -67,13 +67,21 @@ npm run dev | `src/.env` | `/etc/ldap-gateway/.env` | Production (binary install) | ### Environment Variables -**No changes required** - all existing environment variables work as before: +**Configuration syntax has changed** - backend names and SQL configuration updated: ```ini -# These remain the same -DIRECTORY_BACKEND=mysql -AUTH_BACKEND=ldap -MYSQL_HOST=localhost +# Backend names changed from 'mysql' to 'sql' +DIRECTORY_BACKEND=sql # Changed from 'mysql' +AUTH_BACKENDS=sql,ldap # Changed from 'mysql,ldap' + +# SQL configuration now uses connection URL and custom queries +SQL_URL=mysql://user:password@localhost:3306/database # Replaces MYSQL_HOST, MYSQL_PORT, etc. +SQL_QUERY_ONE_USER='SELECT * FROM users WHERE username = ?' +SQL_QUERY_ALL_USERS='SELECT * FROM users' +SQL_QUERY_GROUPS_BY_MEMBER='SELECT * FROM groups WHERE member = ?' +SQL_QUERY_ALL_GROUPS='SELECT * FROM groups' + +# Other settings remain the same LDAP_BIND_DN=... # etc. ``` diff --git a/README.md b/README.md index 97425ae..853b7b8 100644 --- a/README.md +++ b/README.md @@ -118,26 +118,35 @@ Create or edit `/etc/ldap-gateway/.env`: ```ini # Directory backend: where to find user/group information -DIRECTORY_BACKEND=mysql # mysql | mongodb | proxmox +DIRECTORY_BACKEND=sql # sql | mongodb | proxmox # Authentication backends: how to validate passwords (comma-separated for multiple) -AUTH_BACKENDS=ldap # mysql | mongodb | ldap | proxmox | notification | mysql,ldap | ldap,notification +AUTH_BACKENDS=ldap # sql | mongodb | ldap | proxmox | notification | sql,ldap | ldap,notification # LDAP Server Configuration LDAP_BASE_DN=dc=company,dc=com +# SQL configuration (for any SQL-based system) +SQL_URL=mysql://ldap_user:secure_password@localhost:3306/your_database +SQL_QUERY_ONE_USER='SELECT * FROM users WHERE username = ?' +SQL_QUERY_GROUPS_BY_MEMBER='SELECT * FROM groups g WHERE JSON_CONTAINS(g.member_uids, JSON_QUOTE(?))' +SQL_QUERY_ALL_USERS='SELECT * FROM users' +SQL_QUERY_ALL_GROUPS='SELECT + g.gid_number, + g.name, + g.gid_number AS id, + GROUP_CONCAT(u.username) AS member_uids +FROM groups g +LEFT JOIN user_groups ug ON g.gid_number = ug.group_id +LEFT JOIN users u ON ug.user_id = u.id +GROUP BY g.gid_number, g.name +ORDER BY g.name' + # Security: Require authentication for search operations # Default: true (authentication required for security) # Set to false only for development/testing if you need anonymous access REQUIRE_AUTH_FOR_SEARCH=true -# MySQL configuration (for any MySQL-based system) -MYSQL_HOST=localhost -MYSQL_PORT=3306 -MYSQL_USER=ldap_user -MYSQL_PASSWORD=secure_password -MYSQL_DATABASE=your_database - # MongoDB configuration (for mongodb backends) MONGO_URI=mongodb://localhost:27017/ldap_user_db MONGO_DATABASE=ldap_user_db @@ -203,17 +212,17 @@ The LDAP gateway separates **directory lookups** from **authentication**, allowi | Backend | Description | Use Case | |---------|-------------|----------| -| `mysql` | MySQL/MariaDB databases | Any MySQL-based system (WebChart, custom schemas) | +| `sql` | MySQL/MariaDB/SQLite3/PostgreSQL databases | Any SQL-based system (WebChart, custom schemas) | | `mongodb` | MongoDB collections | Modern web applications | | `proxmox` | Proxmox user.cfg/shadow.cfg files | Virtualization environments | ### Authentication Backends (`AUTH_BACKENDS`) -**Multiple backends supported** - Use comma-separated values (e.g., `AUTH_BACKENDS=mysql,ldap`) to try authentication providers in order. +**Multiple backends supported** - Use comma-separated values (e.g., `AUTH_BACKENDS=sql,ldap`) to try authentication providers in order. | Backend | Description | Use Case | |---------|-------------|----------| -| `mysql` | MySQL/MariaDB password hashes | Self-contained auth with MySQL databases | +| `sql` | MySQL/MariaDB/SQLite3/PostgreSQL password hashes | Self-contained auth with SQL databases | | `mongodb` | MongoDB password hashes | Self-contained auth with MongoDB collections | | `ldap` | External LDAP/Active Directory | Enterprise SSO integration | | `proxmox` | Proxmox shadow file | Proxmox container authentication | @@ -221,24 +230,46 @@ The LDAP gateway separates **directory lookups** from **authentication**, allowi ### Example Configurations -#### MySQL + Active Directory +#### SQL + Active Directory ```ini -DIRECTORY_BACKEND=mysql # User info from MySQL +DIRECTORY_BACKEND=sql # User info from MySQL AUTH_BACKENDS=ldap # Passwords via AD -MYSQL_HOST=your-mysql-host -MYSQL_DATABASE=your_database AD_DOMAIN=your-domain.com LDAP_BIND_DN=CN=service,DC=your-domain,DC=com +SQL_URL=mysql://ldap_user:secure_password@localhost:3306/your_database +SQL_QUERY_ONE_USER='SELECT * FROM users WHERE username = ?' +SQL_QUERY_GROUPS_BY_MEMBER='SELECT * FROM groups g WHERE JSON_CONTAINS(g.member_uids, JSON_QUOTE(?))' +SQL_QUERY_ALL_USERS='SELECT * FROM users' +SQL_QUERY_ALL_GROUPS='SELECT + g.gid_number, + g.name, + g.gid_number AS id, + GROUP_CONCAT(u.username) AS member_uids +FROM groups g +LEFT JOIN user_groups ug ON g.gid_number = ug.group_id +LEFT JOIN users u ON ug.user_id = u.id +GROUP BY g.gid_number, g.name +ORDER BY g.name' ``` #### MySQL Self-Contained ```ini DIRECTORY_BACKEND=mysql # User info from MySQL AUTH_BACKENDS=mysql # Passwords in MySQL -MYSQL_HOST=localhost -MYSQL_DATABASE=users -MYSQL_USER=ldap_user -MYSQL_PASSWORD=secure_password +SQL_URL=mysql://ldap_user:secure_password@localhost:3306/your_database +SQL_QUERY_ONE_USER='SELECT * FROM users WHERE username = ?' +SQL_QUERY_GROUPS_BY_MEMBER='SELECT * FROM groups g WHERE JSON_CONTAINS(g.member_uids, JSON_QUOTE(?))' +SQL_QUERY_ALL_USERS='SELECT * FROM users' +SQL_QUERY_ALL_GROUPS='SELECT + g.gid_number, + g.name, + g.gid_number AS id, + GROUP_CONCAT(u.username) AS member_uids +FROM groups g +LEFT JOIN user_groups ug ON g.gid_number = ug.group_id +LEFT JOIN users u ON ug.user_id = u.id +GROUP BY g.gid_number, g.name +ORDER BY g.name' ``` #### MongoDB Self-Contained @@ -262,22 +293,46 @@ PROXMOX_SHADOW_CFG=/etc/pve/shadow.cfg šŸŽ„ **[Multiple Backends Demo](https://youtube.com/shorts/4N-aov0wxZ4?si=AA9SN_s_EfpkM-MK)** - See how to configure multiple authentication backends ```ini -DIRECTORY_BACKEND=mysql # User info from MySQL -AUTH_BACKENDS=mysql,ldap # Try MySQL auth first, fallback to LDAP -MYSQL_HOST=your-mysql-host -MYSQL_DATABASE=your_database +DIRECTORY_BACKEND=sql # User info from SQL +AUTH_BACKENDS=sql,ldap # Try SQL auth first, fallback to LDAP +SQL_URL=mysql://ldap_user:secure_password@localhost:3306/your_database +SQL_QUERY_ONE_USER='SELECT * FROM users WHERE username = ?' +SQL_QUERY_GROUPS_BY_MEMBER='SELECT * FROM groups g WHERE JSON_CONTAINS(g.member_uids, JSON_QUOTE(?))' +SQL_QUERY_ALL_USERS='SELECT * FROM users' +SQL_QUERY_ALL_GROUPS='SELECT + g.gid_number, + g.name, + g.gid_number AS id, + GROUP_CONCAT(u.username) AS member_uids +FROM groups g +LEFT JOIN user_groups ug ON g.gid_number = ug.group_id +LEFT JOIN users u ON ug.user_id = u.id +GROUP BY g.gid_number, g.name +ORDER BY g.name' AD_DOMAIN=your-domain.com LDAP_BIND_DN=CN=service,DC=your-domain,DC=com ``` #### MFA with Push Notifications ```ini -DIRECTORY_BACKEND=mysql # User info from MySQL +DIRECTORY_BACKEND=sql # User info from MySQL AUTH_BACKENDS=ldap,notification # LDAP auth + MFA push notifications -MYSQL_HOST=your-mysql-host -MYSQL_DATABASE=your_database AD_DOMAIN=your-domain.com LDAP_BIND_DN=CN=service,DC=your-domain,DC=com +SQL_URL=mysql://ldap_user:secure_password@localhost:3306/your_database +SQL_QUERY_ONE_USER='SELECT * FROM users WHERE username = ?' +SQL_QUERY_GROUPS_BY_MEMBER='SELECT * FROM groups g WHERE JSON_CONTAINS(g.member_uids, JSON_QUOTE(?))' +SQL_QUERY_ALL_USERS='SELECT * FROM users' +SQL_QUERY_ALL_GROUPS='SELECT + g.gid_number, + g.name, + g.gid_number AS id, + GROUP_CONCAT(u.username) AS member_uids +FROM groups g +LEFT JOIN user_groups ug ON g.gid_number = ug.group_id +LEFT JOIN users u ON ug.user_id = u.id +GROUP BY g.gid_number, g.name +ORDER BY g.name' # MFA Configuration (requires MIE Authenticator app) ENABLE_NOTIFICATION=true @@ -367,15 +422,13 @@ ldap-gateway --config-test ## šŸ„ WebChart Integration -The LDAP Gateway integrates with [WebChart EHR](https://www.mieweb.com/) systems using the MySQL backend: +The LDAP Gateway integrates with [WebChart EHR](https://www.mieweb.com/) systems using the SQL backend: ```ini -DIRECTORY_BACKEND=mysql # WebChart uses MySQL -AUTH_BACKENDS=mysql -MYSQL_HOST=webchart-host -MYSQL_DATABASE=webchart -MYSQL_USER=readonly_user -MYSQL_PASSWORD=secure_password +DIRECTORY_BACKEND=sql # WebChart uses MySQL +AUTH_BACKENDS=sql +SQL_URL=mysql://ldap_user:secure_password@localhost:3306/your_database +# TODO: implement queries matching the webchart schema ``` WebChart users are mapped to standard LDAP objects with healthcare-specific attributes and group memberships based on WebChart realms. diff --git a/nfpm/systemd/ldap-gateway.service b/nfpm/systemd/ldap-gateway.service index 6cafaca..517b4c1 100644 --- a/nfpm/systemd/ldap-gateway.service +++ b/nfpm/systemd/ldap-gateway.service @@ -25,6 +25,9 @@ PrivateTmp=yes ProtectSystem=strict ProtectHome=yes +# Allow creating self-signed certs on startup +ReadWritePaths=/opt/ldap-gateway/server/cert + # Capabilities AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE diff --git a/npm/src/utils/ldapUtils.js b/npm/src/utils/ldapUtils.js index 2ea3cf1..3cbea56 100644 --- a/npm/src/utils/ldapUtils.js +++ b/npm/src/utils/ldapUtils.js @@ -16,7 +16,7 @@ function createLdapEntry(user, baseDn) { const entry = { dn: `uid=${user.username},${baseDn}`, attributes: { - objectClass: ["top", "posixAccount", "inetOrgPerson", "shadowAccount"], + objectClass: ["top", "posixAccount", "inetOrgPerson"], uid: user.username, uidNumber, gidNumber, @@ -26,8 +26,6 @@ function createLdapEntry(user, baseDn) { mail: user.mail || `${user.username}@${extractDomainFromBaseDn(baseDn)}`, homeDirectory: user.home_directory || `/home/${user.username}`, loginShell: "/bin/bash", - shadowLastChange: "0", - userpassword: user?.password, }, }; diff --git a/package-lock.json b/package-lock.json index d40d739..91a1871 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,12 @@ "kuler": "^2.0.0" } }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true + }, "node_modules/@ldap-gateway/core": { "resolved": "npm", "link": true @@ -162,18 +168,231 @@ "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", "license": "MIT" }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@phc/format": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", + "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dependencies": { + "undici-types": "~7.16.0" + } + }, "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", "license": "MIT" }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==" + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argon2": { + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.31.2.tgz", + "integrity": "sha512-QSnJ8By5Mth60IEte45w9Y7v6bWcQw3YhRtJKKN8oNCxnTLDiv/AXXkDPf2srTMfxFVn3QJdVv2nhXESsUa+Yg==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "@phc/format": "^1.0.0", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -218,6 +437,127 @@ "node": ">= 0.6" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/bcrypt/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -246,6 +586,23 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -281,6 +638,14 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/colorspace": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", @@ -303,12 +668,60 @@ "node": ">= 0.8" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "license": "MIT" }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -318,6 +731,24 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -332,12 +763,49 @@ "node": ">= 0.4" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", "license": "MIT" }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -383,6 +851,14 @@ "node": ">= 0.4" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, "node_modules/extsprintf": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", @@ -398,6 +874,11 @@ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", "license": "MIT" }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -440,6 +921,27 @@ "node": ">= 6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -449,6 +951,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -486,6 +1008,31 @@ "node": ">= 0.4" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -498,6 +1045,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "optional": true + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -525,6 +1078,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -537,30 +1095,178 @@ "node": ">= 0.4" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT" + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "optional": true }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "optional": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "optional": true + }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -599,6 +1305,11 @@ "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", "license": "MIT" }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/logform": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", @@ -616,6 +1327,67 @@ "node": ">= 12.0.0" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -646,28 +1418,456 @@ "node": ">= 0.6" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.85.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz", + "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", "dependencies": { - "wrappy": "1" + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "license": "MIT", - "dependencies": { - "fn.name": "1.x.x" + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/precond": { @@ -684,12 +1884,54 @@ "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==", "license": "MIT" }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -717,6 +1959,35 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -746,6 +2017,144 @@ "node": ">=10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize": { + "version": "6.37.7", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", + "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-swizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", @@ -755,6 +2164,87 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "optional": true, + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -773,12 +2263,109 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -788,12 +2375,62 @@ "node": ">= 14.0.0" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.15.23", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz", + "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vasync": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz", @@ -834,6 +2471,43 @@ "node": ">=0.6.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/winston": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", @@ -870,12 +2544,33 @@ "node": ">= 12.0.0" } }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "npm": { "name": "@ldap-gateway/core", "version": "0.1.0", @@ -898,13 +2593,18 @@ "dependencies": { "@ldap-gateway/core": "file:../npm", "@ldapjs/controls": "git+https://github.com/mieweb/controls.git", + "argon2": "^0.31.2", "axios": "^1.7.9", + "bcrypt": "^5.1.1", "chokidar": "4.0.3", "dotenv": "^16.4.7", "express": "^4.21.2", "ldapjs": "git+https://github.com/mieweb/node-ldapjs.git", "mongodb": "^6.14.2", "mysql2": "^3.2.0", + "pg": "8.16.3", + "sequelize": "6.37.7", + "sqlite3": "5.1.7", "unixcrypt": "^1.2.0", "winston": "^3.17.0" }, @@ -1453,10 +3153,6 @@ "node": ">= 0.8" } }, - "server/node_modules/safer-buffer": { - "version": "2.1.2", - "license": "MIT" - }, "server/node_modules/send": { "version": "0.19.0", "license": "MIT", diff --git a/server/.env.example b/server/.env.example index a7e7e9c..6590200 100644 --- a/server/.env.example +++ b/server/.env.example @@ -1,12 +1,12 @@ # LDAP Gateway Server Configuration # Authentication Backend(s) - separate multiple with commas for multi-auth (AND logic) -# Options: mysql, mongodb, ldap, proxmox, notification -AUTH_BACKENDS=mysql +# Options: sql, mongodb, ldap, proxmox, notification +AUTH_BACKENDS=sql # Directory Backend -# Options: mysql, mongodb, proxmox -DIRECTORY_BACKEND=mysql +# Options: sql, mongodb, proxmox +DIRECTORY_BACKEND=sql # LDAP Server Configuration LDAP_COMMON_NAME=localhost @@ -30,12 +30,13 @@ LDAP_BASE_DN=dc=localhost # Option 3: Disable SSL/TLS (not recommended for production) # LDAP_UNENCRYPTED=false -# MySQL Database Configuration (for mysql auth/directory backends) -MYSQL_HOST=localhost -MYSQL_PORT=3306 -MYSQL_DATABASE=ldap_user_db -MYSQL_USER=root -MYSQL_PASSWORD=rootpassword +# SQL Database Configuration (for sql auth/directory backends) +# Supports MySQL, MariaDB, PostgreSQL, SQLite3 +SQL_URL=mysql://root:rootpassword@localhost:3306/ldap_user_db +SQL_QUERY_ONE_USER='SELECT * FROM users WHERE username = ?' +SQL_QUERY_GROUPS_BY_MEMBER='SELECT * FROM groups g WHERE JSON_CONTAINS(g.member_uids, JSON_QUOTE(?))' +SQL_QUERY_ALL_USERS='SELECT * FROM users' +SQL_QUERY_ALL_GROUPS='SELECT g.gid_number, g.name, g.gid_number AS id, GROUP_CONCAT(u.username) AS member_uids FROM groups g LEFT JOIN user_groups ug ON g.gid_number = ug.group_id LEFT JOIN users u ON ug.user_id = u.id GROUP BY g.gid_number, g.name ORDER BY g.name' # MongoDB Configuration (for mongodb auth/directory backends) MONGO_URI=mongodb://localhost:27017/ldap_user_db diff --git a/server/backends/QUICKSTART.md b/server/backends/QUICKSTART.md index aa5017e..c908f0f 100644 --- a/server/backends/QUICKSTART.md +++ b/server/backends/QUICKSTART.md @@ -110,7 +110,7 @@ AUTH_BACKEND=redis-auth REDIS_URL=redis://localhost:6379 # Keep existing directory backend -DIRECTORY_BACKEND=mysql +DIRECTORY_BACKEND=sql ``` ### Step 4: Add Test Data to Redis diff --git a/server/backends/mysql.auth.js b/server/backends/mysql.auth.js deleted file mode 100644 index 61078da..0000000 --- a/server/backends/mysql.auth.js +++ /dev/null @@ -1,70 +0,0 @@ -const { AuthProvider } = require('@ldap-gateway/core'); -const mysqlDriver = require('../db/drivers/mysql'); -const logger = require('../utils/logger'); - -/** - * MySQL Authentication Provider - * Handles user authentication against MySQL database - */ -class MySQLAuthProvider extends AuthProvider { - constructor() { - super(); - this.config = { - type: 'mysql', - host: process.env.MYSQL_HOST || "mysql", - user: process.env.MYSQL_USER || "root", - password: process.env.MYSQL_PASSWORD || "rootpassword", - database: process.env.MYSQL_DATABASE || "ldap_user_db", - }; - this.initialized = false; - } - - async initialize() { - if (!this.initialized) { - logger.info('[MySQLAuthProvider] Initializing MySQL connection...'); - await mysqlDriver.connect(this.config); - this.initialized = true; - logger.info(`[MySQLAuthProvider] Connected to MySQL: ${this.config.host}/${this.config.database}`); - } - } - - async authenticate(username, password) { - try { - await this.initialize(); - - logger.debug(`[MySQLAuthProvider] Authenticating user: ${username}`); - const user = await mysqlDriver.findUserByUsername(username); - - if (!user) { - logger.debug(`[MySQLAuthProvider] User not found: ${username}`); - return false; - } - - // TODO: Implement proper password hashing (bcrypt, etc.) - // For now, using plain text comparison (NOT for production) - const isValid = user.password === password; - - logger.debug(`[MySQLAuthProvider] Authentication result for ${username}: ${isValid}`); - return isValid; - - } catch (error) { - logger.error(`[MySQLAuthProvider] Authentication error for ${username}:`, error); - return false; - } - } - - async cleanup() { - if (this.initialized) { - logger.info('[MySQLAuthProvider] Cleaning up MySQL connection...'); - await mysqlDriver.close(); - this.initialized = false; - logger.info('[MySQLAuthProvider] MySQL connection closed'); - } - } -} - -module.exports = { - name: 'mysql', - type: 'auth', - provider: MySQLAuthProvider, -}; \ No newline at end of file diff --git a/server/backends/mysql.directory.js b/server/backends/mysql.directory.js deleted file mode 100644 index c051d00..0000000 --- a/server/backends/mysql.directory.js +++ /dev/null @@ -1,166 +0,0 @@ -const { DirectoryProvider, filterUtils } = require('@ldap-gateway/core'); -const mysqlDriver = require('../db/drivers/mysql'); -const logger = require('../utils/logger'); - -/** - * MySQL Directory Provider - * Handles user and group directory operations against MySQL database - */ -class MySQLDirectoryProvider extends DirectoryProvider { - constructor() { - super(); - this.config = { - type: 'mysql', - host: process.env.MYSQL_HOST || "mysql", - user: process.env.MYSQL_USER || "root", - password: process.env.MYSQL_PASSWORD || "rootpassword", - database: process.env.MYSQL_DATABASE || "ldap_user_db", - }; - this.initialized = false; - } - - async initialize() { - if (!this.initialized) { - logger.info('[MySQLDirectoryProvider] Initializing MySQL connection...'); - await mysqlDriver.connect(this.config); - this.initialized = true; - logger.info(`[MySQLDirectoryProvider] Connected to MySQL: ${this.config.host}/${this.config.database}`); - } - } - - async findUser(username) { - try { - await this.initialize(); - - logger.debug(`[MySQLDirectoryProvider] Finding user: ${username}`); - const user = await mysqlDriver.findUserByUsername(username); - - if (user) { - logger.debug(`[MySQLDirectoryProvider] User found: ${username}`); - } else { - logger.debug(`[MySQLDirectoryProvider] User not found: ${username}`); - } - - return user; - - } catch (error) { - logger.error(`[MySQLDirectoryProvider] Error finding user ${username}:`, error); - return null; - } - } - - async findGroups(filter) { - try { - await this.initialize(); - - logger.debug(`[MySQLDirectoryProvider] Finding groups with filter: ${filter}`); - - // Parse the filter to extract all conditions - const filterConditions = filterUtils.parseGroupFilter(filter); - logger.debug(`[MySQLDirectoryProvider] Parsed filter conditions:`, filterConditions); - - // If memberUid filter is present, use optimized query (skip wildcards) - if (filterConditions.memberUid && filterConditions.memberUid !== '*') { - const username = filterConditions.memberUid; - logger.debug(`[MySQLDirectoryProvider] Finding groups for member: ${username}`); - const groups = await mysqlDriver.findGroupsByMemberUid(username); - - // Apply cn filter if present (skip wildcards) - if (filterConditions.cn && filterConditions.cn !== '*') { - const filtered = groups.filter(g => g.name === filterConditions.cn); - logger.debug(`[MySQLDirectoryProvider] After cn filter (${filterConditions.cn}): ${filtered.length} groups`); - return filtered; - } - - return groups; - } - - // Get all groups and apply filters - let groups = await mysqlDriver.getAllGroups(); - - // Apply cn filter if present (skip wildcards) - if (filterConditions.cn && filterConditions.cn !== '*') { - groups = groups.filter(g => g.name === filterConditions.cn); - logger.debug(`[MySQLDirectoryProvider] After cn filter (${filterConditions.cn}): ${groups.length} groups`); - } - - // Apply gidNumber filter if present - if (filterConditions.gidNumber && filterConditions.gidNumber !== '*') { - const gidNum = parseInt(filterConditions.gidNumber, 10); - groups = groups.filter(g => g.gid_number === gidNum || g.gidNumber === gidNum); - logger.debug(`[MySQLDirectoryProvider] After gidNumber filter (${gidNum}): ${groups.length} groups`); - - // If no explicit group found, check for user private group - if (groups.length === 0) { - const users = await mysqlDriver.getAllUsers(); - const user = users.find(u => u.gid_number === gidNum || u.gidNumber === gidNum); - if (user) { - logger.debug(`[MySQLDirectoryProvider] Creating implicit user private group for gid ${gidNum} (user: ${user.username})`); - groups = [{ - name: user.username, - memberUids: [user.username], - gid_number: gidNum, - gidNumber: gidNum, - dn: `cn=${user.username},${process.env.LDAP_BASE_DN}`, - objectClass: ["posixGroup"], - }]; - } - } - } - - logger.debug(`[MySQLDirectoryProvider] Returning ${groups.length} groups`); - return groups; - - } catch (error) { - logger.error('[MySQLDirectoryProvider] Error finding groups:', error); - return []; - } - } - - async getAllUsers() { - try { - await this.initialize(); - - logger.debug('[MySQLDirectoryProvider] Getting all users'); - const users = await mysqlDriver.getAllUsers(); - - logger.debug(`[MySQLDirectoryProvider] Found ${users.length} users`); - return users; - - } catch (error) { - logger.error('[MySQLDirectoryProvider] Error getting all users:', error); - return []; - } - } - - async getAllGroups() { - try { - await this.initialize(); - - logger.debug('[MySQLDirectoryProvider] Getting all groups'); - const groups = await mysqlDriver.getAllGroups(); - - logger.debug(`[MySQLDirectoryProvider] Found ${groups.length} groups`); - return groups; - - } catch (error) { - logger.error('[MySQLDirectoryProvider] Error getting all groups:', error); - return []; - } - } - - async cleanup() { - if (this.initialized) { - logger.info('[MySQLDirectoryProvider] Cleaning up MySQL connection...'); - await mysqlDriver.close(); - this.initialized = false; - logger.info('[MySQLDirectoryProvider] MySQL connection closed'); - } - } -} - -module.exports = { - name: 'mysql', - type: 'directory', - provider: MySQLDirectoryProvider, -}; \ No newline at end of file diff --git a/server/backends/sql.auth.js b/server/backends/sql.auth.js new file mode 100644 index 0000000..4e1c318 --- /dev/null +++ b/server/backends/sql.auth.js @@ -0,0 +1,111 @@ +const { AuthProvider } = require('@ldap-gateway/core'); +const logger = require('../utils/logger'); +const { Sequelize } = require('sequelize'); +const argon2 = require('argon2'); +const bcrypt = require('bcrypt'); +const unixcrypt = require('unixcrypt'); + +/** + * SQL Authentication Provider + * Handles user authentication against SQL database + */ +class SQLAuthProvider extends AuthProvider { + constructor() { + super(); + this.sequelize = new Sequelize( + process.env.SQL_URI, + { logging: msg => logger.debug(msg) } + ); + } + + // constructor handles initialization + async initialize() { return; } + + /** + * Verify password against crypt-style hash + * Supports: argon2, bcrypt, sha512, sha256, md5, des + */ + async verifyCryptPassword(password, hash) { + if (!hash || !hash.startsWith('$')) { + logger.warn('[SQLAuthProvider] Invalid hash format - must be crypt-style (starting with $)'); + return false; + } + + const parts = hash.split('$'); + if (parts.length < 3) { + logger.warn('[SQLAuthProvider] Malformed crypt hash'); + return false; + } + + const hashType = parts[1]; + logger.debug(`[SQLAuthProvider] Detected hash type: ${hashType}`); + + try { + // Argon2 format: $argon2i$, $argon2d$, $argon2id$ + if (hashType.startsWith('argon2')) { + return await argon2.verify(hash, password); + } + + // Bcrypt format: $2a$, $2b$, $2y$ + if (hashType.startsWith('2')) { + return await bcrypt.compare(password, hash); + } + + // Unix crypt formats: $6$ (sha512), $5$ (sha256), $1$ (md5), or no $ (des) + if (['1', '5', '6'].includes(hashType) || !hashType) { + const crypted = unixcrypt.encrypt(password, hash); + return crypted === hash; + } + + logger.warn(`[SQLAuthProvider] Unsupported hash type: ${hashType}`); + return false; + + } catch (error) { + logger.error(`[SQLAuthProvider] Password verification error:`, error); + return false; + } + } + + async authenticate(username, password) { + try { + logger.debug(`[SQLAuthProvider] Authenticating user: ${username}`); + const [results, _] = await this.sequelize.query( + process.env.SQL_QUERY_ONE_USER, + { replacements: [username] } + ); + + if (results.length === 0) { + logger.debug(`[SQLAuthProvider] User not found: ${username}`); + return false; + } + const user = results[0]; + + if (!user.password) { + logger.warn(`[SQLAuthProvider] No password hash found for user: ${username}`); + return false; + } + + // Verify password against crypt-style hash + const isValid = await this.verifyCryptPassword(password, user.password); + + logger.debug(`[SQLAuthProvider] Authentication result for ${username}: ${isValid}`); + return isValid; + + } catch (error) { + logger.error(`[SQLAuthProvider] Authentication error for ${username}:`, error); + return false; + } + } + + async cleanup() { + logger.info('[SQLAuthProvider] Cleaning up SQL connection...'); + await this.sequelize.close(); + logger.info('[SQLAuthProvider] SQL connection closed'); + } +} + +module.exports = { + name: 'sql', + type: 'auth', + provider: SQLAuthProvider, +}; \ No newline at end of file diff --git a/server/backends/sql.directory.js b/server/backends/sql.directory.js new file mode 100644 index 0000000..1573d07 --- /dev/null +++ b/server/backends/sql.directory.js @@ -0,0 +1,152 @@ +const { DirectoryProvider, filterUtils } = require('@ldap-gateway/core'); +const logger = require('../utils/logger'); +const { Sequelize } = require('sequelize'); + +/** + * SQL Directory Provider + * Handles user and group directory operations against SQL database + */ +class SQLDirectoryProvider extends DirectoryProvider { + constructor() { + super(); + this.sequelize = new Sequelize( + process.env.SQL_URI, + { logging: msg => logger.debug(msg) } + ); + } + + // constructor handles initialization + async initialize() { return; } + + async findUser(username) { + try { + logger.debug(`[SQLDirectoryProvider] Finding user: ${username}`); + const [results, _] = await this.sequelize.query( + process.env.SQL_QUERY_ONE_USER, + { replacements: [username] } + ); + + if (results.length === 0) { + logger.debug(`[SQLDirectoryProvider] User not found: ${username}`); + return null; + } + + logger.debug(`[SQLDirectoryProvider] User found: ${username}`); + return results[0]; + } catch (error) { + logger.error(`[SQLDirectoryProvider] Error finding user ${username}:`, error); + return null; + } + } + + async findGroups(filter) { + try { + logger.debug(`[SQLDirectoryProvider] Finding groups with filter: ${filter}`); + + // Parse the filter to extract all conditions + const filterConditions = filterUtils.parseGroupFilter(filter); + logger.debug(`[SQLDirectoryProvider] Parsed filter conditions:`, filterConditions); + + // If memberUid filter is present, use optimized query (skip wildcards) + if (filterConditions.memberUid && filterConditions.memberUid !== '*') { + const username = filterConditions.memberUid; + logger.debug(`[SQLDirectoryProvider] Finding groups for member: ${username}`); + const [groups, _] = await this.sequelize.query( + process.env.SQL_QUERY_GROUPS_BY_MEMBER, + { replacements: [username] } + ); + + // Apply cn filter if present (skip wildcards) + if (filterConditions.cn && filterConditions.cn !== '*') { + const filtered = groups.filter(g => g.name === filterConditions.cn); + logger.debug(`[SQLDirectoryProvider] After cn filter (${filterConditions.cn}): ${filtered.length} groups`); + return filtered; + } + + return groups; + } + + // Get all groups and apply filters + let groups = await this.getAllGroups(); + + // Apply cn filter if present (skip wildcards) + if (filterConditions.cn && filterConditions.cn !== '*') { + groups = groups.filter(g => g.name === filterConditions.cn); + logger.debug(`[SQLDirectoryProvider] After cn filter (${filterConditions.cn}): ${groups.length} groups`); + } + + // Apply gidNumber filter if present + if (filterConditions.gidNumber && filterConditions.gidNumber !== '*') { + const gidNum = parseInt(filterConditions.gidNumber, 10); + groups = groups.filter(g => g.gid_number === gidNum || g.gidNumber === gidNum); + logger.debug(`[SQLDirectoryProvider] After gidNumber filter (${gidNum}): ${groups.length} groups`); + + // If no explicit group found, check for user private group + if (groups.length === 0) { + const users = await this.getAllUsers(); + const user = users.find(u => u.gid_number === gidNum || u.gidNumber === gidNum); + if (user) { + logger.debug(`[SQLDirectoryProvider] Creating implicit user private group for gid ${gidNum} (user: ${user.username})`); + groups = [{ + name: user.username, + memberUids: [user.username], + gid_number: gidNum, + gidNumber: gidNum, + dn: `cn=${user.username},${process.env.LDAP_BASE_DN}`, + objectClass: ["posixGroup"], + }]; + } + } + } + + logger.debug(`[SQLDirectoryProvider] Returning ${groups.length} groups`); + return groups; + + } catch (error) { + logger.error('[SQLDirectoryProvider] Error finding groups:', error); + return []; + } + } + + async getAllUsers() { + try { + logger.debug('[SQLDirectoryProvider] Getting all users'); + const [users, _] = await this.sequelize.query(process.env.SQL_QUERY_ALL_USERS); + + logger.debug(`[SQLDirectoryProvider] Found ${users.length} users`); + return users; + + } catch (error) { + logger.error('[SQLDirectoryProvider] Error getting all users:', error); + return []; + } + } + + async getAllGroups() { + try { + await this.initialize(); + + logger.debug('[SQLDirectoryProvider] Getting all groups'); + const [groups, _] = await this.sequelize.query(process.env.SQL_QUERY_ALL_GROUPS); + + logger.debug(`[SQLDirectoryProvider] Found ${groups.length} groups`); + return groups; + + } catch (error) { + logger.error('[SQLDirectoryProvider] Error getting all groups:', error); + return []; + } + } + + async cleanup() { + logger.info('[SQLDirectoryProvider] Cleaning up SQL connection...'); + await this.sequelize.close(); + logger.info('[SQLDirectoryProvider] SQL connection closed'); + } +} + +module.exports = { + name: 'sql', + type: 'directory', + provider: SQLDirectoryProvider, +}; \ No newline at end of file diff --git a/server/cert/.gitignore b/server/cert/.gitignore new file mode 100644 index 0000000..b722e9e --- /dev/null +++ b/server/cert/.gitignore @@ -0,0 +1 @@ +!.gitignore \ No newline at end of file diff --git a/server/db/drivers/mysql.js b/server/db/drivers/mysql.js deleted file mode 100644 index 5efc5f8..0000000 --- a/server/db/drivers/mysql.js +++ /dev/null @@ -1,117 +0,0 @@ -const mysql = require("mysql2/promise"); -const logger = require("../../utils/logger"); - -// Create a connection pool at startup -let pool; - -function createPool(config) { - if (!pool) { - pool = mysql.createPool({ - host: config.host, - user: config.user, - password: config.password, - database: config.database, - waitForConnections: true, - connectionLimit: 10, // Maximum number of connections in the pool - queueLimit: 0 // No limit on the number of waiting requests - }); - // show connection success with details as a connection string - console.log(`MySQL Connection Pool Created: mysql://${config.user}@${config.host}/${config.database}`); - } - return pool; -} - -// Initialize the connection pool -async function connect(config) { - return createPool(config); -} - -// Close the pool (when shutting down the app) -async function close() { - if (pool) { - await pool.end(); - pool = null; - console.log("MySQL Connection Pool Closed"); - } -} - -async function findUserByUsername(username) { - const connection = await pool.getConnection(); - try { - const [rows] = await connection.execute( - "SELECT * FROM users WHERE username = ?", - [username] - ); - return rows[0] || null; - } finally { - connection.release(); // Release connection back to pool - } -} - -async function findGroupsByMemberUid(username) { - const connection = await pool.getConnection(); - try { - const [rows] = await connection.execute( - "SELECT g.name, g.gid, g.member_uids " + - "FROM `groups` g " + - "WHERE JSON_CONTAINS(g.member_uids, JSON_QUOTE(?))", - [username] - ); - return rows.map(row => { - if (row.member_uids && typeof row.member_uids === 'string') { - try { - row.member_uids = JSON.parse(row.member_uids); - } catch (e) { - // error - } - } - return row; - }); - } finally { - connection.release(); - } -} - -async function getAllUsers() { - const [rows] = await pool.query('SELECT * FROM users'); - return rows; -} - -async function getAllGroups() { - try { - const query = ` - SELECT - g.gid_number, - g.name, - g.gid_number as id, - GROUP_CONCAT(u.username) as member_uids - FROM \`groups\` g - LEFT JOIN user_groups ug ON g.gid_number = ug.group_id - LEFT JOIN users u ON ug.user_id = u.id - GROUP BY g.gid_number, g.name - ORDER BY g.name - `; - - const [groups] = await pool.query(query); - - return groups.map(group => ({ - id: group.id, - name: group.name, - gid_number: group.gid_number, - member_uids: group.member_uids ? group.member_uids.split(',') : [] - })); - } catch (error) { - logger.error('Error getting all groups from MySQL:', error); - throw error; - } -} - - -module.exports = { - connect, - close, - findUserByUsername, - findGroupsByMemberUid, - getAllUsers, - getAllGroups -}; \ No newline at end of file diff --git a/server/package.json b/server/package.json index 1afb259..c16ddb1 100644 --- a/server/package.json +++ b/server/package.json @@ -18,13 +18,18 @@ "dependencies": { "@ldap-gateway/core": "file:../npm", "@ldapjs/controls": "git+https://github.com/mieweb/controls.git", + "argon2": "^0.31.2", "axios": "^1.12.0", + "bcrypt": "^5.1.1", "chokidar": "4.0.3", "dotenv": "^16.4.7", "express": "^4.21.2", "ldapjs": "git+https://github.com/mieweb/node-ldapjs.git", "mongodb": "^6.14.2", "mysql2": "^3.2.0", + "pg": "8.16.3", + "sequelize": "6.37.7", + "sqlite3": "5.1.7", "unixcrypt": "^1.2.0", "winston": "^3.17.0" } diff --git a/server/test/data/directory.sqlite.env b/server/test/data/directory.sqlite.env new file mode 100644 index 0000000..2167b7d --- /dev/null +++ b/server/test/data/directory.sqlite.env @@ -0,0 +1,19 @@ +LDAP_COMMON_NAME=example.com +LDAP_BASE_DN=dc=example,dc=com +PORT=6636 + +AUTH_BACKENDS=sql +DIRECTORY_BACKEND=sql + +SQL_URI=sqlite://test001.sqlite +SQL_QUERY_ALL_USERS='SELECT * FROM users' +SQL_QUERY_ALL_GROUPS='SELECT * FROM groups' +SQL_QUERY_ONE_USER='SELECT * FROM users WHERE username = ?' +SQL_QUERY_GROUPS_BY_MEMBER='SELECT g.* FROM groups g JOIN user_groups ug ON g.gid_number = ug.gid_number JOIN users u ON ug.uid_number = u.uid_number WHERE u.username = ?' + +ENABLE_NOTIFICATION=false +LOG_LEVEL=debug +REQUIRE_AUTH_FOR_SEARCH=false + +# Setting for ldapsearch in the tests +LDAPTLS_REQCERT=never \ No newline at end of file diff --git a/server/test/data/directory.sqlite.sql b/server/test/data/directory.sqlite.sql new file mode 100644 index 0000000..33a26e4 --- /dev/null +++ b/server/test/data/directory.sqlite.sql @@ -0,0 +1,39 @@ +DROP TABLE IF EXISTS users; +CREATE TABLE users ( + uid_number INTEGER PRIMARY KEY NOT NULL, + username TEXT UNIQUE NOT NULL, + gid_number INTEGER NOT NULL, + full_name TEXT NOT NULL, + surname TEXT NOT NULL, + mail TEXT NOT NULL, + password TEXT NOT NULL +); + +DROP TABLE IF EXISTS groups; +CREATE TABLE groups ( + gid_number INTEGER PRIMARY KEY NOT NULL, + name TEXT UNIQUE NOT NULL +); + +DROP TABLE IF EXISTS user_groups; +CREATE TABLE user_groups ( + uid_number INTEGER NOT NULL, + gid_number INTEGER NOT NULL, + FOREIGN KEY (uid_number) REFERENCES users(uid_number), + FOREIGN KEY (gid_number) REFERENCES groups(gid_number) +); + +INSERT INTO groups (gid_number, name) VALUES +(2000, 'sysadmins'), +(2001, 'ldapusers'); + +INSERT INTO users (uid_number, username, gid_number, full_name, surname, mail, password) VALUES +(2000, 'alice', 2001, 'Alice Smith', 'Smith', 'asmith@example.com', 'alicepass'), +(2001, 'bob', 2001, 'Bob Johnson', 'Johnson', 'bjohnson@example.com', 'bobpass'), +(2002, 'carol', 2001, 'Carol Williams', 'Williams', 'cwilliams@example.com', 'carolpass'); + +INSERT INTO user_groups (uid_number, gid_number) VALUES +(2000, 2001), +(2001, 2001), +(2002, 2001), +(2000, 2000); -- alice is also a sysadmin \ No newline at end of file diff --git a/server/test/test001-sqlite.sh b/server/test/test001-sqlite.sh new file mode 100755 index 0000000..8c05da8 --- /dev/null +++ b/server/test/test001-sqlite.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SERVER_DIR="$(dirname "$SCRIPT_DIR")" + +cd "${SERVER_DIR}" || exit 1 + +echo 'Importing test database...' +sqlite3 test001.sqlite < test/data/directory.sqlite.sql + +echo 'Reading environment variables...' +set -a; source test/data/directory.sqlite.env; set +a + +echo 'Starting ldap server...' +npm start & +PID=$! + +echo 'Waiting for server to start...' +for i in {1..10}; do + ldapsearch -x -H ldaps://localhost:$PORT -b "$LDAP_BASE_DN" > /dev/null 2>&1 + if [ $? -eq 0 ]; then + break + fi + echo 'Waiting 5 seconds for LDAP server to be ready...' + sleep 5 +done + +test_search () { + local filter="$1" + local expected_count="$2" + + SEARCHOUT="$(ldapsearch -x \ + -H ldaps://localhost:$PORT \ + -b "$LDAP_BASE_DN" \ + "$filter" \ + 2>/dev/null + )" + if [ $? -ne 0 ]; then + echo "ldapsearch command failed" + kill $PID + wait $PID + exit 1 + fi + + RC="$(echo "$SEARCHOUT" | grep '^dn: ' | wc -l)" + if [ "$RC" -ne "$expected_count" ]; then + echo "$filter returned unexpected number of entries:" + echo "$SEARCHOUT" + kill $PID + wait $PID + exit 1 + fi +} + +echo 'Testing full directory search...' +test_search '' 5 + +echo 'Testing user search...' +test_search '(uid=alice)' 1 + +echo 'Testing group search...' +test_search '(objectClass=posixGroup)' 2 + +echo 'Testing specific group search...' +test_search '(&(objectClass=posixGroup)(cn=ldapusers))' 1 + +kill $PID +wait $PID +exit 0 \ No newline at end of file diff --git a/server/test001.sqlite b/server/test001.sqlite new file mode 100644 index 0000000..76198b8 Binary files /dev/null and b/server/test001.sqlite differ diff --git a/server/utils/backendLoader.js b/server/utils/backendLoader.js index 48f04c9..faed0a7 100644 --- a/server/utils/backendLoader.js +++ b/server/utils/backendLoader.js @@ -152,7 +152,14 @@ class BackendLoader { * @returns {Function|null} Backend class or null if not found */ getDirectoryBackend(name) { - return this.loadedBackends.directory.get(name) || null; + const directoryBackend = this.loadedBackends.directory.get(name); + if (!directoryBackend) { + const msg = `Directory backend not found: ${name}`; + logger.error(`[BackendLoader] ${msg}`); + throw new Error(msg); + } + + return this.loadedBackends.directory.get(name); } /** diff --git a/server/utils/logger.js b/server/utils/logger.js index 7477703..abe149f 100644 --- a/server/utils/logger.js +++ b/server/utils/logger.js @@ -1,5 +1,4 @@ const winston = require('winston'); -const path = require("path"); const customLevels = { levels: { @@ -18,13 +17,8 @@ const customLevels = { }, }; -// Create logs directory if it doesn't exist -const fs = require("fs"); -const logDir = "logs"; -if (!fs.existsSync(logDir)) { - fs.mkdirSync(logDir); -} - +// Logger configured for systemd compatibility +// Logs to stdout only - systemd journald captures and manages logs const logger = winston.createLogger({ levels: customLevels.levels, transports: [ @@ -35,8 +29,6 @@ const logger = winston.createLogger({ winston.format.simple() ), }), - new winston.transports.File({ filename: path.join(logDir, "error.log"), level: "error" }), - new winston.transports.File({ filename: path.join(logDir, "combined.log") }), ], }); diff --git a/server/utils/setupUtils.js b/server/utils/setupUtils.js index aa2d16a..327a933 100644 --- a/server/utils/setupUtils.js +++ b/server/utils/setupUtils.js @@ -82,18 +82,18 @@ async function runInteractiveSetup() { // Authentication backend - simplified choice console.log('\nAuthentication backends:'); - console.log(' mysql - Use MySQL database for authentication'); + console.log(' sql - Use MySQL database for authentication'); console.log(' mongodb - Use MongoDB database for authentication'); console.log(' ldap - Delegate authentication to another LDAP server'); console.log(' proxmox - Use Proxmox VE authentication'); - config.AUTH_BACKEND = await askQuestion(rl, 'Authentication backend (mysql/mongodb/ldap/proxmox)', 'mysql'); + config.AUTH_BACKEND = await askQuestion(rl, 'Authentication backend (sql/mongodb/ldap/proxmox)', 'sql'); // Directory backend - simplified choice console.log('\nDirectory backends:'); - console.log(' mysql - Use MySQL database for user/group directory'); + console.log(' sql - Use MySQL database for user/group directory'); console.log(' mongodb - Use MongoDB database for user/group directory'); console.log(' proxmox - Use Proxmox VE user configuration'); - config.DIRECTORY_BACKEND = await askQuestion(rl, 'Directory backend (mysql/mongodb/proxmox)', 'mysql'); + config.DIRECTORY_BACKEND = await askQuestion(rl, 'Directory backend (sql/mongodb/proxmox)', 'sql'); // Only ask for additional config if needed if (config.AUTH_BACKEND === 'ldap') { @@ -116,13 +116,13 @@ async function runInteractiveSetup() { config.LOG_LEVEL = 'info'; // Ask for database config if using mysql or mongodb backend - if (config.AUTH_BACKEND === 'mysql' || config.DIRECTORY_BACKEND === 'mysql') { + if (config.AUTH_BACKEND === 'sql' || config.DIRECTORY_BACKEND === 'sql') { console.log('\nšŸ“Š MySQL Database Configuration:'); - config.MYSQL_HOST = await askQuestion(rl, 'MySQL host', 'localhost'); - config.MYSQL_PORT = await askQuestion(rl, 'MySQL port', '3306'); - config.MYSQL_DATABASE = await askQuestion(rl, 'MySQL database name', 'ldap_user_db'); - config.MYSQL_USER = await askQuestion(rl, 'MySQL username', 'root'); - config.MYSQL_PASSWORD = await askQuestion(rl, 'MySQL password'); + config.SQL_URI = await askQuestion(rl, 'SQL connection URI', 'sqlite://ldap_user_db.sqlite'); + config.SQL_QUERY_ALL_USERS = await askQuestion(rl, 'SQL query to get all users', 'SELECT * FROM users'); + config.SQL_QUERY_ALL_GROUPS = await askQuestion(rl, 'SQL query to get all groups', 'SELECT * FROM groups'); + config.SQL_QUERY_ONE_USER = await askQuestion(rl, 'SQL query to find one user by username', 'SELECT * FROM users WHERE username = ?'); + config.SQL_QUERY_GROUPS_BY_MEMBER = await askQuestion(rl, 'SQL query to find groups by member username', 'SELECT g.* FROM groups g JOIN user_groups ug ON g.id = ug.group_id JOIN users u ON ug.user_id = u.id WHERE u.username = ?'); } else if (config.AUTH_BACKEND === 'mongodb' || config.DIRECTORY_BACKEND === 'mongodb') { console.log('\nšŸ“Š MongoDB Configuration:'); config.MONGO_URI = await askQuestion(rl, 'MongoDB URI', 'mongodb://localhost:27017/ldap_user_db'); @@ -152,13 +152,19 @@ function generateEnvFile(config) { envContent += `AUTH_BACKENDS=${config.AUTH_BACKEND}\n`; envContent += `DIRECTORY_BACKEND=${config.DIRECTORY_BACKEND}\n\n`; - if (config.DB_HOST) { - envContent += '# Database Configuration\n'; - envContent += `DB_HOST=${config.DB_HOST}\n`; - envContent += `DB_PORT=${config.DB_PORT}\n`; - envContent += `DB_NAME=${config.DB_NAME}\n`; - envContent += `DB_USER=${config.DB_USER}\n`; - envContent += `DB_PASSWORD=${config.DB_PASSWORD}\n\n`; + if (config.AUTH_BACKEND === 'sql' || config.DIRECTORY_BACKEND === 'sql') { + envContent += '# SQL Database Configuration\n'; + envContent += `SQL_URI=${config.SQL_URI}\n`; + envContent += `SQL_QUERY_ALL_USERS=${config.SQL_QUERY_ALL_USERS}\n`; + envContent += `SQL_QUERY_ALL_GROUPS=${config.SQL_QUERY_ALL_GROUPS}\n`; + envContent += `SQL_QUERY_ONE_USER=${config.SQL_QUERY_ONE_USER}\n`; + envContent += `SQL_QUERY_GROUPS_BY_MEMBER=${config.SQL_QUERY_GROUPS_BY_MEMBER}\n\n`; + } + + if (config.AUTH_BACKEND === 'mongodb' || config.DIRECTORY_BACKEND === 'mongodb') { + envContent += '# MongoDB Configuration\n'; + envContent += `MONGO_URI=${config.MONGO_URI}\n`; + envContent += `MONGO_DATABASE=${config.MONGO_DATABASE}\n\n`; } if (config.LDAP_SERVER_URL) { @@ -190,13 +196,11 @@ function generateEnvFile(config) { async function checkAndSetupEnvironment(forceReconfig = false) { const envPath = path.join(process.cwd(), '.env'); + + const needsConfig = forceReconfig || (!process.env.LDAP_BASE_DN || !process.env.AUTH_BACKENDS || !process.env.DIRECTORY_BACKEND) && !fs.existsSync(envPath); - if (!fs.existsSync(envPath) || forceReconfig) { - if (forceReconfig && fs.existsSync(envPath)) { - console.log('\nšŸ”„ Reconfiguring LDAP server...'); - } else { - console.log('No .env file found.'); - } + if (needsConfig) { + console.log('\nšŸ”„ Reconfiguring LDAP server...'); const config = await runInteractiveSetup(); generateEnvFile(config);