ldap-rest 0.13.4 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/index.js +1 -1
- package/dist/config/args.js +1 -1
- package/dist/lib/errors.js +1 -1
- package/dist/lib/expressFormatedResponses.js +1 -1
- package/dist/lib/ldapActions.js +1 -1
- package/dist/lib/operation.js +1 -0
- package/dist/lib/parseConfig.js +1 -1
- package/dist/lib/rawBody.js +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/plugins/auth/hmac.js +1 -1
- package/dist/plugins/auth/totp.js +1 -1
- package/dist/plugins/ldap/onChange.js +1 -1
- package/dist/plugins/scim/errors.js +1 -1
- package/dist/plugins/scim/scim.js +1 -1
- package/dist/plugins/twake/groups.js +1 -1
- package/dist/plugins/twake/instances.js +1 -1
- package/dist/plugins/twake/lifecycleEvents.js +1 -1
- package/dist/plugins/twake/spaces.js +1 -0
- package/dist/plugins/twake/tombstone.js +1 -1
- package/dist/src/bin/index.d.ts +16 -0
- package/dist/src/config/args.d.ts +8 -1
- package/dist/src/hooks.d.ts +17 -0
- package/dist/src/lib/errors.d.ts +5 -0
- package/dist/src/lib/ldapActions.d.ts +12 -1
- package/dist/src/lib/operation.d.ts +29 -0
- package/dist/src/lib/parseConfig.d.ts +4 -1
- package/dist/src/lib/rawBody.d.ts +27 -8
- package/dist/src/lib/utils.d.ts +6 -0
- package/dist/src/plugins/auth/hmac.d.ts +1 -1
- package/dist/src/plugins/ldap/onChange.d.ts +5 -2
- package/dist/src/plugins/scim/scim.d.ts +11 -1
- package/dist/src/plugins/twake/groups.d.ts +72 -12
- package/dist/src/plugins/twake/instances.d.ts +0 -8
- package/dist/src/plugins/twake/spaces.d.ts +186 -0
- package/dist/src/plugins/twake/tombstone.d.ts +5 -0
- package/docs/plugin-development/README.md +21 -0
- package/docs/plugin-development/hooks.md +25 -0
- package/docs/usage/configuration.md +83 -4
- package/docs/usage/plugins/auth/hmac.md +6 -1
- package/docs/usage/plugins/integrations/README.md +6 -0
- package/docs/usage/plugins/integrations/groups.md +12 -5
- package/docs/usage/plugins/integrations/instances.md +0 -10
- package/docs/usage/plugins/integrations/lifecycle-events.md +3 -1
- package/docs/usage/plugins/integrations/spaces.md +221 -0
- package/docs/usage/plugins/ldap/raw.md +1 -1
- package/docs/usage/upgrading.md +131 -0
- package/package.json +6 -1
- package/static/browser/shared/utils/hmac.js +5 -0
- package/static/browser/shared/utils/hmac.js.map +1 -1
package/dist/bin/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fileURLToPath as e}from"url";import{join as t,dirname as r}from"path";import i from"express";import s from"body-parser";import{parseConfig as o}from"../lib/parseConfig.js";import{keepRawBody as n}from"../lib/rawBody.js";import a from"../config/args.js";import h from"../lib/ldapActions.js";import{buildLogger as l}from"../logger/winston.js";import{setLogger as u}from"../lib/expressFormatedResponses.js";import{prefixCoversPath as d}from"../lib/auth/base.js";import{assertAuthzComposition as g}from"../lib/authz/composition.js";import{
|
|
1
|
+
import{fileURLToPath as e}from"url";import{join as t,dirname as r}from"path";import i from"express";import s from"body-parser";import{parseConfig as o}from"../lib/parseConfig.js";import{keepRawBody as n}from"../lib/rawBody.js";import a from"../config/args.js";import h from"../lib/ldapActions.js";import{buildLogger as l}from"../logger/winston.js";import{setLogger as u}from"../lib/expressFormatedResponses.js";import{prefixCoversPath as d}from"../lib/auth/base.js";import{assertAuthzComposition as g}from"../lib/authz/composition.js";import{AUTHZ_REFUSED as c}from"../lib/errors.js";export{BadGatewayError,BadRequestError,ConflictError,ForbiddenError,GatewayTimeoutError,HttpError,NotFoundError,ServiceUnavailableError,TooManyRequestsError,UnauthorizedError,UriTooLongError}from"../lib/errors.js";import{recordHookOwner as p}from"../lib/utils.js";export{asyncHandler,escapeDnValue,escapeLdapFilter,escapeRegex,getCompiledRegex,getParentDn,getRdn,hookOwner,isAuthzRefusal,isChildOf,isDnInBranch,isDummyMemberDn,launchHooks,launchHooksChained,normalizeDn,organizationLink,parseDn,rdnValue,substringSearchFilter,transformSchemas,unescapeDnValue,validateDnValue}from"../lib/utils.js";import f from"../plugins/priority.json.js";class DM{constructor(){this.hooks={},this.loadedPlugins={},this.authenticators=[],this._authState={dispatcherMounted:!1},this._errorMiddlewareSetup=!1,this.bodyParsers=i.Router(),this._composed=!1,this.config=o(a),this.app=i(),this.app.use(this.bodyParsers),this.app.use(s.json({verify:n})),this.app.use(s.urlencoded({extended:!0,verify:n})),this.logger=l(this.config),this.ldap=new h(this),u(this.logger),this.operationSequence=0;const e=[];if(this.config.plugin){const t=this.config.plugin.find(e=>e.includes("configApi"));let r=this.config.plugin.filter(e=>!e.includes("configApi"));const i=this.logger,s=(async()=>{for(const e of f){const t=this.resolveModule(DM.moduleOf(e)),s=r.filter(e=>this.resolveModule(DM.moduleOf(e))===t);if(0!==s.length){r=r.filter(e=>!s.includes(e));for(const e of s)i.debug(`Loading ${e} in the priority pass`),await this.loadPlugin(e)}}})();e.push(s),e.push(s.then(async()=>{const e=r.map(e=>(i.debug(`Loading ${e} in the parallel batch`),this.loadPlugin(e)));await Promise.all(e)})),t&&e.push(Promise.all(e).then(()=>this.loadPlugin(t)))}this.ready=new Promise((t,r)=>{e.length>0?Promise.all(e).then(async()=>{this.setupErrorMiddleware(),this.warnUnauthenticatedRoutes(),await g(this),this._composed=!0,this.afterLoad(),t()}).catch(e=>r(new Error("Error loading plugins: "+e))):(this.setupErrorMiddleware(),this._composed=!0,t())})}routerStack(){const e=this.app;return(e.router??e._router)?.stack}withConfig(e){return Object.assign(Object.create(Object.getPrototypeOf(this)),this,{config:e})}mountAuthDispatcher(e){const t=e.roles||[];t.includes("protect")||t.includes("logging")||this.mountAuthDispatcherNow()}mountAuthDispatcherNow(){this._authState.dispatcherMounted||(this._authState.dispatcherMounted=!0,this.app.use((e,t,r)=>{this.dispatchAuth(e,t,r)}))}registerAuthenticator(e){this.mountAuthDispatcherNow(),this.authenticators.includes(e)||this.authenticators.push(e)}registerBodyParser(e,t){this.bodyParsers.use(e,t)}dispatchAuth(e,t,r){let i=-1,s=[];for(const t of this.authenticators)for(const r of t.pathPrefixes)d(r,e.path)&&(r.length>i?(i=r.length,s=[t]):r.length!==i||s.includes(t)||s.push(t));if(i<0&&(s=this.authenticators.filter(e=>0===e.pathPrefixes.length)),0===s.length)return r();const finish=i=>{if(i>=s.length)return r();const o=s[i];if(!o.afterChain)return finish(i+1);o.afterChain(e,t,()=>finish(i+1))},o=[];e.authenticators=o;const run=r=>{if(r>=s.length)return finish(0);const i=s[r];i.authenticate(e,t,()=>{i.vouchedFor(e)&&o.push(i.name),run(r+1)})};run(0)}afterLoad(){for(const e of Object.values(this.loadedPlugins))try{e.afterLoad?.()}catch(t){this.logger.error(`Plugin ${e.name}: afterLoad failed: ${String(t)}`)}}warnUnauthenticatedRoutes(){const e=Object.values(this.loadedPlugins).filter(e=>e.roles?.includes("auth"));if(0===e.length)return[];const t=[];for(const r of e){if(!r.pathPrefixes||0===r.pathPrefixes.length)return[];t.push(...r.pathPrefixes)}const r=this.routerStack(),i=new Set;for(const e of r||[]){const t=e.route?.path;"string"==typeof t?i.add(t):Array.isArray(t)&&t.forEach(e=>"string"==typeof e&&i.add(e))}const s=[...i].filter(e=>!t.some(t=>e===t||e.startsWith(`${t}/`)));return s.length>0&&this.logger.warn(`Authentication is restricted to ${t.join(", ")}, so these routes are served without authentication: ${s.sort().join(", ")}`),s.sort()}setupErrorMiddleware(){if(this._errorMiddlewareSetup&&this._errorMiddleware){const e=this.routerStack();if(e){const t=e.findIndex(e=>e.handle===this._errorMiddleware);-1!==t&&e.splice(t,1)}}this._errorMiddleware=(e,t,r,i)=>{let s="statusCode"in e?e.statusCode:500,o=e.message;if(500===s&&/\[authz-forbidden\]/.test(e.message)?(s=403,o=c):403===s&&/\[authz-forbidden\]/.test(e.message)&&(o=c),s>=400&&s<500)return this.logger.warn(`Client error ${s} in request ${t.method} ${t.path}: ${o}`),void(r.headersSent||r.status(s).json({error:o}));this.logger.error(`Server error ${s} in request ${t.method} ${t.path}: ${e.message}`,{stack:e.stack,url:t.url,method:t.method}),r.headersSent||r.status(s).json({error:"Internal Server Error",message:this.config.debug?e.message:"An error occurred"})},this.app.use(this._errorMiddleware),this._errorMiddlewareSetup=!0}run(){return process.on("uncaughtException",e=>{this.logger.error(`Uncaught exception: ${e.message}`,{stack:e.stack})}),process.on("unhandledRejection",(e,t)=>{this.logger.error(`Unhandled promise rejection: ${e}`,{reason:e,promise:t})}),new Promise((e,t)=>{this.server=this.app.listen(this.config.port,r=>{r?(this.logger.error(`Error starting server: ${r}`),t(r)):(this.logger.debug(`Server started on port ${this.config.port}`),e())})})}stop(){this.app.removeAllListeners(),this.server?.close(),this.logger.debug("Server stopped")}static moduleOf(e){const t=e.indexOf(":");return-1===t?e:e.substring(0,t)}resolveModule(i){return i.startsWith("core/")?i.replace("core/",t(r(e(import.meta.url)),"..","plugins")+"/").replace(/$/,".js"):i.startsWith(".")?e(new URL(i,import.meta.url)):i}loadPlugin(e){let t,r;if(/:/.test(e)){let i=e.substring(e.indexOf(":")+1);if(e=e.substring(0,e.indexOf(":")),/:/.test(i)){t=i.substring(0,i.indexOf(":")),t||(t=void 0),i=i.substring(i.indexOf(":")+1);try{r=JSON.parse(i),"object"!=typeof r?(this.logger.error(`Overrides for plugin ${e} are not valid: ${i}`),r=void 0):this.logger.debug(`Overrides for plugin ${t||e}: ${i}`)}catch(t){this.logger.error(`Failed to parse overrides for plugin ${e}: ${t}, using ${i}`),r=void 0}}else t=i,t||(t=void 0)}else t=void 0;return this.logger.debug(`Loading plugin ${e}`),e.startsWith("core/")&&(e=this.resolveModule(e)),new Promise((i,s)=>{import(e).then(async o=>{let n;if(o&&o.default&&(o=o.default),r){const e={...this.config,...r};n=new o(this.withConfig(e))}else n=new o(this);if(!n)return s(new Error(`Unable to load ${e}`));i(await this.registerPlugin(e,n,t)),this.logger.debug(`Plugin ${n.name} loaded`)}).catch(t=>s(new Error(`Failed to load plugin ${e}: ${t}`)))})}async admitToComposition(e){this.loadedPlugins[e.name]=e;try{await g(this,e)}finally{delete this.loadedPlugins[e.name]}}async registerPlugin(e,t,r){if(t.name||(t.name=e),r&&(t.name=r),this.loadedPlugins[t.name])return this.logger.warn(`Plugin ${e} not registered: the name "${t.name}" is already taken. This instance is dropped and its routes will not exist. To load the same plugin twice, give each one a name: --plugin '${e}:${t.name}2:{…}'`),!1;if(this.logger.debug(`Registering plugin ${e} as ${t.name}`),t.dependencies)for(const e in t.dependencies)this.loadedPlugins[e]||(this.logger.debug(`Plugin ${t.name} depends on ${e}, loading it first`),await this.loadPlugin(t.dependencies[e]));if(this._composed&&await this.admitToComposition(t),t.api&&(this.mountAuthDispatcher(t),this.logger.debug(`Plugin ${t.name} has API, registering it`),await t.api(this.app),this._errorMiddlewareSetup&&this.setupErrorMiddleware()),t.hooks)for(const e in t.hooks){this.logger.debug(`Plugin ${t.name} has hook ${e}, registering it`);const r=t.hooks[e];if(this.hooks[e]||(this.hooks[e]=[]),!r||"function"!=typeof r)throw new Error(`Plugin ${t.name}: hook ${e} is invalid`);this.hooks[e].push(r),p(r,t.name,e)}return this.loadedPlugins[t.name]=t,!0}}export{DM,p as recordHookOwner};
|
package/dist/config/args.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fileURLToPath as _}from"url";import{join as e,dirname as a}from"path";const t=[["--port","DM_PORT",8081,"number"],["--plugin","DM_PLUGINS",[],"array","--plugins"],["--log-level","DM_LOG_LEVEL","notice"],["--logger","DM_LOGGER","console"],["--api-prefix","DM_API_PREFIX","/api"],["--mail-domain","DM_MAIL_DOMAIN",[],"array","--mail-domains"],["--ldap-base","DM_LDAP_BASE",""],["--ldap-dn","DM_LDAP_DN","cn=admin,dc=example,dc=com"],["--ldap-pwd","DM_LDAP_PWD","admin"],["--ldap-url","DM_LDAP_URL",["ldap://localhost"],"array","--ldap-urls"],["--ldap-user-main-attribute","DM_LDAP_USER_ATTRIBUTE","uid"],["--ldap-cache-max","DM_LDAP_CACHE_MAX",1e3,"number"],["--ldap-cache-ttl","DM_LDAP_CACHE_TTL",0,"number"],["--ldap-pool-size","DM_LDAP_POOL_SIZE",5,"number"],["--ldap-connection-ttl","DM_LDAP_CONNECTION_TTL",60,"number"],["--schemas-path","DM_SCHEMAS_PATH",e(a(_(import.meta.url)),"..","..","static","schemas")],["--mail-attribute","DM_MAIL_ATTRIBUTE","mail"],["--quota-attribute","DM_QUOTA_ATTRIBUTE","mailQuota"],["--delegation-attribute","DM_DELEGATION_ATTRIBUTE","twakeDelegatedUsers"],["--alias-attribute","DM_ALIAS_ATTRIBUTE","mailAlternateAddress"],["--forward-attribute","DM_FORWARD_ATTRIBUTE","mailForwardingAddress"],["--display-name-attribute","DM_DISPLAY_NAME_ATTRIBUTE","displayName"],["--drive-quota-attribute","DM_DRIVE_QUOTA_ATTRIBUTE","twakeDriveQuota"],["--user-class","DM_USER_CLASSES",["top","twakeAccount","twakeWhitePages"],"array","--user-classes"],["--ldap-top-organization","DM_LDAP_TOP_ORGANIZATION",""],["--ldap-organization-class","DM_LDAP_ORGANIZATION_CLASSES",["top","organizationalUnit","twakeDepartment"],"array","--ldap-organization-classes"],["--ldap-organization-link-attribute","DM_LDAP_ORGANIZATION_LINK_ATTRIBUTE","twakeDepartmentLink"],["--ldap-organization-path-attribute","DM_LDAP_ORGANIZATION_PATH_ATTRIBUTE","twakeDepartmentPath"],["--ldap-organization-path-separator","DM_LDAP_ORGANIZATION_PATH_SEPARATOR"," / "],["--ldap-organization-max-subnodes","DM_LDAP_ORGANIZATION_MAX_SUBNODES",50,"number"],["--organization-schema","DM_ORGANIZATION_SCHEMA",""],["--enterprise-domain-name-attribute","DM_ENTERPRISE_DOMAIN_NAME_ATTRIBUTE","associatedDomain"],["--enterprise-domain-link-attribute","DM_ENTERPRISE_DOMAIN_LINK_ATTRIBUTE",""],["--storage-backend","DM_STORAGE_BACKEND",""],["--storage-sweep-interval","DM_STORAGE_SWEEP_INTERVAL",600,"number"],["--storage-ldap-base","DM_STORAGE_LDAP_BASE",""],["--storage-ldap-object-class","DM_STORAGE_LDAP_OBJECT_CLASS","applicationProcess"],["--storage-file-directory","DM_STORAGE_FILE_DIRECTORY",""],["--storage-postgres-url","DM_STORAGE_POSTGRES_URL",""],["--storage-postgres-table","DM_STORAGE_POSTGRES_TABLE","ldap_rest_storage"],["--storage-valkey-url","DM_STORAGE_VALKEY_URL",""],["--storage-valkey-prefix","DM_STORAGE_VALKEY_PREFIX","ldap-rest:"],["--bcl-retention","DM_BCL_RETENTION",604800,"number"],["--ldap-group-base","DM_LDAP_GROUP_BASE",""],["--ldap-groups-main-attribute","DM_LDAP_GROUPS_MAIN_ATTRIBUTE","cn"],["--group-class","DM_GROUP_CLASSES",["top","groupOfNames"],"array","--group-classes"],["--group-allow-unexistent-members","DM_ALLOW_UNEXISTENT_MEMBERS",!1,"boolean"],["--group-default-attributes","DM_GROUP_DEFAULT_ATTRIBUTES",{},"json"],["--group-dummy-user","DM_GROUP_DUMMY_USER","cn=fakeuser"],["--group-schema","DM_GROUP_SCHEMA",e(a(_(import.meta.url)),"..","..","static","schemas","twake","groups.json")],["--external-members-branch","DM_EXTERNAL_MEMBERS_BRANCH","ou=contacts,dc=example,dc=com"],["--external-branch-class","DM_EXTERNAL_BRANCH_CLASSES",["top","inetOrgPerson"],"array","--external-branch-classes"],["--static-path","DM_STATIC_PATH",e(a(_(import.meta.url)),"..","..","static")],["--static-name","DM_STATIC_NAME","static"],["--ldap-flat-schema","DM_LDAP_FLAT_SCHEMA",[],"array","--ldap-flat-schemas"],["--ldap-flat-auto-repair","DM_LDAP_FLAT_AUTO_REPAIR",!0,"boolean"],["--ldap-raw-base","DM_LDAP_RAW_BASE",[],"array","--ldap-raw-bases"],["--ldap-raw-hidden-attribute","DM_LDAP_RAW_HIDDEN_ATTRIBUTES",[],"array","--ldap-raw-hidden-attributes"],["--ldap-raw-show-secrets","DM_LDAP_RAW_SHOW_SECRETS",!1,"boolean"],["--ldap-raw-max-results","DM_LDAP_RAW_MAX_RESULTS",200,"number"],["--ldap-raw-schema-cache-ttl","DM_LDAP_RAW_SCHEMA_CACHE_TTL",3600,"number"],["--bulk-import-schemas","DM_BULK_IMPORT_SCHEMAS",""],["--bulk-import-max-file-size","DM_BULK_IMPORT_MAX_FILE_SIZE","10485760"],["--bulk-import-batch-size","DM_BULK_IMPORT_BATCH_SIZE","100"],["--james-webadmin-url","DM_JAMES_WEBADMIN_URL","http://localhost:8000"],["--james-webadmin-token","DM_JAMES_WEBADMIN_TOKEN",""],["--james-signature-template","DM_JAMES_SIGNATURE_TEMPLATE",""],["--ldap-concurrency","DM_LDAP_CONCURRENCY",10,"number"],["--james-concurrency","DM_JAMES_CONCURRENCY",10,"number"],["--james-init-delay","DM_JAMES_INIT_DELAY",1e3,"number"],["--james-mailing-list-branch","DM_JAMES_MAILING_LIST_BRANCHES",[],"array","--james-mailing-list-branches"],["--james-mailbox-type-attribute","DM_JAMES_MAILBOX_TYPE_ATTRIBUTE","twakeMailboxType"],["--twake-drive-webadmin-url","DM_TWAKE_DRIVE_WEBADMIN_URL",""],["--twake-drive-webadmin-token","DM_TWAKE_DRIVE_WEBADMIN_TOKEN",""],["--twake-drive-concurrency","DM_TWAKE_DRIVE_CONCURRENCY",10,"number"],["--twake-drive-domain-attribute","DM_TWAKE_DRIVE_DOMAIN_ATTRIBUTE","twakeCozyDomain"],["--twake-drive-default-domain-template","DM_TWAKE_DRIVE_DEFAULT_DOMAIN_TEMPLATE",""],["--calendar-webadmin-url","DM_CALENDAR_WEBADMIN_URL","http://localhost:8080"],["--calendar-webadmin-token","DM_CALENDAR_WEBADMIN_TOKEN",""],["--calendar-concurrency","DM_CALENDAR_CONCURRENCY",10,"number"],["--calendar-resource-base","DM_CALENDAR_RESOURCE_BASE",""],["--calendar-resource-objectclass","DM_CALENDAR_RESOURCE_OBJECTCLASS",""],["--calendar-resource-creator","DM_CALENDAR_RESOURCE_CREATOR",""],["--calendar-resource-domain","DM_CALENDAR_RESOURCE_DOMAIN",""],["--calendar-firstname-attribute","DM_CALENDAR_FIRSTNAME_ATTRIBUTE","givenName"],["--calendar-lastname-attribute","DM_CALENDAR_LASTNAME_ATTRIBUTE","sn"],["--cozy-admin-url","DM_COZY_ADMIN_URL",""],["--cozy-admin-user","DM_COZY_ADMIN_USER","admin"],["--cozy-admin-passphrase","DM_COZY_ADMIN_PASSPHRASE",""],["--cozy-org-id","DM_COZY_ORG_ID",""],["--cozy-org-domain","DM_COZY_ORG_DOMAIN",""],["--cozy-default-locale","DM_COZY_DEFAULT_LOCALE","fr"],["--cozy-context-name","DM_COZY_CONTEXT_NAME","default"],["--cozy-apps","DM_COZY_APPS","home,drive,settings,notes,dataproxy"],["--cozy-auth-exchange","DM_COZY_AUTH_EXCHANGE","auth"],["--cozy-b2b-exchange","DM_COZY_B2B_EXCHANGE","b2b"],["--cozy-user-created-routing-key","DM_COZY_USER_CREATED_ROUTING_KEY","user.created"],["--cozy-user-deleted-routing-key","DM_COZY_USER_DELETED_ROUTING_KEY","domain.user.deleted"],["--cloudery-manager-url","DM_CLOUDERY_MANAGER_URL",""],["--cloudery-manager-token","DM_CLOUDERY_MANAGER_TOKEN",""],["--cloudery-offer","DM_CLOUDERY_OFFER","b2b_twake_default"],["--cloudery-domain","DM_CLOUDERY_DOMAIN",""],["--cloudery-user-branch","DM_CLOUDERY_USER_BRANCH",""],["--cloudery-org-id-header","DM_CLOUDERY_ORG_ID_HEADER","x-cloudery-org-id"],["--cloudery-org-role-header","DM_CLOUDERY_ORG_ROLE_HEADER","x-cloudery-org-role"],["--cloudery-default-org-role","DM_CLOUDERY_DEFAULT_ORG_ROLE","member"],["--cloudery-fqdn-attribute","DM_CLOUDERY_FQDN_ATTRIBUTE","twakeWorkspaceUrl"],["--cloudery-org-id-attribute","DM_CLOUDERY_ORG_ID_ATTRIBUTE","twakeOrganizationId"],["--cloudery-org-role-attribute","DM_CLOUDERY_ORG_ROLE_ATTRIBUTE","twakeOrganizationRole"],["--cloudery-phones-attribute","DM_CLOUDERY_PHONES_ATTRIBUTE","twakePhones"],["--cloudery-invited-attribute","DM_CLOUDERY_INVITED_ATTRIBUTE","twakeInvited"],["--cloudery-default-locale","DM_CLOUDERY_DEFAULT_LOCALE","en"],["--cloudery-workflow-poll-interval-ms","DM_CLOUDERY_WORKFLOW_POLL_INTERVAL_MS",2e3,"number"],["--cloudery-workflow-max-attempts","DM_CLOUDERY_WORKFLOW_MAX_ATTEMPTS",60,"number"],["--twake-lifecycle-role-attribute","DM_TWAKE_LIFECYCLE_ROLE_ATTRIBUTE",""],["--twake-lifecycle-lock-attribute","DM_TWAKE_LIFECYCLE_LOCK_ATTRIBUTE",""],["--twake-lifecycle-lock-value","DM_TWAKE_LIFECYCLE_LOCK_VALUE",""],["--twake-lifecycle-deleted-attribute","DM_TWAKE_LIFECYCLE_DELETED_ATTRIBUTE",""],["--twake-lifecycle-deleted-value","DM_TWAKE_LIFECYCLE_DELETED_VALUE","TRUE"],["--twake-lifecycle-deleted-at-attribute","DM_TWAKE_LIFECYCLE_DELETED_AT_ATTRIBUTE",""],["--twake-lifecycle-deleted-at-format","DM_TWAKE_LIFECYCLE_DELETED_AT_FORMAT","iso8601"],["--twake-lifecycle-reason-attribute","DM_TWAKE_LIFECYCLE_REASON_ATTRIBUTE",""],["--twake-lifecycle-rules","DM_TWAKE_LIFECYCLE_RULES",""],["--twake-lifecycle-member-attribute","DM_TWAKE_LIFECYCLE_MEMBER_ATTRIBUTE","member"],["--twake-group-base","DM_TWAKE_GROUP_BASE",""],["--twake-group-user-base","DM_TWAKE_GROUP_USER_BASE",""],["--twake-group-organization-dn","DM_TWAKE_GROUP_ORGANIZATION_DN",""],["--twake-group-organization-status-attribute","DM_TWAKE_GROUP_ORGANIZATION_STATUS_ATTRIBUTE","twakeOrgStatus"],["--twake-group-organization-deleted-value","DM_TWAKE_GROUP_ORGANIZATION_DELETED_VALUE","deleted"],["--twake-group-display-name-attribute","DM_TWAKE_GROUP_DISPLAY_NAME_ATTRIBUTE","twakeDisplayName"],["--twake-group-color-attribute","DM_TWAKE_GROUP_COLOR_ATTRIBUTE","twakeGroupColor"],["--twake-group-created-at-attribute","DM_TWAKE_GROUP_CREATED_AT_ATTRIBUTE","twakeCreatedAt"],["--twake-group-max-page-limit","DM_TWAKE_GROUP_MAX_PAGE_LIMIT",1e3,"number"],["--twake-group-member-fields","DM_TWAKE_GROUP_MEMBER_FIELDS",{},"json"],["--twake-tombstone-dn","DM_TWAKE_TOMBSTONE_DN",[],"array"],["--twake-tombstone-default-reason","DM_TWAKE_TOMBSTONE_DEFAULT_REASON","deleted"],["--twake-tombstone-reason-header","DM_TWAKE_TOMBSTONE_REASON_HEADER","x-deletion-reason"],["--twake-tombstone-reasons","DM_TWAKE_TOMBSTONE_REASONS",[],"array"],["--twake-tombstone-clear-attributes","DM_TWAKE_TOMBSTONE_CLEAR_ATTRIBUTES",[],"array"],["--twake-tombstone-erase-min-age","DM_TWAKE_TOMBSTONE_ERASE_MIN_AGE",2592e3,"number"],["--twake-tombstone-group-bases","DM_TWAKE_TOMBSTONE_GROUP_BASES",[],"array"],["--twake-instance-dn","DM_TWAKE_INSTANCE_DN",[],"array"],["--twake-instance-skip-attribute","DM_TWAKE_INSTANCE_SKIP_ATTRIBUTE",""],["--twake-instance-skip-value","DM_TWAKE_INSTANCE_SKIP_VALUE",""],["--twake-instance-provider","DM_TWAKE_INSTANCE_PROVIDER","cloudery"],["--twake-instance-id","DM_TWAKE_INSTANCE_ID","{uid}"],["--twake-instance-fqdn-attribute","DM_TWAKE_INSTANCE_FQDN_ATTRIBUTE","twakeWorkspaceUrl"],["--twake-instance-sent-attribute","DM_TWAKE_INSTANCE_SENT_ATTRIBUTE","twakeCreatedEventAt"],["--twake-instance-locale","DM_TWAKE_INSTANCE_LOCALE",""],["--twake-instance-timeout","DM_TWAKE_INSTANCE_TIMEOUT",3e4,"number"],["--twake-instance-cloudery-url","DM_TWAKE_INSTANCE_CLOUDERY_URL",""],["--twake-instance-cloudery-token","DM_TWAKE_INSTANCE_CLOUDERY_TOKEN",""],["--twake-instance-cloudery-offer","DM_TWAKE_INSTANCE_CLOUDERY_OFFER","b2b_twake_default"],["--twake-instance-cloudery-domain","DM_TWAKE_INSTANCE_CLOUDERY_DOMAIN",""],["--twake-instance-cloudery-organization-offer","DM_TWAKE_INSTANCE_CLOUDERY_ORGANIZATION_OFFER",""],["--twake-instance-cozy-url","DM_TWAKE_INSTANCE_COZY_URL",""],["--twake-instance-cozy-user","DM_TWAKE_INSTANCE_COZY_USER","admin"],["--twake-instance-cozy-passphrase","DM_TWAKE_INSTANCE_COZY_PASSPHRASE",""],["--twake-instance-cozy-domain","DM_TWAKE_INSTANCE_COZY_DOMAIN",""],["--twake-instance-cozy-context","DM_TWAKE_INSTANCE_COZY_CONTEXT","default"],["--twake-instance-cozy-apps","DM_TWAKE_INSTANCE_COZY_APPS","home,drive,settings,notes,dataproxy"],["--twake-instance-cozy-org-id","DM_TWAKE_INSTANCE_COZY_ORG_ID",""],["--twake-instance-cozy-org-domain","DM_TWAKE_INSTANCE_COZY_ORG_DOMAIN",""],["--twake-instance-auth-exchange","DM_TWAKE_INSTANCE_AUTH_EXCHANGE","auth"],["--twake-instance-b2b-exchange","DM_TWAKE_INSTANCE_B2B_EXCHANGE","b2b"],["--twake-instance-user-created-key","DM_TWAKE_INSTANCE_USER_CREATED_KEY","user.created"],["--twake-instance-organization-created-key","DM_TWAKE_INSTANCE_ORGANIZATION_CREATED_KEY","organization.created"],["--twake-instance-workplace-created-key","DM_TWAKE_INSTANCE_WORKPLACE_CREATED_KEY","workplace.created"],["--twake-instance-queue","DM_TWAKE_INSTANCE_QUEUE","workplace.created.ldap-rest"],["--twake-instance-organization-base","DM_TWAKE_INSTANCE_ORGANIZATION_BASE",""],["--twake-instance-organization-id-attribute","DM_TWAKE_INSTANCE_ORGANIZATION_ID_ATTRIBUTE","ou"],["--twake-instance-organization-domain-attribute","DM_TWAKE_INSTANCE_ORGANIZATION_DOMAIN_ATTRIBUTE","twakeDomain"],["--twake-instance-organization-name-attribute","DM_TWAKE_INSTANCE_ORGANIZATION_NAME_ATTRIBUTE","description"],["--twake-instance-organization-fqdn-attribute","DM_TWAKE_INSTANCE_ORGANIZATION_FQDN_ATTRIBUTE",""],["--twake-instance-organization-account","DM_TWAKE_INSTANCE_ORGANIZATION_ACCOUNT",""],["--rabbitmq-url","DM_RABBITMQ_URL",""],["--applicative-account-base","DM_APPLICATIVE_ACCOUNT_BASE",""],["--max-app-accounts","DM_MAX_APP_ACCOUNTS",5,"number"],["--applicative-account-attribute","DM_APPLICATIVE_ACCOUNT_ATTRIBUTES",["objectClass","cn","sn","givenName","displayName","description"],"array","--applicative-account-attributes"],["--ldap-operational-attribute","DM_LDAP_OPERATIONAL_ATTRIBUTES",["dn","controls","structuralObjectClass","entryUUID","entryDN","subschemaSubentry","modifyTimestamp","modifiersName","createTimestamp","creatorsName","userPassword"],"array","--ldap-operational-attributes"],["--trash-base","DM_TRASH_BASE",""],["--trash-watched-bases","DM_TRASH_WATCHED_BASES",""],["--trash-add-metadata","DM_TRASH_ADD_METADATA","true"],["--trash-auto-create","DM_TRASH_AUTO_CREATE","true"],["--llng-ini","DM_LLNG_INI","/etc/lemonldap-ng/lemonldap-ng.ini"],["--llng-username-header","DM_LLNG_USERNAME_HEADER",""],["--auth-path-prefix","DM_AUTH_PATH_PREFIX",[],"array","--auth-path-prefixes"],["--auth-token","DM_AUTH_TOKENS",[],"array","--auth-tokens"],["--auth-fake-user","DM_AUTH_FAKE_USER",""],["--auth-totp","DM_AUTH_TOTP",[],"array","--auth-totps"],["--auth-totp-window","DM_AUTH_TOTP_WINDOW",1,"number"],["--auth-totp-step","DM_AUTH_TOTP_STEP",30,"number"],["--auth-hmac","DM_AUTH_HMAC",[],"array","--auth-hmacs"],["--auth-hmac-window","DM_AUTH_HMAC_WINDOW",12e4,"number"],["--authz-per-route","DM_AUTHZ_PER_ROUTES",[],"array","--authz-per-routes"],["--authz-unresolved-user","DM_AUTHZ_UNRESOLVED_USER","deny"],["--authz-identity","DM_AUTHZ_IDENTITY","req.user"],["--authz-for","DM_AUTHZ_FOR",[],"array"],["--authz-combine","DM_AUTHZ_COMBINE",!1,"boolean"],["--authz-scope-source","DM_AUTHZ_SCOPE_SOURCE",""],["--authz-filter-attached-entries","DM_AUTHZ_FILTER_ATTACHED_ENTRIES",!1,"boolean"],["--authz-transit-branch","DM_AUTHZ_TRANSIT_BRANCH",""],["--authz-per-branch-config","DM_AUTHZ_PER_BRANCH_CONFIG",{default:{read:!0,write:!1,delete:!1}},"json"],["--authz-per-branch-cache-ttl","DM_AUTHZ_PER_BRANCH_CACHE_TTL",60,"number"],["--authz-dynamic-bypass","DM_AUTHZ_DYNAMIC_BYPASS",[],"array"],["--authz-dynamic-base","DM_AUTHZ_DYNAMIC_BASE",""],["--authz-dynamic-cache-ttl","DM_AUTHZ_DYNAMIC_CACHE_TTL",60,"number"],["--authz-dynamic-token-attribute","DM_AUTHZ_DYNAMIC_TOKEN_ATTRIBUTE","userPassword"],["--authz-dynamic-config-attribute","DM_AUTHZ_DYNAMIC_CONFIG_ATTRIBUTE","description"],["--authz-dynamic-tenant-attribute","DM_AUTHZ_DYNAMIC_TENANT_ATTRIBUTE","cn"],["--authz-dynamic-reload-endpoint","DM_AUTHZ_DYNAMIC_RELOAD_ENDPOINT",!1,"boolean"],["--authz-local-admin-attribute","DM_AUTHZ_LOCAL_ADMIN_ATTRIBUTE","twakeLocalAdminLink"],["--oidc-server","DM_OIDC_SERVER",""],["--oidc-username-claim","DM_OIDC_USERNAME_CLAIM","sub"],["--oidc-client-id","DM_OIDC_CLIENT_ID",""],["--oidc-client-secret","DM_OIDC_CLIENT_SECRET",""],["--base-url","DM_BASE_URL",""],["--rate-limit-window-ms","DM_RATE_LIMIT_WINDOW_MS",9e5,"number"],["--rate-limit-max","DM_RATE_LIMIT_MAX",100,"number"],["--crowdsec-url","DM_CROWDSEC_URL","http://localhost:8080/v1/decisions"],["--crowdsec-api-key","DM_CROWDSEC_API_KEY",""],["--crowdsec-cache-ttl","DM_CROWDSEC_CACHE_TTL",60,"number"],["--trusted-proxy","DM_TRUSTED_PROXIES",[],"array","--trusted-proxies"],["--trusted-proxy-auth-header","DM_TRUSTED_PROXY_AUTH_HEADER","Auth-User"],["--ppolicy-default-dn","DM_PPOLICY_DEFAULT_DN",""],["--ppolicy-warn-days","DM_PPOLICY_WARN_DAYS",14,"number"],["--ppolicy-validate-complexity","DM_PPOLICY_VALIDATE_COMPLEXITY",!1,"boolean"],["--ppolicy-min-length","DM_PPOLICY_MIN_LENGTH",12,"number"],["--ppolicy-require-uppercase","DM_PPOLICY_REQUIRE_UPPERCASE",!0,"boolean"],["--ppolicy-require-lowercase","DM_PPOLICY_REQUIRE_LOWERCASE",!0,"boolean"],["--ppolicy-require-digit","DM_PPOLICY_REQUIRE_DIGIT",!0,"boolean"],["--ppolicy-require-special","DM_PPOLICY_REQUIRE_SPECIAL",!0,"boolean"],["--ldap-users-base","DM_LDAP_USERS_BASE",""],["--scim-prefix","DM_SCIM_PREFIX","/scim/v2"],["--scim-user-base","DM_SCIM_USER_BASE",""],["--scim-group-base","DM_SCIM_GROUP_BASE",""],["--scim-user-base-template","DM_SCIM_USER_BASE_TEMPLATE",""],["--scim-group-base-template","DM_SCIM_GROUP_BASE_TEMPLATE",""],["--scim-base-map","DM_SCIM_BASE_MAP",""],["--scim-user-base-header","DM_SCIM_USER_BASE_HEADER",""],["--scim-group-base-header","DM_SCIM_GROUP_BASE_HEADER",""],["--scim-base-header-root","DM_SCIM_BASE_HEADER_ROOT",""],["--scim-user-object-class","DM_SCIM_USER_OBJECT_CLASSES",["top","inetOrgPerson","organizationalPerson","person"],"array","--scim-user-object-classes"],["--scim-user-rdn-attribute","DM_SCIM_USER_RDN_ATTRIBUTE","uid"],["--scim-user-lock-attribute","DM_SCIM_USER_LOCK_ATTRIBUTE","pwdAccountLockedTime"],["--scim-user-lock-value","DM_SCIM_USER_LOCK_VALUE",""],["--scim-group-object-class","DM_SCIM_GROUP_OBJECT_CLASSES",["top","groupOfNames"],"array","--scim-group-object-classes"],["--scim-group-rdn-attribute","DM_SCIM_GROUP_RDN_ATTRIBUTE","cn"],["--scim-group-external-id-attribute","DM_SCIM_GROUP_EXTERNAL_ID_ATTRIBUTE",""],["--scim-id-attribute","DM_SCIM_ID_ATTRIBUTE","rdn"],["--scim-user-mapping","DM_SCIM_USER_MAPPING",""],["--scim-group-mapping","DM_SCIM_GROUP_MAPPING",""],["--scim-max-results","DM_SCIM_MAX_RESULTS",200,"number"],["--scim-max-scanned","DM_SCIM_MAX_SCANNED",1e4,"number"],["--scim-bulk-max-operations","DM_SCIM_BULK_MAX_OPERATIONS",100,"number"],["--scim-bulk-max-payload-size","DM_SCIM_BULK_MAX_PAYLOAD_SIZE",1048576,"number"],["--scim-etag","DM_SCIM_ETAG",!1,"boolean"],["--scim-base-url","DM_SCIM_BASE_URL",""]];export{t as default};
|
|
1
|
+
import{fileURLToPath as e}from"url";import{join as _,dirname as a}from"path";const t=[["--port","DM_PORT",8081,"number"],["--plugin","DM_PLUGINS",[],"array","--plugins","identifiers"],["--log-level","DM_LOG_LEVEL","notice"],["--logger","DM_LOGGER","console"],["--api-prefix","DM_API_PREFIX","/api"],["--mail-domain","DM_MAIL_DOMAIN",[],"array","--mail-domains","identifiers"],["--ldap-base","DM_LDAP_BASE",""],["--ldap-dn","DM_LDAP_DN","cn=admin,dc=example,dc=com"],["--ldap-pwd","DM_LDAP_PWD","admin"],["--ldap-url","DM_LDAP_URL",["ldap://localhost"],"array","--ldap-urls","identifiers",!0],["--ldap-user-main-attribute","DM_LDAP_USER_ATTRIBUTE","uid"],["--ldap-cache-max","DM_LDAP_CACHE_MAX",1e3,"number"],["--ldap-cache-ttl","DM_LDAP_CACHE_TTL",0,"number"],["--ldap-pool-size","DM_LDAP_POOL_SIZE",5,"number"],["--ldap-connection-ttl","DM_LDAP_CONNECTION_TTL",60,"number"],["--schemas-path","DM_SCHEMAS_PATH",_(a(e(import.meta.url)),"..","..","static","schemas")],["--mail-attribute","DM_MAIL_ATTRIBUTE","mail"],["--quota-attribute","DM_QUOTA_ATTRIBUTE","mailQuota"],["--delegation-attribute","DM_DELEGATION_ATTRIBUTE","twakeDelegatedUsers"],["--alias-attribute","DM_ALIAS_ATTRIBUTE","mailAlternateAddress"],["--forward-attribute","DM_FORWARD_ATTRIBUTE","mailForwardingAddress"],["--display-name-attribute","DM_DISPLAY_NAME_ATTRIBUTE","displayName"],["--drive-quota-attribute","DM_DRIVE_QUOTA_ATTRIBUTE","twakeDriveQuota"],["--user-class","DM_USER_CLASSES",["top","twakeAccount","twakeWhitePages"],"array","--user-classes","identifiers"],["--ldap-top-organization","DM_LDAP_TOP_ORGANIZATION",""],["--ldap-organization-class","DM_LDAP_ORGANIZATION_CLASSES",["top","organizationalUnit","twakeDepartment"],"array","--ldap-organization-classes","identifiers"],["--ldap-organization-link-attribute","DM_LDAP_ORGANIZATION_LINK_ATTRIBUTE","twakeDepartmentLink"],["--ldap-organization-path-attribute","DM_LDAP_ORGANIZATION_PATH_ATTRIBUTE","twakeDepartmentPath"],["--ldap-organization-path-separator","DM_LDAP_ORGANIZATION_PATH_SEPARATOR"," / "],["--ldap-organization-max-subnodes","DM_LDAP_ORGANIZATION_MAX_SUBNODES",50,"number"],["--organization-schema","DM_ORGANIZATION_SCHEMA",""],["--enterprise-domain-name-attribute","DM_ENTERPRISE_DOMAIN_NAME_ATTRIBUTE","associatedDomain"],["--enterprise-domain-link-attribute","DM_ENTERPRISE_DOMAIN_LINK_ATTRIBUTE",""],["--storage-backend","DM_STORAGE_BACKEND",""],["--storage-sweep-interval","DM_STORAGE_SWEEP_INTERVAL",600,"number"],["--storage-ldap-base","DM_STORAGE_LDAP_BASE",""],["--storage-ldap-object-class","DM_STORAGE_LDAP_OBJECT_CLASS","applicationProcess"],["--storage-file-directory","DM_STORAGE_FILE_DIRECTORY",""],["--storage-postgres-url","DM_STORAGE_POSTGRES_URL",""],["--storage-postgres-table","DM_STORAGE_POSTGRES_TABLE","ldap_rest_storage"],["--storage-valkey-url","DM_STORAGE_VALKEY_URL",""],["--storage-valkey-prefix","DM_STORAGE_VALKEY_PREFIX","ldap-rest:"],["--bcl-retention","DM_BCL_RETENTION",604800,"number"],["--ldap-group-base","DM_LDAP_GROUP_BASE",""],["--ldap-groups-main-attribute","DM_LDAP_GROUPS_MAIN_ATTRIBUTE","cn"],["--group-class","DM_GROUP_CLASSES",["top","groupOfNames"],"array","--group-classes","identifiers"],["--group-allow-unexistent-members","DM_ALLOW_UNEXISTENT_MEMBERS",!1,"boolean"],["--group-default-attributes","DM_GROUP_DEFAULT_ATTRIBUTES",{},"json"],["--group-dummy-user","DM_GROUP_DUMMY_USER","cn=fakeuser"],["--group-schema","DM_GROUP_SCHEMA",_(a(e(import.meta.url)),"..","..","static","schemas","twake","groups.json")],["--external-members-branch","DM_EXTERNAL_MEMBERS_BRANCH","ou=contacts,dc=example,dc=com"],["--external-branch-class","DM_EXTERNAL_BRANCH_CLASSES",["top","inetOrgPerson"],"array","--external-branch-classes","identifiers"],["--static-path","DM_STATIC_PATH",_(a(e(import.meta.url)),"..","..","static")],["--static-name","DM_STATIC_NAME","static"],["--ldap-flat-schema","DM_LDAP_FLAT_SCHEMA",[],"array","--ldap-flat-schemas"],["--ldap-flat-auto-repair","DM_LDAP_FLAT_AUTO_REPAIR",!0,"boolean"],["--ldap-raw-base","DM_LDAP_RAW_BASE",[],"array","--ldap-raw-bases","dns"],["--ldap-raw-hidden-attribute","DM_LDAP_RAW_HIDDEN_ATTRIBUTES",[],"array","--ldap-raw-hidden-attributes","identifiers"],["--ldap-raw-show-secrets","DM_LDAP_RAW_SHOW_SECRETS",!1,"boolean"],["--ldap-raw-max-results","DM_LDAP_RAW_MAX_RESULTS",200,"number"],["--ldap-raw-schema-cache-ttl","DM_LDAP_RAW_SCHEMA_CACHE_TTL",3600,"number"],["--bulk-import-schemas","DM_BULK_IMPORT_SCHEMAS",""],["--bulk-import-max-file-size","DM_BULK_IMPORT_MAX_FILE_SIZE","10485760"],["--bulk-import-batch-size","DM_BULK_IMPORT_BATCH_SIZE","100"],["--james-webadmin-url","DM_JAMES_WEBADMIN_URL","http://localhost:8000"],["--james-webadmin-token","DM_JAMES_WEBADMIN_TOKEN",""],["--james-signature-template","DM_JAMES_SIGNATURE_TEMPLATE",""],["--ldap-concurrency","DM_LDAP_CONCURRENCY",10,"number"],["--james-concurrency","DM_JAMES_CONCURRENCY",10,"number"],["--james-init-delay","DM_JAMES_INIT_DELAY",1e3,"number"],["--james-mailing-list-branch","DM_JAMES_MAILING_LIST_BRANCHES",[],"array","--james-mailing-list-branches","dns"],["--james-mailbox-type-attribute","DM_JAMES_MAILBOX_TYPE_ATTRIBUTE","twakeMailboxType"],["--twake-drive-webadmin-url","DM_TWAKE_DRIVE_WEBADMIN_URL",""],["--twake-drive-webadmin-token","DM_TWAKE_DRIVE_WEBADMIN_TOKEN",""],["--twake-drive-concurrency","DM_TWAKE_DRIVE_CONCURRENCY",10,"number"],["--twake-drive-domain-attribute","DM_TWAKE_DRIVE_DOMAIN_ATTRIBUTE","twakeCozyDomain"],["--twake-drive-default-domain-template","DM_TWAKE_DRIVE_DEFAULT_DOMAIN_TEMPLATE",""],["--calendar-webadmin-url","DM_CALENDAR_WEBADMIN_URL","http://localhost:8080"],["--calendar-webadmin-token","DM_CALENDAR_WEBADMIN_TOKEN",""],["--calendar-concurrency","DM_CALENDAR_CONCURRENCY",10,"number"],["--calendar-resource-base","DM_CALENDAR_RESOURCE_BASE",""],["--calendar-resource-objectclass","DM_CALENDAR_RESOURCE_OBJECTCLASS",""],["--calendar-resource-creator","DM_CALENDAR_RESOURCE_CREATOR",""],["--calendar-resource-domain","DM_CALENDAR_RESOURCE_DOMAIN",""],["--calendar-firstname-attribute","DM_CALENDAR_FIRSTNAME_ATTRIBUTE","givenName"],["--calendar-lastname-attribute","DM_CALENDAR_LASTNAME_ATTRIBUTE","sn"],["--cozy-admin-url","DM_COZY_ADMIN_URL",""],["--cozy-admin-user","DM_COZY_ADMIN_USER","admin"],["--cozy-admin-passphrase","DM_COZY_ADMIN_PASSPHRASE",""],["--cozy-org-id","DM_COZY_ORG_ID",""],["--cozy-org-domain","DM_COZY_ORG_DOMAIN",""],["--cozy-default-locale","DM_COZY_DEFAULT_LOCALE","fr"],["--cozy-context-name","DM_COZY_CONTEXT_NAME","default"],["--cozy-apps","DM_COZY_APPS","home,drive,settings,notes,dataproxy"],["--cozy-auth-exchange","DM_COZY_AUTH_EXCHANGE","auth"],["--cozy-b2b-exchange","DM_COZY_B2B_EXCHANGE","b2b"],["--cozy-user-created-routing-key","DM_COZY_USER_CREATED_ROUTING_KEY","user.created"],["--cozy-user-deleted-routing-key","DM_COZY_USER_DELETED_ROUTING_KEY","domain.user.deleted"],["--cloudery-manager-url","DM_CLOUDERY_MANAGER_URL",""],["--cloudery-manager-token","DM_CLOUDERY_MANAGER_TOKEN",""],["--cloudery-offer","DM_CLOUDERY_OFFER","b2b_twake_default"],["--cloudery-domain","DM_CLOUDERY_DOMAIN",""],["--cloudery-user-branch","DM_CLOUDERY_USER_BRANCH",""],["--cloudery-org-id-header","DM_CLOUDERY_ORG_ID_HEADER","x-cloudery-org-id"],["--cloudery-org-role-header","DM_CLOUDERY_ORG_ROLE_HEADER","x-cloudery-org-role"],["--cloudery-default-org-role","DM_CLOUDERY_DEFAULT_ORG_ROLE","member"],["--cloudery-fqdn-attribute","DM_CLOUDERY_FQDN_ATTRIBUTE","twakeWorkspaceUrl"],["--cloudery-org-id-attribute","DM_CLOUDERY_ORG_ID_ATTRIBUTE","twakeOrganizationId"],["--cloudery-org-role-attribute","DM_CLOUDERY_ORG_ROLE_ATTRIBUTE","twakeOrganizationRole"],["--cloudery-phones-attribute","DM_CLOUDERY_PHONES_ATTRIBUTE","twakePhones"],["--cloudery-invited-attribute","DM_CLOUDERY_INVITED_ATTRIBUTE","twakeInvited"],["--cloudery-default-locale","DM_CLOUDERY_DEFAULT_LOCALE","en"],["--cloudery-workflow-poll-interval-ms","DM_CLOUDERY_WORKFLOW_POLL_INTERVAL_MS",2e3,"number"],["--cloudery-workflow-max-attempts","DM_CLOUDERY_WORKFLOW_MAX_ATTEMPTS",60,"number"],["--twake-lifecycle-role-attribute","DM_TWAKE_LIFECYCLE_ROLE_ATTRIBUTE",""],["--twake-lifecycle-lock-attribute","DM_TWAKE_LIFECYCLE_LOCK_ATTRIBUTE",""],["--twake-lifecycle-lock-value","DM_TWAKE_LIFECYCLE_LOCK_VALUE",""],["--twake-lifecycle-deleted-attribute","DM_TWAKE_LIFECYCLE_DELETED_ATTRIBUTE",""],["--twake-lifecycle-deleted-value","DM_TWAKE_LIFECYCLE_DELETED_VALUE","TRUE"],["--twake-lifecycle-deleted-at-attribute","DM_TWAKE_LIFECYCLE_DELETED_AT_ATTRIBUTE",""],["--twake-lifecycle-deleted-at-format","DM_TWAKE_LIFECYCLE_DELETED_AT_FORMAT","iso8601"],["--twake-lifecycle-reason-attribute","DM_TWAKE_LIFECYCLE_REASON_ATTRIBUTE",""],["--twake-lifecycle-rules","DM_TWAKE_LIFECYCLE_RULES",""],["--twake-lifecycle-member-attribute","DM_TWAKE_LIFECYCLE_MEMBER_ATTRIBUTE","member"],["--twake-group-base","DM_TWAKE_GROUP_BASE",""],["--twake-group-user-base","DM_TWAKE_GROUP_USER_BASE",""],["--twake-group-organization-dn","DM_TWAKE_GROUP_ORGANIZATION_DN",""],["--twake-group-organization-status-attribute","DM_TWAKE_GROUP_ORGANIZATION_STATUS_ATTRIBUTE","twakeOrgStatus"],["--twake-group-organization-deleted-value","DM_TWAKE_GROUP_ORGANIZATION_DELETED_VALUE","deleted"],["--twake-group-display-name-attribute","DM_TWAKE_GROUP_DISPLAY_NAME_ATTRIBUTE","twakeDisplayName"],["--twake-group-color-attribute","DM_TWAKE_GROUP_COLOR_ATTRIBUTE","twakeGroupColor"],["--twake-group-created-at-attribute","DM_TWAKE_GROUP_CREATED_AT_ATTRIBUTE","twakeCreatedAt"],["--twake-group-max-page-limit","DM_TWAKE_GROUP_MAX_PAGE_LIMIT",1e3,"number"],["--twake-group-member-fields","DM_TWAKE_GROUP_MEMBER_FIELDS",{},"json"],["--twake-space-base","DM_TWAKE_SPACE_BASE",""],["--twake-space-class","DM_TWAKE_SPACE_CLASS",["top","twakeSpace"],"array",null,"identifiers"],["--twake-space-display-name-attribute","DM_TWAKE_SPACE_DISPLAY_NAME_ATTRIBUTE","twakeDisplayName"],["--twake-space-admin-attribute","DM_TWAKE_SPACE_ADMIN_ATTRIBUTE","twakeSpaceAdmin"],["--twake-space-editor-attribute","DM_TWAKE_SPACE_EDITOR_ATTRIBUTE","twakeSpaceEditor"],["--twake-space-viewer-attribute","DM_TWAKE_SPACE_VIEWER_ATTRIBUTE","twakeSpaceViewer"],["--twake-space-user-role-attribute","DM_TWAKE_SPACE_USER_ROLE_ATTRIBUTE",""],["--twake-space-exchange","DM_TWAKE_SPACE_EXCHANGE","space"],["--twake-tombstone-dn","DM_TWAKE_TOMBSTONE_DN",[],"array",null,"dns"],["--twake-tombstone-default-reason","DM_TWAKE_TOMBSTONE_DEFAULT_REASON","deleted"],["--twake-tombstone-reason-header","DM_TWAKE_TOMBSTONE_REASON_HEADER","x-deletion-reason"],["--twake-tombstone-reasons","DM_TWAKE_TOMBSTONE_REASONS",[],"array",null,"identifiers"],["--twake-tombstone-clear-attributes","DM_TWAKE_TOMBSTONE_CLEAR_ATTRIBUTES",[],"array",null,"identifiers"],["--twake-tombstone-erase-min-age","DM_TWAKE_TOMBSTONE_ERASE_MIN_AGE",2592e3,"number"],["--twake-tombstone-group-bases","DM_TWAKE_TOMBSTONE_GROUP_BASES",[],"array",null,"dns"],["--twake-instance-dn","DM_TWAKE_INSTANCE_DN",[],"array",null,"dns"],["--twake-instance-skip-attribute","DM_TWAKE_INSTANCE_SKIP_ATTRIBUTE",""],["--twake-instance-skip-value","DM_TWAKE_INSTANCE_SKIP_VALUE",""],["--twake-instance-provider","DM_TWAKE_INSTANCE_PROVIDER","cloudery"],["--twake-instance-id","DM_TWAKE_INSTANCE_ID","{uid}"],["--twake-instance-fqdn-attribute","DM_TWAKE_INSTANCE_FQDN_ATTRIBUTE","twakeWorkspaceUrl"],["--twake-instance-sent-attribute","DM_TWAKE_INSTANCE_SENT_ATTRIBUTE","twakeCreatedEventAt"],["--twake-instance-locale","DM_TWAKE_INSTANCE_LOCALE",""],["--twake-instance-timeout","DM_TWAKE_INSTANCE_TIMEOUT",3e4,"number"],["--twake-instance-cloudery-url","DM_TWAKE_INSTANCE_CLOUDERY_URL",""],["--twake-instance-cloudery-token","DM_TWAKE_INSTANCE_CLOUDERY_TOKEN",""],["--twake-instance-cloudery-offer","DM_TWAKE_INSTANCE_CLOUDERY_OFFER","b2b_twake_default"],["--twake-instance-cloudery-domain","DM_TWAKE_INSTANCE_CLOUDERY_DOMAIN",""],["--twake-instance-cloudery-organization-offer","DM_TWAKE_INSTANCE_CLOUDERY_ORGANIZATION_OFFER",""],["--twake-instance-cozy-url","DM_TWAKE_INSTANCE_COZY_URL",""],["--twake-instance-cozy-user","DM_TWAKE_INSTANCE_COZY_USER","admin"],["--twake-instance-cozy-passphrase","DM_TWAKE_INSTANCE_COZY_PASSPHRASE",""],["--twake-instance-cozy-domain","DM_TWAKE_INSTANCE_COZY_DOMAIN",""],["--twake-instance-cozy-context","DM_TWAKE_INSTANCE_COZY_CONTEXT","default"],["--twake-instance-cozy-apps","DM_TWAKE_INSTANCE_COZY_APPS","home,drive,settings,notes,dataproxy"],["--twake-instance-cozy-org-id","DM_TWAKE_INSTANCE_COZY_ORG_ID",""],["--twake-instance-cozy-org-domain","DM_TWAKE_INSTANCE_COZY_ORG_DOMAIN",""],["--twake-instance-auth-exchange","DM_TWAKE_INSTANCE_AUTH_EXCHANGE","auth"],["--twake-instance-b2b-exchange","DM_TWAKE_INSTANCE_B2B_EXCHANGE","b2b"],["--twake-instance-user-created-key","DM_TWAKE_INSTANCE_USER_CREATED_KEY","user.created"],["--twake-instance-organization-created-key","DM_TWAKE_INSTANCE_ORGANIZATION_CREATED_KEY","organization.created"],["--twake-instance-workplace-created-key","DM_TWAKE_INSTANCE_WORKPLACE_CREATED_KEY","workplace.created"],["--twake-instance-queue","DM_TWAKE_INSTANCE_QUEUE","workplace.created.ldap-rest"],["--twake-instance-organization-base","DM_TWAKE_INSTANCE_ORGANIZATION_BASE",""],["--twake-instance-organization-id-attribute","DM_TWAKE_INSTANCE_ORGANIZATION_ID_ATTRIBUTE","ou"],["--twake-instance-organization-domain-attribute","DM_TWAKE_INSTANCE_ORGANIZATION_DOMAIN_ATTRIBUTE","twakeDomain"],["--twake-instance-organization-name-attribute","DM_TWAKE_INSTANCE_ORGANIZATION_NAME_ATTRIBUTE","description"],["--twake-instance-organization-fqdn-attribute","DM_TWAKE_INSTANCE_ORGANIZATION_FQDN_ATTRIBUTE",""],["--rabbitmq-url","DM_RABBITMQ_URL",""],["--applicative-account-base","DM_APPLICATIVE_ACCOUNT_BASE",""],["--max-app-accounts","DM_MAX_APP_ACCOUNTS",5,"number"],["--applicative-account-attribute","DM_APPLICATIVE_ACCOUNT_ATTRIBUTES",["objectClass","cn","sn","givenName","displayName","description"],"array","--applicative-account-attributes","identifiers"],["--ldap-operational-attribute","DM_LDAP_OPERATIONAL_ATTRIBUTES",["dn","controls","structuralObjectClass","entryUUID","entryDN","subschemaSubentry","modifyTimestamp","modifiersName","createTimestamp","creatorsName","userPassword"],"array","--ldap-operational-attributes","identifiers"],["--trash-base","DM_TRASH_BASE",""],["--trash-watched-bases","DM_TRASH_WATCHED_BASES",""],["--trash-add-metadata","DM_TRASH_ADD_METADATA","true"],["--trash-auto-create","DM_TRASH_AUTO_CREATE","true"],["--llng-ini","DM_LLNG_INI","/etc/lemonldap-ng/lemonldap-ng.ini"],["--llng-username-header","DM_LLNG_USERNAME_HEADER",""],["--auth-path-prefix","DM_AUTH_PATH_PREFIX",[],"array","--auth-path-prefixes"],["--auth-token","DM_AUTH_TOKENS",[],"array","--auth-tokens"],["--auth-fake-user","DM_AUTH_FAKE_USER",""],["--auth-totp","DM_AUTH_TOTP",[],"array","--auth-totps","phrases"],["--auth-totp-window","DM_AUTH_TOTP_WINDOW",1,"number"],["--auth-totp-step","DM_AUTH_TOTP_STEP",30,"number"],["--auth-hmac","DM_AUTH_HMAC",[],"array","--auth-hmacs","phrases"],["--auth-hmac-window","DM_AUTH_HMAC_WINDOW",12e4,"number"],["--authz-per-route","DM_AUTHZ_PER_ROUTES",[],"array","--authz-per-routes"],["--authz-unresolved-user","DM_AUTHZ_UNRESOLVED_USER","deny"],["--authz-identity","DM_AUTHZ_IDENTITY","req.user"],["--authz-for","DM_AUTHZ_FOR",[],"array",null,"identifiers"],["--authz-combine","DM_AUTHZ_COMBINE",!1,"boolean"],["--authz-scope-source","DM_AUTHZ_SCOPE_SOURCE",""],["--authz-filter-attached-entries","DM_AUTHZ_FILTER_ATTACHED_ENTRIES",!1,"boolean"],["--authz-transit-branch","DM_AUTHZ_TRANSIT_BRANCH",""],["--authz-per-branch-config","DM_AUTHZ_PER_BRANCH_CONFIG",{default:{read:!0,write:!1,delete:!1}},"json"],["--authz-per-branch-cache-ttl","DM_AUTHZ_PER_BRANCH_CACHE_TTL",60,"number"],["--authz-dynamic-bypass","DM_AUTHZ_DYNAMIC_BYPASS",[],"array",null,"identifiers"],["--authz-dynamic-base","DM_AUTHZ_DYNAMIC_BASE",""],["--authz-dynamic-cache-ttl","DM_AUTHZ_DYNAMIC_CACHE_TTL",60,"number"],["--authz-dynamic-token-attribute","DM_AUTHZ_DYNAMIC_TOKEN_ATTRIBUTE","userPassword"],["--authz-dynamic-config-attribute","DM_AUTHZ_DYNAMIC_CONFIG_ATTRIBUTE","description"],["--authz-dynamic-tenant-attribute","DM_AUTHZ_DYNAMIC_TENANT_ATTRIBUTE","cn"],["--authz-dynamic-reload-endpoint","DM_AUTHZ_DYNAMIC_RELOAD_ENDPOINT",!1,"boolean"],["--authz-local-admin-attribute","DM_AUTHZ_LOCAL_ADMIN_ATTRIBUTE","twakeLocalAdminLink"],["--oidc-server","DM_OIDC_SERVER",""],["--oidc-username-claim","DM_OIDC_USERNAME_CLAIM","sub"],["--oidc-client-id","DM_OIDC_CLIENT_ID",""],["--oidc-client-secret","DM_OIDC_CLIENT_SECRET",""],["--base-url","DM_BASE_URL",""],["--rate-limit-window-ms","DM_RATE_LIMIT_WINDOW_MS",9e5,"number"],["--rate-limit-max","DM_RATE_LIMIT_MAX",100,"number"],["--crowdsec-url","DM_CROWDSEC_URL","http://localhost:8080/v1/decisions"],["--crowdsec-api-key","DM_CROWDSEC_API_KEY",""],["--crowdsec-cache-ttl","DM_CROWDSEC_CACHE_TTL",60,"number"],["--trusted-proxy","DM_TRUSTED_PROXIES",[],"array","--trusted-proxies","identifiers"],["--trusted-proxy-auth-header","DM_TRUSTED_PROXY_AUTH_HEADER","Auth-User"],["--ppolicy-default-dn","DM_PPOLICY_DEFAULT_DN",""],["--ppolicy-warn-days","DM_PPOLICY_WARN_DAYS",14,"number"],["--ppolicy-validate-complexity","DM_PPOLICY_VALIDATE_COMPLEXITY",!1,"boolean"],["--ppolicy-min-length","DM_PPOLICY_MIN_LENGTH",12,"number"],["--ppolicy-require-uppercase","DM_PPOLICY_REQUIRE_UPPERCASE",!0,"boolean"],["--ppolicy-require-lowercase","DM_PPOLICY_REQUIRE_LOWERCASE",!0,"boolean"],["--ppolicy-require-digit","DM_PPOLICY_REQUIRE_DIGIT",!0,"boolean"],["--ppolicy-require-special","DM_PPOLICY_REQUIRE_SPECIAL",!0,"boolean"],["--ldap-users-base","DM_LDAP_USERS_BASE",""],["--scim-prefix","DM_SCIM_PREFIX","/scim/v2"],["--scim-user-base","DM_SCIM_USER_BASE",""],["--scim-group-base","DM_SCIM_GROUP_BASE",""],["--scim-user-base-template","DM_SCIM_USER_BASE_TEMPLATE",""],["--scim-group-base-template","DM_SCIM_GROUP_BASE_TEMPLATE",""],["--scim-base-map","DM_SCIM_BASE_MAP",""],["--scim-user-base-header","DM_SCIM_USER_BASE_HEADER",""],["--scim-group-base-header","DM_SCIM_GROUP_BASE_HEADER",""],["--scim-base-header-root","DM_SCIM_BASE_HEADER_ROOT",""],["--scim-user-object-class","DM_SCIM_USER_OBJECT_CLASSES",["top","inetOrgPerson","organizationalPerson","person"],"array","--scim-user-object-classes","identifiers"],["--scim-user-rdn-attribute","DM_SCIM_USER_RDN_ATTRIBUTE","uid"],["--scim-user-lock-attribute","DM_SCIM_USER_LOCK_ATTRIBUTE","pwdAccountLockedTime"],["--scim-user-lock-value","DM_SCIM_USER_LOCK_VALUE",""],["--scim-group-object-class","DM_SCIM_GROUP_OBJECT_CLASSES",["top","groupOfNames"],"array","--scim-group-object-classes","identifiers"],["--scim-group-rdn-attribute","DM_SCIM_GROUP_RDN_ATTRIBUTE","cn"],["--scim-group-external-id-attribute","DM_SCIM_GROUP_EXTERNAL_ID_ATTRIBUTE",""],["--scim-id-attribute","DM_SCIM_ID_ATTRIBUTE","rdn"],["--scim-user-mapping","DM_SCIM_USER_MAPPING",""],["--scim-group-mapping","DM_SCIM_GROUP_MAPPING",""],["--scim-max-results","DM_SCIM_MAX_RESULTS",200,"number"],["--scim-max-scanned","DM_SCIM_MAX_SCANNED",1e4,"number"],["--scim-bulk-max-operations","DM_SCIM_BULK_MAX_OPERATIONS",100,"number"],["--scim-bulk-max-payload-size","DM_SCIM_BULK_MAX_PAYLOAD_SIZE",1048576,"number"],["--scim-etag","DM_SCIM_ETAG",!1,"boolean"],["--scim-base-url","DM_SCIM_BASE_URL",""]];export{t as default};
|
package/dist/lib/errors.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class HttpError extends Error{constructor(r,
|
|
1
|
+
class HttpError extends Error{constructor(r,o=500){super(r),this.statusCode=o,this.name="HttpError"}}class BadRequestError extends HttpError{constructor(r="Bad request"){super(r,400),this.name="BadRequestError"}}class UnauthorizedError extends HttpError{constructor(r="Unauthorized"){super(r,401),this.name="UnauthorizedError"}}const r="Token does not have permission on this branch";class ForbiddenError extends HttpError{constructor(r="Forbidden"){super(r,403),this.name="ForbiddenError"}}class AmbiguousIdentityError extends ForbiddenError{constructor(r,o){super(`[authz-forbidden] ${r} names ${o} directory entries, so no single one can be taken for the caller`),this.identity=r,this.count=o,this.name="AmbiguousIdentityError"}}class NotFoundError extends HttpError{constructor(r="Not found"){super(r,404),this.name="NotFoundError"}}class ConflictError extends HttpError{constructor(r="Conflict"){super(r,409),this.name="ConflictError"}}class UriTooLongError extends HttpError{constructor(r="URI Too Long"){super(r,414),this.name="UriTooLongError"}}class UnprocessableEntityError extends HttpError{constructor(r="Unprocessable Entity"){super(r,422),this.name="UnprocessableEntityError"}}class TooManyRequestsError extends HttpError{constructor(r="Too Many Requests"){super(r,429),this.name="TooManyRequestsError"}}class BadGatewayError extends HttpError{constructor(r="Bad Gateway"){super(r,502),this.name="BadGatewayError"}}class ServiceUnavailableError extends HttpError{constructor(r="Service Unavailable"){super(r,503),this.name="ServiceUnavailableError"}}class GatewayTimeoutError extends HttpError{constructor(r="Gateway Timeout"){super(r,504),this.name="GatewayTimeoutError"}}export{r as AUTHZ_REFUSED,AmbiguousIdentityError,BadGatewayError,BadRequestError,ConflictError,ForbiddenError,GatewayTimeoutError,HttpError,NotFoundError,ServiceUnavailableError,TooManyRequestsError,UnauthorizedError,UnprocessableEntityError,UriTooLongError};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{AUTHZ_REFUSED as r}from"./errors.js";let t;const setLogger=r=>{t=r},getLogger=()=>t,tryMethod=async(r,t,...e)=>{try{return await t(...e),ok(r)}catch(t){return serverError(r,t)}},tryMethodData=async(r,t,...e)=>{try{const n=await t(...e);return ok(r,n)}catch(t){return serverError(r,t)}},_rejectResponse=(r,e,n)=>{t?.warn(`Client error ${r}: ${n}`),e.status(r).json({error:n})},ok=(r,t={success:!0})=>{r.status(200).json(t)},created=(r,t)=>{r.status(201).json(t)},multiStatus=(r,t)=>{r.status(207).json(t)},badRequest=(r,t="Bad request")=>_rejectResponse(400,r,t),unauthorized=(r,t="Unauthorized")=>_rejectResponse(401,r,t),forbidden=(r,t="Forbidden")=>_rejectResponse(403,r,t),notFound=(r,t="Not found")=>_rejectResponse(404,r,t),serverError=(e,n)=>{const s=n instanceof Error?n.message:String(n);let o=n instanceof Error&&"statusCode"in n?n.statusCode:500;if(500===o&&/\[authz-forbidden\]/.test(s)&&(o=403),o>=400&&o<500){const n=403===o&&/\[authz-forbidden\]/.test(s)?r:s;return t?.warn(`Client error ${o}: ${n}`),void e.status(o).json({error:n})}n instanceof Error?t?.error(n.message,n):"string"==typeof n?t?.error(n):t?.error("Server error",n),e.status(o).json({error:"check logs"})},wantJson=(r,t)=>!1!==r.accepts("json")||(badRequest(t),!1),jsonBody=(r,t,...e)=>{try{if(!wantJson(r,t))return!1;const n=r.body;if(e.length>0)for(let r=0;r<e.length;r++)if(null==n[e[r]])return badRequest(t,"Bad content"),!1;return n}catch(r){return badRequest(t,"Bad content"),!1}};export{badRequest,created,forbidden,getLogger,jsonBody,multiStatus,notFound,ok,serverError,setLogger,tryMethod,tryMethodData,unauthorized,wantJson};
|
package/dist/lib/ldapActions.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash as e}from"node:crypto";import{Client as t,Attribute as i,Change as s,Control as n}from"ldapts";import{LRUCache as a}from"lru-cache";import r from"p-limit";import{normalizeDn as o,isDnInBranch as c,launchHooksChained as l,launchHooks as h,escapeDnValue as d}from"./utils.js";import{changeContext as p}from"./changeContext.js";import{NotFoundError as f,ConflictError as u}from"./errors.js";import{SchemaIndex as g,parseSchema as y}from"./ldapSchema.js";const m={scope:"sub",filter:"(objectClass=*)",attributes:["*"],sizeLimit:0,timeLimit:10,paged:{pageSize:100}};function ldapError(e,t){const i=new Error(`${e}: ${String(t)}`),s=t?.code;return"number"==typeof s&&(i.code=s),i}const digestKey=t=>e("sha256").update(t).digest("hex").slice(0,12),foldDn=e=>e.trim().toLowerCase().split(",").map(e=>e.trim()).join(","),cloneSearchResult=e=>({searchEntries:e.searchEntries.map(e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Array.isArray(t)?[...t]:t]))),searchReferences:[...e.searchReferences]});class ldapActions{constructor(e){this.cacheGeneration=0,this.connectionPool=[],this.currentUrlIndex=0,this.waitingResolvers=[],this.availableConnections=[],this.isCleaningUp=!1,this.declaredClasses=new Map,this.schemaUnavailable=!1,this.classWarnings=new a({max:1e4}),this.parent=e,this.logger=e.logger,this.config=e.config,this.poolSize=this.config.ldap_pool_size||5,this.connectionTtl=1e3*(this.config.ldap_connection_ttl||60),this.logger.info(`LDAP connection pool initialized: size=${this.poolSize}, ttl=${this.connectionTtl/1e3}s`);const t=this.config.ldap_concurrency||10;this.queryLimit=r(t),this.logger.info(`Global LDAP query concurrency limit set to ${t}`);const i="string"==typeof this.config.ldap_cache_max?parseInt(this.config.ldap_cache_max,10)||1e3:this.config.ldap_cache_max??1e3,s=this.config.ldap_cache_ttl,n="string"==typeof s?parseInt(s,10)||0:s??0,o=1e3*Math.max(n,0);if(this.cacheEnabled=o>0,this.searchCache=new a({max:i,ttl:this.cacheEnabled?o:void 0,updateAgeOnGet:!1,updateAgeOnHas:!1}),this.logger.info(this.cacheEnabled?`LDAP search cache initialized: max=${i}, ttl=${o/1e3}s`:"LDAP search cache disabled (--ldap-cache-ttl=0)"),this.attrSignatureCache=new a({max:1e3}),!e.config.ldap_url||0===e.config.ldap_url.length)throw new Error("LDAP URL is not defined");if(!e.config.ldap_dn)throw new Error("LDAP DN is not defined");if(!e.config.ldap_pwd)throw new Error("LDAP password is not defined");if(!e.config.ldap_base)throw new Error("LDAP base is not defined, please set --ldap-base (or DM_LDAP_BASE)");this.base=e.config.ldap_base,this.ldapUrls=e.config.ldap_url,this.logger.info(`LDAP failover configured with ${this.ldapUrls.length} URL(s): ${this.ldapUrls.join(", ")}`),this.options={url:this.ldapUrls[0],timeout:0,connectTimeout:0,strictDN:!1},this.ldapUrls[0].startsWith("ldaps://")&&(this.options.tlsOptions={minVersion:"TLSv1.2"}),this.dn=e.config.ldap_dn,this.pwd=e.config.ldap_pwd}async createConnection(){const e=[];for(let i=0;i<this.ldapUrls.length;i++){const s=(this.currentUrlIndex+i)%this.ldapUrls.length,n=this.ldapUrls[s];try{this.logger.debug(`Attempting connection to ${n}`);const e={...this.options,url:n,tlsOptions:n.startsWith("ldaps://")?{minVersion:"TLSv1.2"}:void 0},i=new t(e);return await i.bind(this.dn,this.pwd),s!==this.currentUrlIndex&&(this.logger.info(`LDAP failover: switched from ${this.ldapUrls[this.currentUrlIndex]} to ${n}`),this.currentUrlIndex=s),i}catch(t){this.logger.warn(`Failed to connect to ${n}: ${String(t)}`),e.push(t)}}throw this.logger.error(`LDAP connection failed for all ${this.ldapUrls.length} URL(s)`),new Error(`LDAP connection failed for all URLs: ${e.map(e=>e.message).join(", ")}`)}cleanupExpiredConnections(){if(!this.isCleaningUp){this.isCleaningUp=!0;try{const e=Date.now(),t=[];for(let i=this.availableConnections.length-1;i>=0;i--){const s=this.availableConnections[i];if(e-s.createdAt>this.connectionTtl){t.push(s),this.availableConnections.splice(i,1);const e=this.connectionPool.indexOf(s);-1!==e&&this.connectionPool.splice(e,1)}}for(const e of t)e.client.unbind().catch(e=>{this.logger.debug(`Error unbinding expired connection: ${String(e)}`)});t.length>0&&this.logger.debug(`Cleaned up ${t.length} expired LDAP connections`)}finally{this.isCleaningUp=!1}}}async acquireConnection(){if(this.cleanupExpiredConnections(),this.availableConnections.length>0){const e=this.availableConnections.pop();return e.inUse=!0,this.logger.debug("Reusing pooled LDAP connection"),e}if(this.connectionPool.length<this.poolSize){const e={client:await this.createConnection(),createdAt:Date.now(),inUse:!0};return this.connectionPool.push(e),this.logger.debug(`Created new LDAP connection (pool: ${this.connectionPool.length}/${this.poolSize})`),e}return this.logger.debug("LDAP connection pool full, waiting for available connection"),new Promise((e,t)=>{const i=globalThis.setTimeout(()=>{const e=this.waitingResolvers.indexOf(resolveWrapper);-1!==e&&this.waitingResolvers.splice(e,1),t(new Error("LDAP connection pool timeout after 30s"))},3e4),resolveWrapper=t=>{globalThis.clearTimeout(i),e(t)};this.waitingResolvers.push(resolveWrapper)})}releaseConnection(e){if(Date.now()-e.createdAt>this.connectionTtl){const t=this.connectionPool.indexOf(e);return-1!==t&&this.connectionPool.splice(t,1),e.client.unbind().catch(()=>{}),void this.logger.debug("Released expired LDAP connection (discarded)")}if(this.waitingResolvers.length>0){const t=this.waitingResolvers.shift();return this.logger.debug("Released LDAP connection to waiting request"),void t(e)}e.inUse=!1,this.availableConnections.push(e),this.logger.debug("Released LDAP connection back to pool")}getAttributeSignature(e){if(!e||0===e.length)return"*";const t=e.join("|");let i=this.attrSignatureCache.get(t);return i||(i=[...e].sort().join(","),this.attrSignatureCache.set(t,i)),i}getCacheKey(e,t){const i=this.getAttributeSignature(t.attributes),s=t.explicitBufferAttributes?.length?[...t.explicitBufferAttributes].sort().join(","):"-",n="string"==typeof t.filter?t.filter:t.filter?t.filter.toString():"(objectClass=*)";return[e,t.scope||"sub",n,i,s,!1===t.returnAttributeValues?"novalues":"values",t.derefAliases??"-",String(t.sizeLimit??"-")].join("\0")}invalidateCache(e){if(!this.cacheEnabled)return;this.cacheGeneration++;const t=foldDn(e),i=`,${t}`,s=[];for(const e of this.searchCache.keys()){const n=foldDn(e.split("\0")[0]);(n===t||n.endsWith(i))&&s.push(e)}for(const e of s)this.searchCache.delete(e)}get system(){return this}forRequest(e){return{search:(t,i)=>this.search(t,i??this.base,e),add:(t,i)=>this.add(t,i,e),modify:(t,i,s)=>this.modify(t,i,e,s),rename:(t,i)=>this.rename(t,i,e),delete:t=>this.delete(t,e)}}declareObjectClasses(e,t,i=!1){const s=(Array.isArray(t)?t:[t]).filter(e=>"string"==typeof e&&""!==e);if(!e||0===s.length)return;const n=JSON.stringify([o(e),s.map(e=>e.toLowerCase()).sort(),i]);this.declaredClasses.set(n,{base:e,classes:s,includeBase:i})}declaredFor(e){let t=0,i=[];for(const s of this.declaredClasses.values()){if(!c(e,s.base))continue;const n=o(s.base);if(!s.includeBase&&o(e)===n)continue;const a=n.split(",").length;a>t&&(t=a,i=[]),a===t&&i.push(...s.classes)}const s=new Set;return i.filter(e=>{const t=e.toLowerCase();return!s.has(t)&&(s.add(t),!0)})}warnOnce(e){this.classWarnings.has(e)||(this.classWarnings.set(e,!0),this.logger.warn(e))}async classRepair(e,t){if(!1===this.config.ldap_flat_auto_repair)return[];const i=this.declaredFor(e);if(0===i.length)return[];const s=Array.isArray(t.delete)?t.delete:Object.entries(t.delete||{}).filter(([,e])=>!e||Array.isArray(e)&&!e.length).map(([e])=>e);if([...Object.keys(t.add||{}),...Object.keys(t.replace||{}),...Array.isArray(t.delete)?t.delete:Object.keys(t.delete||{})].some(e=>"objectclass"===e.toLowerCase()))return[];let n;try{n=await this.schemaIndex(),this.schemaUnavailable=!1}catch(e){return this.schemaUnavailable||this.logger.warn(`Object classes are not repaired: the directory schema could not be read (${String(e)})`),this.schemaUnavailable=!0,[]}const attributeKey=e=>n.getAttributeType(e)?.oid??e.toLowerCase(),allowedBy=e=>{const{must:t,may:i}=n.resolveAttributes(e);return new Set([...t,...i].map(attributeKey))},valued=e=>Object.entries(e||{}).filter(([,e])=>Array.isArray(e)?e.length>0:null!=e&&""!==e).map(([e])=>e),a=[...valued(t.add),...valued(t.replace)],r=i.filter(e=>{const t=n.getObjectClass(e);if("AUXILIARY"!==t?.kind)return!1;const i=allowedBy([e]);return a.some(e=>i.has(attributeKey(e)))});if(0===r.length)return[];let o;try{o=(await this.system.search({paged:!1,scope:"base",attributes:["objectClass",...n.resolveAttributes(r).must]},e)).searchEntries[0]}catch{return[]}if(!o)return[];const c=o.objectClass??o.objectclass,l=(Array.isArray(c)?c:c?[c]:[]).map(String),h=allowedBy(l);let d=a.filter(e=>n.getAttributeType(e)&&!h.has(attributeKey(e)));if(0===d.length)return[];const p=new Set(s.map(attributeKey)),f=new Set([...valued(o).filter(e=>!p.has(attributeKey(e))),...a].map(attributeKey)),{auxiliary:u,structural:g}=n.missingClasses(i,l),y=[];for(const t of u){const i=t.names[0]??t.oid,s=allowedBy([i]),a=d.filter(e=>s.has(attributeKey(e)));if(0===a.length)continue;const r=n.resolveAttributes([i]).must.filter(e=>!f.has(attributeKey(e)));r.length>0?this.warnOnce(`${e} needs ${i} for ${a.join(", ")}, and cannot be given it without ${r.join(", ")}`):(y.push(i),d=d.filter(e=>!a.includes(e)))}const m=g.filter(e=>{const t=allowedBy([e]);return d.some(e=>t.has(attributeKey(e)))});return m.length>0&&this.warnOnce(`${e} cannot hold ${d.join(", ")}: its schema declares ${m.join(", ")}, a structural class an existing entry cannot be given`),y.length>0&&this.logger.info(`${e}: adding ${y.join(", ")}, which the change needs and its schema declares`),y}async subschemaDn(){try{const e=await this.system.search({paged:!1,scope:"base",filter:"(objectClass=*)",attributes:["subschemaSubentry"]},""),t=e.searchEntries[0]?.subschemaSubentry,i=Array.isArray(t)?t[0]:t;if(i)return String(i)}catch(e){this.logger.warn(`Unable to read subschemaSubentry from root DSE: ${String(e)}`)}return"cn=Subschema"}async schemaIndex(){const e=1e3*(this.config.ldap_raw_schema_cache_ttl??3600);if(this.schemaCache&&Date.now()-this.schemaCache.fetchedAt<e)return this.schemaCache.index;if(this.schemaLoading)return this.schemaLoading;this.schemaLoading=(async()=>{const e=await this.subschemaDn(),t=(await this.system.search({paged:!1,scope:"base",filter:"(objectClass=*)",attributes:["objectClasses","attributeTypes","ldapSyntaxes","matchingRules"]},e).catch(t=>{if(32===t?.code)throw new f(`Subschema entry ${e} not found`);throw t})).searchEntries[0];if(!t)throw new f(`Subschema entry ${e} not found`);const list=e=>{if(void 0===e)return[];return(Array.isArray(e)?e:[e]).map(e=>Buffer.isBuffer(e)?e.toString("utf8"):e)},i=new g(y({objectClasses:list(t.objectClasses),attributeTypes:list(t.attributeTypes),ldapSyntaxes:list(t.ldapSyntaxes),matchingRules:list(t.matchingRules)}));return this.schemaCache={index:i,fetchedAt:Date.now()},this.logger.info(`LDAP schema loaded from ${e}: ${i.schema.objectClasses.length} object classes, ${i.schema.attributeTypes.length} attribute types`),i})();try{return await this.schemaLoading}finally{this.schemaLoading=void 0}}filterForCaller(e,t,i){if(!this.parent.hooks.ldapsearchfilter)return e;const s=this.parent.hooks.ldapsearchfilter,one=async e=>{const[n]=await l(s,[e,t,i]);return n??e};if("function"==typeof e[Symbol.asyncIterator]){const t=e;return async function*(){for await(const e of t)yield await one(e)}()}return one(e)}async search(e,t=this.base,i){let s={...m,...e};s=await l(this.parent.hooks.ldapsearchopts,s),[t,s]=await l(this.parent.hooks.ldapsearchrequest,[t,s,i]);const n=this.cacheEnabled&&!s.paged&&"base"===s.scope,a=n?this.getCacheKey(t,s):"",r=this.cacheGeneration;if(n){const e=this.searchCache.get(a);if(e)return this.logger.debug(`LDAP search cache hit: ${digestKey(a)}`),this.filterForCaller(cloneSearchResult(e),i,s)}const o=await this.acquireConnection();try{let e=s.paged?o.client.searchPaginated(t,s):o.client.search(t,s);if(e=await l(this.parent.hooks.ldapsearchresult,e),n){const t=e;return r===this.cacheGeneration?(this.searchCache.set(a,cloneSearchResult(t)),this.logger.debug(`LDAP search cached: ${digestKey(a)}`)):this.logger.debug(`LDAP search not cached, a write overtook it: ${digestKey(a)}`),this.filterForCaller(t,i,s)}return s.paged?this.filterForCaller(this.wrapPaginatedSearch(e,o),i,s):this.filterForCaller(e,i,s)}finally{s.paged||this.releaseConnection(o)}}async*wrapPaginatedSearch(e,t){try{for await(const t of e)yield t}finally{this.releaseConnection(t)}}async add(e,t,s){e=this.setDn(e),t.objectClass&&0!==t.objectClass.length||!this.config.user_class||(t.objectClass=this.config.user_class);const n={};for(const[e,i]of Object.entries(t))Buffer.isBuffer(i)?n[e]=i.toString():Array.isArray(i)&&i.length>0&&Buffer.isBuffer(i[0])?n[e]=i.map(e=>e.toString()):n[e]=i;[e,t]=await l(this.parent.hooks.ldapaddrequest,[e,n,s]);const a=[];for(const[e,s]of Object.entries(t)){const t=Array.isArray(s)?s:[s];a.push(new i({type:e,values:t}))}const r=await this.acquireConnection();try{this.invalidateCache(e),await r.client.add(e,a),this.invalidateCache(e),h(this.parent.hooks.ldapadddone,[e,t],p(s))}catch(t){throw function(e,t){const i=ldapError("LDAP add error",t);if(68!==i.code)return i;const s=new u(`Entry ${e} already exists`);return s.code=68,s}(e,t)}finally{this.releaseConnection(r)}return await h(this.parent.hooks.ldapaddafter,[e,t],p(s)),!0}async modify(e,t,a,r={}){e=this.setDn(e);const o=[],c=this.opNumber();if([e,t]=await l(this.parent.hooks.ldapmodifyrequest,[e,t,c,a]),t.add)for(const[e,n]of Object.entries(t.add))o.push(new s({operation:"add",modification:new i({type:e,values:Array.isArray(n)?n:[n]})}));if(t.replace)for(const[e,n]of Object.entries(t.replace))o.push(new s({operation:"replace",modification:new i({type:e,values:Array.isArray(n)?n:[n]})}));if(t.delete)if(Array.isArray(t.delete))for(const e of t.delete)e&&o.push(new s({operation:"delete",modification:new i({type:e,values:[]})}));else for(const[e,n]of Object.entries(t.delete)){const t=new s({operation:"delete",modification:new i(n?{type:e,values:Array.isArray(n)?n:[n]}:{type:e})});o.push(t)}if(0===o.length){return Object.keys(t.add||{}).length>0||Object.keys(t.replace||{}).length>0||(Array.isArray(t.delete)?t.delete.length>0:Object.keys(t.delete||{}).length>0)?this.logger.warn(`Modify on ${e} asked for changes but emitted none; nothing was written`):this.logger.debug(`Modify on ${e} had nothing to apply`),h(this.parent.hooks.ldapmodifydone,[e,{},c],r.context??p(a)),!1}{const l=await this.classRepair(e,t),d=l.length>0?new s({operation:"add",modification:new i({type:"objectClass",values:l})}):void 0;d&&o.push(d);const f=await this.acquireConnection();try{this.invalidateCache(e);const i=r.permissive?[new n("1.2.840.113556.1.4.1413",{critical:!1})]:void 0;try{await f.client.modify(e,o,i)}catch(t){if(!d||20!==t?.code)throw t;o.splice(o.indexOf(d),1),await f.client.modify(e,o,i)}return this.invalidateCache(e),h(this.parent.hooks.ldapmodifydone,[e,t,c],r.context??p(a)),!0}catch(t){throw this.logger.warn(`Changes that failed: ${e}, ${JSON.stringify(o)}`),function(e,t){const i=ldapError("LDAP modify error",t);if(65!==i.code)return i;const s=new u(`Entry ${e} cannot hold this change: ${String(t)}`);return s.code=65,s}(e,t)}finally{this.releaseConnection(f)}}}async rename(e,t,i){e=this.setDn(e),t=this.setDn(t),[e,t]=await l(this.parent.hooks.ldaprenamerequest,[e,t,i]);const s=await this.acquireConnection();try{return this.invalidateCache(e),this.invalidateCache(t),await s.client.modifyDN(e,t),this.invalidateCache(e),this.invalidateCache(t),h(this.parent.hooks.ldaprenamedone,[e,t],p(i)),!0}catch(e){throw ldapError("LDAP rename error",e)}finally{this.releaseConnection(s)}}async move(e,t,i){e=this.setDn(e),t=this.setDn(t);const s=await this.acquireConnection();try{return this.invalidateCache(e),this.invalidateCache(t),await s.client.modifyDN(e,t),this.invalidateCache(e),this.invalidateCache(t),h(this.parent.hooks.ldaprenamedone,[e,t],p(i)),this.logger.debug(`LDAP move: ${e} -> ${t}`),!0}catch(e){throw ldapError("LDAP move error",e)}finally{this.releaseConnection(s)}}async delete(e,t){e=Array.isArray(e)?e.map(e=>this.setDn(e)):this.setDn(e),Array.isArray(e)||(e=[e]),[e]=await l(this.parent?.hooks.ldapdeleterequest,[e,t]);const i=await this.acquireConnection();try{for(const s of e){try{this.invalidateCache(s),await i.client.del(s),this.invalidateCache(s)}catch(e){throw ldapError("LDAP delete error",e)}h(this.parent.hooks.ldapdeletedone,s,p(t))}return!0}finally{this.releaseConnection(i)}}setDn(e){return/=/.test(e)?/,/.test(e)||(e+=`,${this.base}`):e=`${this.config.ldap_user_main_attribute}=${d(e)},${this.base}`,e}opNumber(){return this.parent.operationSequence++}}export{ldapActions as default};
|
|
1
|
+
import{createHash as e}from"node:crypto";import{Client as t,Attribute as i,Change as s,Control as n}from"ldapts";import{LRUCache as a}from"lru-cache";import r from"p-limit";import{normalizeDn as o,isDnInBranch as c,launchHooksChained as l,launchHooks as h,escapeDnValue as d}from"./utils.js";import{changeContext as p}from"./changeContext.js";import{runOperation as u,outsideOperation as f}from"./operation.js";import{NotFoundError as g,ConflictError as y}from"./errors.js";import{SchemaIndex as m,parseSchema as b}from"./ldapSchema.js";const w={scope:"sub",filter:"(objectClass=*)",attributes:["*"],sizeLimit:0,timeLimit:10,paged:{pageSize:100}};function ldapError(e,t){const i=new Error(`${e}: ${String(t)}`),s=t?.code;return"number"==typeof s&&(i.code=s),i}const digestKey=t=>e("sha256").update(t).digest("hex").slice(0,12),foldDn=e=>e.trim().toLowerCase().split(",").map(e=>e.trim()).join(","),cloneSearchResult=e=>({searchEntries:e.searchEntries.map(e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Array.isArray(t)?[...t]:t]))),searchReferences:[...e.searchReferences]});class ldapActions{constructor(e){this.cacheGeneration=0,this.connectionPool=[],this.currentUrlIndex=0,this.waitingResolvers=[],this.availableConnections=[],this.isCleaningUp=!1,this.declaredClasses=new Map,this.schemaUnavailable=!1,this.classWarnings=new a({max:1e4}),this.parent=e,this.logger=e.logger,this.config=e.config,this.poolSize=this.config.ldap_pool_size||5,this.connectionTtl=1e3*(this.config.ldap_connection_ttl||60),this.logger.info(`LDAP connection pool initialized: size=${this.poolSize}, ttl=${this.connectionTtl/1e3}s`);const t=this.config.ldap_concurrency||10;this.queryLimit=r(t),this.logger.info(`Global LDAP query concurrency limit set to ${t}`);const i="string"==typeof this.config.ldap_cache_max?parseInt(this.config.ldap_cache_max,10)||1e3:this.config.ldap_cache_max??1e3,s=this.config.ldap_cache_ttl,n="string"==typeof s?parseInt(s,10)||0:s??0,o=1e3*Math.max(n,0);if(this.cacheEnabled=o>0,this.searchCache=new a({max:i,ttl:this.cacheEnabled?o:void 0,updateAgeOnGet:!1,updateAgeOnHas:!1}),this.logger.info(this.cacheEnabled?`LDAP search cache initialized: max=${i}, ttl=${o/1e3}s`:"LDAP search cache disabled (--ldap-cache-ttl=0)"),this.attrSignatureCache=new a({max:1e3}),!e.config.ldap_url||0===e.config.ldap_url.length)throw new Error("LDAP URL is not defined");if(!e.config.ldap_dn)throw new Error("LDAP DN is not defined");if(!e.config.ldap_pwd)throw new Error("LDAP password is not defined");if(!e.config.ldap_base)throw new Error("LDAP base is not defined, please set --ldap-base (or DM_LDAP_BASE)");this.base=e.config.ldap_base,this.ldapUrls=e.config.ldap_url,this.logger.info(`LDAP failover configured with ${this.ldapUrls.length} URL(s): ${this.ldapUrls.join(", ")}`),this.options={url:this.ldapUrls[0],timeout:0,connectTimeout:0,strictDN:!1},this.ldapUrls[0].startsWith("ldaps://")&&(this.options.tlsOptions={minVersion:"TLSv1.2"}),this.dn=e.config.ldap_dn,this.pwd=e.config.ldap_pwd}async createConnection(){const e=[];for(let i=0;i<this.ldapUrls.length;i++){const s=(this.currentUrlIndex+i)%this.ldapUrls.length,n=this.ldapUrls[s];try{this.logger.debug(`Attempting connection to ${n}`);const e={...this.options,url:n,tlsOptions:n.startsWith("ldaps://")?{minVersion:"TLSv1.2"}:void 0},i=new t(e);return await i.bind(this.dn,this.pwd),s!==this.currentUrlIndex&&(this.logger.info(`LDAP failover: switched from ${this.ldapUrls[this.currentUrlIndex]} to ${n}`),this.currentUrlIndex=s),i}catch(t){this.logger.warn(`Failed to connect to ${n}: ${String(t)}`),e.push(t)}}throw this.logger.error(`LDAP connection failed for all ${this.ldapUrls.length} URL(s)`),new Error(`LDAP connection failed for all URLs: ${e.map(e=>e.message).join(", ")}`)}cleanupExpiredConnections(){if(!this.isCleaningUp){this.isCleaningUp=!0;try{const e=Date.now(),t=[];for(let i=this.availableConnections.length-1;i>=0;i--){const s=this.availableConnections[i];if(e-s.createdAt>this.connectionTtl){t.push(s),this.availableConnections.splice(i,1);const e=this.connectionPool.indexOf(s);-1!==e&&this.connectionPool.splice(e,1)}}for(const e of t)e.client.unbind().catch(e=>{this.logger.debug(`Error unbinding expired connection: ${String(e)}`)});t.length>0&&this.logger.debug(`Cleaned up ${t.length} expired LDAP connections`)}finally{this.isCleaningUp=!1}}}async acquireConnection(){if(this.cleanupExpiredConnections(),this.availableConnections.length>0){const e=this.availableConnections.pop();return e.inUse=!0,this.logger.debug("Reusing pooled LDAP connection"),e}if(this.connectionPool.length<this.poolSize){const e={client:await this.createConnection(),createdAt:Date.now(),inUse:!0};return this.connectionPool.push(e),this.logger.debug(`Created new LDAP connection (pool: ${this.connectionPool.length}/${this.poolSize})`),e}return this.logger.debug("LDAP connection pool full, waiting for available connection"),new Promise((e,t)=>{const i=globalThis.setTimeout(()=>{const e=this.waitingResolvers.indexOf(resolveWrapper);-1!==e&&this.waitingResolvers.splice(e,1),t(new Error("LDAP connection pool timeout after 30s"))},3e4),resolveWrapper=t=>{globalThis.clearTimeout(i),e(t)};this.waitingResolvers.push(resolveWrapper)})}releaseConnection(e){if(Date.now()-e.createdAt>this.connectionTtl){const t=this.connectionPool.indexOf(e);return-1!==t&&this.connectionPool.splice(t,1),e.client.unbind().catch(()=>{}),void this.logger.debug("Released expired LDAP connection (discarded)")}if(this.waitingResolvers.length>0){const t=this.waitingResolvers.shift();return this.logger.debug("Released LDAP connection to waiting request"),void t(e)}e.inUse=!1,this.availableConnections.push(e),this.logger.debug("Released LDAP connection back to pool")}getAttributeSignature(e){if(!e||0===e.length)return"*";const t=e.join("|");let i=this.attrSignatureCache.get(t);return i||(i=[...e].sort().join(","),this.attrSignatureCache.set(t,i)),i}getCacheKey(e,t){const i=this.getAttributeSignature(t.attributes),s=t.explicitBufferAttributes?.length?[...t.explicitBufferAttributes].sort().join(","):"-",n="string"==typeof t.filter?t.filter:t.filter?t.filter.toString():"(objectClass=*)";return[e,t.scope||"sub",n,i,s,!1===t.returnAttributeValues?"novalues":"values",t.derefAliases??"-",String(t.sizeLimit??"-")].join("\0")}invalidateCache(e){if(!this.cacheEnabled)return;this.cacheGeneration++;const t=foldDn(e),i=`,${t}`,s=[];for(const e of this.searchCache.keys()){const n=foldDn(e.split("\0")[0]);(n===t||n.endsWith(i))&&s.push(e)}for(const e of s)this.searchCache.delete(e)}get system(){return this}forRequest(e){return{search:(t,i)=>this.search(t,i??this.base,e),add:(t,i)=>this.add(t,i,e),modify:(t,i,s)=>this.modify(t,i,e,s),rename:(t,i)=>this.rename(t,i,e),delete:t=>this.delete(t,e)}}declareObjectClasses(e,t,i=!1){const s=(Array.isArray(t)?t:[t]).filter(e=>"string"==typeof e&&""!==e);if(!e||0===s.length)return;const n=JSON.stringify([o(e),s.map(e=>e.toLowerCase()).sort(),i]);this.declaredClasses.set(n,{base:e,classes:s,includeBase:i})}declaredFor(e){let t=0,i=[];for(const s of this.declaredClasses.values()){if(!c(e,s.base))continue;const n=o(s.base);if(!s.includeBase&&o(e)===n)continue;const a=n.split(",").length;a>t&&(t=a,i=[]),a===t&&i.push(...s.classes)}const s=new Set;return i.filter(e=>{const t=e.toLowerCase();return!s.has(t)&&(s.add(t),!0)})}warnOnce(e){this.classWarnings.has(e)||(this.classWarnings.set(e,!0),this.logger.warn(e))}async classRepair(e,t){if(!1===this.config.ldap_flat_auto_repair)return[];const i=this.declaredFor(e);if(0===i.length)return[];const s=Array.isArray(t.delete)?t.delete:Object.entries(t.delete||{}).filter(([,e])=>!e||Array.isArray(e)&&!e.length).map(([e])=>e);if([...Object.keys(t.add||{}),...Object.keys(t.replace||{}),...Array.isArray(t.delete)?t.delete:Object.keys(t.delete||{})].some(e=>"objectclass"===e.toLowerCase()))return[];let n;try{n=await this.schemaIndex(),this.schemaUnavailable=!1}catch(e){return this.schemaUnavailable||this.logger.warn(`Object classes are not repaired: the directory schema could not be read (${String(e)})`),this.schemaUnavailable=!0,[]}const attributeKey=e=>n.getAttributeType(e)?.oid??e.toLowerCase(),allowedBy=e=>{const{must:t,may:i}=n.resolveAttributes(e);return new Set([...t,...i].map(attributeKey))},valued=e=>Object.entries(e||{}).filter(([,e])=>Array.isArray(e)?e.length>0:null!=e&&""!==e).map(([e])=>e),a=[...valued(t.add),...valued(t.replace)],r=i.filter(e=>{const t=n.getObjectClass(e);if("AUXILIARY"!==t?.kind)return!1;const i=allowedBy([e]);return a.some(e=>i.has(attributeKey(e)))});if(0===r.length)return[];let o;try{o=(await this.system.search({paged:!1,scope:"base",attributes:["objectClass",...n.resolveAttributes(r).must]},e)).searchEntries[0]}catch{return[]}if(!o)return[];const c=o.objectClass??o.objectclass,l=(Array.isArray(c)?c:c?[c]:[]).map(String),h=allowedBy(l);let d=a.filter(e=>n.getAttributeType(e)&&!h.has(attributeKey(e)));if(0===d.length)return[];const p=new Set(s.map(attributeKey)),u=new Set([...valued(o).filter(e=>!p.has(attributeKey(e))),...a].map(attributeKey)),{auxiliary:f,structural:g}=n.missingClasses(i,l),y=[];for(const t of f){const i=t.names[0]??t.oid,s=allowedBy([i]),a=d.filter(e=>s.has(attributeKey(e)));if(0===a.length)continue;const r=n.resolveAttributes([i]).must.filter(e=>!u.has(attributeKey(e)));r.length>0?this.warnOnce(`${e} needs ${i} for ${a.join(", ")}, and cannot be given it without ${r.join(", ")}`):(y.push(i),d=d.filter(e=>!a.includes(e)))}const m=g.filter(e=>{const t=allowedBy([e]);return d.some(e=>t.has(attributeKey(e)))});return m.length>0&&this.warnOnce(`${e} cannot hold ${d.join(", ")}: its schema declares ${m.join(", ")}, a structural class an existing entry cannot be given`),y.length>0&&this.logger.info(`${e}: adding ${y.join(", ")}, which the change needs and its schema declares`),y}async subschemaDn(){try{const e=await this.system.search({paged:!1,scope:"base",filter:"(objectClass=*)",attributes:["subschemaSubentry"]},""),t=e.searchEntries[0]?.subschemaSubentry,i=Array.isArray(t)?t[0]:t;if(i)return String(i)}catch(e){this.logger.warn(`Unable to read subschemaSubentry from root DSE: ${String(e)}`)}return"cn=Subschema"}async schemaIndex(){const e=1e3*(this.config.ldap_raw_schema_cache_ttl??3600);if(this.schemaCache&&Date.now()-this.schemaCache.fetchedAt<e)return this.schemaCache.index;if(this.schemaLoading)return this.schemaLoading;this.schemaLoading=(async()=>{const e=await this.subschemaDn(),t=(await this.system.search({paged:!1,scope:"base",filter:"(objectClass=*)",attributes:["objectClasses","attributeTypes","ldapSyntaxes","matchingRules"]},e).catch(t=>{if(32===t?.code)throw new g(`Subschema entry ${e} not found`);throw t})).searchEntries[0];if(!t)throw new g(`Subschema entry ${e} not found`);const list=e=>{if(void 0===e)return[];return(Array.isArray(e)?e:[e]).map(e=>Buffer.isBuffer(e)?e.toString("utf8"):e)},i=new m(b({objectClasses:list(t.objectClasses),attributeTypes:list(t.attributeTypes),ldapSyntaxes:list(t.ldapSyntaxes),matchingRules:list(t.matchingRules)}));return this.schemaCache={index:i,fetchedAt:Date.now()},this.logger.info(`LDAP schema loaded from ${e}: ${i.schema.objectClasses.length} object classes, ${i.schema.attributeTypes.length} attribute types`),i})();try{return await this.schemaLoading}finally{this.schemaLoading=void 0}}filterForCaller(e,t,i){if(!this.parent.hooks.ldapsearchfilter)return e;const s=this.parent.hooks.ldapsearchfilter,one=async e=>{const[n]=await l(s,[e,t,i]);return n??e};if("function"==typeof e[Symbol.asyncIterator]){const t=e;return async function*(){for await(const e of t)yield await one(e)}()}return one(e)}async search(e,t=this.base,i){let s={...w,...e};s=await l(this.parent.hooks.ldapsearchopts,s),[t,s]=await l(this.parent.hooks.ldapsearchrequest,[t,s,i]);const n=this.cacheEnabled&&!s.paged&&"base"===s.scope,a=n?this.getCacheKey(t,s):"",r=this.cacheGeneration;if(n){const e=this.searchCache.get(a);if(e)return this.logger.debug(`LDAP search cache hit: ${digestKey(a)}`),this.filterForCaller(cloneSearchResult(e),i,s)}const o=await this.acquireConnection();try{let e=s.paged?o.client.searchPaginated(t,s):o.client.search(t,s);if(e=await l(this.parent.hooks.ldapsearchresult,e),n){const t=e;return r===this.cacheGeneration?(this.searchCache.set(a,cloneSearchResult(t)),this.logger.debug(`LDAP search cached: ${digestKey(a)}`)):this.logger.debug(`LDAP search not cached, a write overtook it: ${digestKey(a)}`),this.filterForCaller(t,i,s)}return s.paged?this.filterForCaller(this.wrapPaginatedSearch(e,o),i,s):this.filterForCaller(e,i,s)}finally{s.paged||this.releaseConnection(o)}}async*wrapPaginatedSearch(e,t){try{for await(const t of e)yield t}finally{this.releaseConnection(t)}}async add(e,t,s){e=this.setDn(e),t.objectClass&&0!==t.objectClass.length||!this.config.user_class||(t.objectClass=this.config.user_class);const n={};for(const[e,i]of Object.entries(t))Buffer.isBuffer(i)?n[e]=i.toString():Array.isArray(i)&&i.length>0&&Buffer.isBuffer(i[0])?n[e]=i.map(e=>e.toString()):n[e]=i;[e,t]=await l(this.parent.hooks.ldapaddrequest,[e,n,s]);const a=[];for(const[e,s]of Object.entries(t)){const t=Array.isArray(s)?s:[s];a.push(new i({type:e,values:t}))}const r=await this.acquireConnection();try{this.invalidateCache(e),await r.client.add(e,a),this.invalidateCache(e),h(this.parent.hooks.ldapadddone,[e,t],p(s))}catch(t){throw function(e,t){const i=ldapError("LDAP add error",t);if(68!==i.code)return i;const s=new y(`Entry ${e} already exists`);return s.code=68,s}(e,t)}finally{this.releaseConnection(r)}return await h(this.parent.hooks.ldapaddafter,[e,t],p(s)),!0}async modify(e,t,i,s={}){const n=this.opNumber(),a=[];try{return await u(n,()=>this.applyModify(e,t,i,s,n,a))}finally{this.endOperation(a,this.parent.hooks.ldapmodifyend,n)}}async applyModify(e,t,a,r,o,c){e=this.setDn(e);const d=[];if([e,t]=await l(this.parent.hooks.ldapmodifyrequest,[e,t,o,a]),t.add)for(const[e,n]of Object.entries(t.add))d.push(new s({operation:"add",modification:new i({type:e,values:Array.isArray(n)?n:[n]})}));if(t.replace)for(const[e,n]of Object.entries(t.replace))d.push(new s({operation:"replace",modification:new i({type:e,values:Array.isArray(n)?n:[n]})}));if(t.delete)if(Array.isArray(t.delete))for(const e of t.delete)e&&d.push(new s({operation:"delete",modification:new i({type:e,values:[]})}));else for(const[e,n]of Object.entries(t.delete)){const t=new s({operation:"delete",modification:new i(n?{type:e,values:Array.isArray(n)?n:[n]}:{type:e})});d.push(t)}if(0===d.length){return Object.keys(t.add||{}).length>0||Object.keys(t.replace||{}).length>0||(Array.isArray(t.delete)?t.delete.length>0:Object.keys(t.delete||{}).length>0)?this.logger.warn(`Modify on ${e} asked for changes but emitted none; nothing was written`):this.logger.debug(`Modify on ${e} had nothing to apply`),c.push(h(this.parent.hooks.ldapmodifydone,[e,{},o],r.context??p(a))),!1}{const l=await this.classRepair(e,t),u=l.length>0?new s({operation:"add",modification:new i({type:"objectClass",values:l})}):void 0;u&&d.push(u);const f=await this.acquireConnection();try{this.invalidateCache(e);const i=r.permissive?[new n("1.2.840.113556.1.4.1413",{critical:!1})]:void 0;try{await f.client.modify(e,d,i)}catch(t){if(!u||20!==t?.code)throw t;d.splice(d.indexOf(u),1),await f.client.modify(e,d,i)}return this.invalidateCache(e),c.push(h(this.parent.hooks.ldapmodifydone,[e,t,o],r.context??p(a))),!0}catch(t){throw this.logger.warn(`Changes that failed: ${e}, ${JSON.stringify(d)}`),function(e,t){const i=ldapError("LDAP modify error",t);if(65!==i.code)return i;const s=new y(`Entry ${e} cannot hold this change: ${String(t)}`);return s.code=65,s}(e,t)}finally{this.releaseConnection(f)}}}async rename(e,t,i){e=this.setDn(e),t=this.setDn(t);const s=this.opNumber(),n=[];try{return await u(s,()=>this.applyRename(e,t,i,n))}finally{this.endOperation(n,this.parent.hooks.ldaprenameend,s)}}async applyRename(e,t,i,s){[e,t]=await l(this.parent.hooks.ldaprenamerequest,[e,t,i]);const n=await this.acquireConnection();try{return this.invalidateCache(e),this.invalidateCache(t),await n.client.modifyDN(e,t),this.invalidateCache(e),this.invalidateCache(t),s.push(h(this.parent.hooks.ldaprenamedone,[e,t],p(i))),!0}catch(e){throw ldapError("LDAP rename error",e)}finally{this.releaseConnection(n)}}async move(e,t,i){e=this.setDn(e),t=this.setDn(t);const s=await this.acquireConnection();try{return this.invalidateCache(e),this.invalidateCache(t),await s.client.modifyDN(e,t),this.invalidateCache(e),this.invalidateCache(t),f(()=>h(this.parent.hooks.ldaprenamedone,[e,t],p(i))),this.logger.debug(`LDAP move: ${e} -> ${t}`),!0}catch(e){throw ldapError("LDAP move error",e)}finally{this.releaseConnection(s)}}async delete(e,t,i={}){const s=(Array.isArray(e)?e:[e]).map(e=>this.setDn(e)),n=this.opNumber(),a=[];try{return await u(n,()=>this.applyDelete(s,t,i,a))}finally{this.endOperation(a,this.parent?.hooks.ldapdeleteend,n)}}async applyDelete(e,t,i,s){[e]=await l(this.parent?.hooks.ldapdeleterequest,[e,t]);const n=await this.acquireConnection();try{for(const a of e){try{this.invalidateCache(a),await n.client.del(a),this.invalidateCache(a)}catch(e){throw ldapError("LDAP delete error",e)}s.push(h(this.parent.hooks.ldapdeletedone,a,i.context??p(t)))}return!0}finally{this.releaseConnection(n)}}setDn(e){return/=/.test(e)?/,/.test(e)||(e+=`,${this.base}`):e=`${this.config.ldap_user_main_attribute}=${d(e)},${this.base}`,e}endOperation(e,t,...i){t?.length&&Promise.all(e).then(()=>h(t,...i))}opNumber(){return this.parent.operationSequence++}}export{ldapActions as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{AsyncLocalStorage as n}from"async_hooks";const r=new n;function runOperation(n,t){return r.run(n,t)}function outsideOperation(n){return r.exit(n)}function currentOperation(){return r.getStore()}export{currentOperation,outsideOperation,runOperation};
|
package/dist/lib/parseConfig.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class ConfigParser{constructor(
|
|
1
|
+
class ConfigParser{constructor(e){this.config=e}parse(e=process.argv){const r={},t=this.parseCliArgs(e);for(const e of this.config){const n=this.getKeyFromCliArg(e[0]);let i=e[2],o=!0;if(void 0!==e[1]){const r=process.env[e[1]],t="array"===e[3]&&void 0!==r?splitValue(r,e[5],"plural"):[],n=void 0!==r&&("array"===e[3]&&0===t.length||("number"===e[3]||"json"===e[3])&&""===r.trim());if(n&&e[6])throw new Error(`Error in environment variable ${e[1]}: the value is empty. Set it, or leave the variable out`);if(void 0!==r&&!n)if(o=!1,"boolean"===e[3])i="true"===r.toLowerCase();else if("number"===e[3]){if(i=parseNumber(r),Number.isNaN(i))throw new Error(`Error in environment variable ${e[1]}: "${r}" is not a number`)}else if("array"===e[3])i=t;else if("json"===e[3])try{i=JSON.parse(r)}catch(r){throw new Error(`Error parsing JSON from environment variable ${e[1]}: ${r}`)}else i=r}if(t.has(e[0])){const r=t.get(e[0]);if("boolean"===e[3])i=!0;else if("number"===e[3])i=r;else if("array"===e[3])i=(o?[]:i).concat(r.flatMap(r=>splitCliValue(e[0],r,e[5],"singular"))),o=!1;else if("json"===e[3])try{i=JSON.parse(r)}catch(r){throw new Error(`Error parsing JSON from command line argument ${e[0]}: ${r}`)}else i=r;t.delete(e[0])}if("array"===e[3]&&e[4]&&t.has(e[4])){const r=t.get(e[4]);i=(o?[]:i).concat(splitCliValue(e[4],r,e[5],"plural")),t.delete(e[4])}r[n]=i}return t.forEach((e,t)=>{if(r[t])throw new Error(`Error in command line: ${t} redefined`);r[t.replace(/^-+/,"").replace(/-/g,"_")]=e}),r}parseCliArgs(e){const r=new Map;for(let t=2;t<e.length;t++){const n=e[t];if(n.startsWith("--")||n.startsWith("-")){const i=this.config.find(e=>e[0]===n);if(i&&"boolean"===i[3])r.set(n,!0);else if(i&&"number"===i[3]){if(!e[t+1]?.trim())throw new Error(`Error in command line: ${n} has an empty value. Leave it out to keep the default`);const i=parseNumber(e[t+1]);if(Number.isNaN(i))throw new Error(`Error in command line: ${n} takes a number, got "${e[t+1]}"`);r.set(n,i),t++}else if(i&&"array"===i[3]){const o=r.get(n)||[],s=e[t+1];o.push(s),r.set(n,o);const a=e[t+2];if(void 0!==a&&!a.startsWith("-"))throw new Error(`Error in command line: ${n} takes one value, got "${s}" followed by "${a}". Repeat ${n} for each value`+(i[4]?`, or give them all to ${i[4]}`:""))}else{const i=e[t+1];r.set(n,i),t++}}}return r}getKeyFromCliArg(e){return e.startsWith("--")?e.substring(2).replace(/-/g,"_"):e.startsWith("-")?e.substring(1).replace(/-/g,"_"):e}}function splitValue(e,r,t){let n;if("singular"===t)"identifiers"===r&&(n=/,/);else if("dns"===r)n=/;/;else{const t="phrases"===r?"\\r\\n":"\\s";n=new RegExp(`[${e.includes(";")?";":","}${t}]+`)}return n?e.split(n).map(e=>e.trim()).filter(e=>e.length>0):[e.trim()].filter(e=>e.length>0)}function parseNumber(e){const r=e.trim();return/^-?\d+$/.test(r)?Number(r):NaN}function splitCliValue(e,r,t,n){const i=void 0===r?[]:splitValue(r,t,n);if(0===i.length)throw new Error(`Error in command line: ${e} has an empty value. Leave it out to keep the default`);return i}function parseConfig(e,r=process.argv){return new ConfigParser(e).parse(r)}export{ConfigParser,parseConfig};
|
package/dist/lib/rawBody.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
import e from"body-parser";const o=new WeakMap;function keepRawBody(e,r,n,t){o.set(e,{bytes:n,encoding:t})}function rawBodyOf(e){return o.get(e)}function jsonBodyParser(o={}){return e.json({...o,verify:keepRawBody})}export{jsonBodyParser,keepRawBody,rawBodyOf};
|
package/dist/lib/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BadRequestError as e}from"./errors.js";import{getLogger as n}from"./expressFormatedResponses.js";const r=new Map;function getCompiledRegex(e,n){const t=n?`${e}:${n}`:e;let i=r.get(t);return i||(i=new RegExp(e,n),r.set(t,i)),i}function escapeRegex(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}const describeThrown=e=>{try{return JSON.stringify(e)??String(e)}catch{return String(e)}},hookLabel=e=>{const n=hookOwner(e);return n?` in ${n.plugin} (${n.hook})`:e.name?` in ${e.name}`:""},launchHooks=async(e,...r)=>{if(e)for(const t of e)if(t)try{await t(...r)}catch(e){const r=hookLabel(t);e instanceof Error?n()?.error(`Hook error${r}`,e):n()?.error(`Hook error${r}: ${describeThrown(e)}`)}},t=new WeakMap,recordHookOwner=(e,n,r)=>{t.set(e,{plugin:n,hook:r})},hookOwner=e=>t.get(e),
|
|
1
|
+
import{BadRequestError as e}from"./errors.js";import{getLogger as n}from"./expressFormatedResponses.js";const r=new Map;function getCompiledRegex(e,n){const t=n?`${e}:${n}`:e;let i=r.get(t);return i||(i=new RegExp(e,n),r.set(t,i)),i}function escapeRegex(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}const describeThrown=e=>{try{return JSON.stringify(e)??String(e)}catch{return String(e)}},hookLabel=e=>{const n=hookOwner(e);return n?` in ${n.plugin} (${n.hook})`:e.name?` in ${e.name}`:""},launchHooks=async(e,...r)=>{if(e)for(const t of e)if(t)try{await t(...r)}catch(e){const r=hookLabel(t);e instanceof Error?n()?.error(`Hook error${r}`,e):n()?.error(`Hook error${r}: ${describeThrown(e)}`)}},t=new WeakMap,recordHookOwner=(e,n,r)=>{t.set(e,{plugin:n,hook:r})},hookOwner=e=>t.get(e),isAuthzRefusal=e=>/\[authz-forbidden\]/.test(String(e?.message??"")),isRefusal=e=>403===e?.statusCode||isAuthzRefusal(e),launchHooksChained=async(e,r)=>{if(e)for(const t of e)if(t)try{r=await t(r)}catch(e){if(isRefusal(e)){const r=hookOwner(t);n()?.warn(`${r?`${r.plugin} refused ${r.hook}`:`a hook no plugin registered (${t.name||"anonymous"}) refused`}: ${e.message}`)}throw e}return r},transformSchemas=(e,n)=>e.toString().replace(/__(\S+)__/g,(e,r)=>{if(!r||"string"!=typeof r)return e;const t=r.trim().toLowerCase();return n[t]?"object"!=typeof n[t]?n[t]:JSON.stringify(n[t]):e});function escapeLdapFilter(e){return e.replace(/\\/g,"\\5c").replace(/\*/g,"\\2a").replace(/\(/g,"\\28").replace(/\)/g,"\\29").replace(/\0/g,"\\00")}function substringSearchFilter(n,r){const t=/^[a-zA-Z][a-zA-Z0-9-]*$/,i=r.split(",").map(e=>e.trim()).filter(Boolean);if(0===i.length||!i.every(e=>t.test(e)))throw new e("Invalid LDAP attribute name");const o=escapeLdapFilter(n),a=i.map(e=>`(${e}=*${o}*)`);return 1===a.length?a[0]:`(|${a.join("")})`}function escapeDnValue(e){return e.replace(/\\/g,"\\\\").replace(/,/g,"\\,").replace(/\+/g,"\\+").replace(/"/g,'\\"').replace(/</g,"\\<").replace(/>/g,"\\>").replace(/;/g,"\\;").replace(/=/g,"\\=").replace(/\0/g,"\\00").replace(/^\s/,"\\ ").replace(/\s$/,"\\ ").replace(/^#/,"\\#")}function unescapeDnValue(n){if("string"!=typeof n)throw new e("DN value must be a string");let r="",t=0;for(;t<n.length;)if("\\"===n[t]&&t+1<n.length){const e=n[t+1];if(/[0-9a-fA-F]/.test(e)&&t+2<n.length){const e=n.substring(t+1,t+3);if(/^[0-9a-fA-F]{2}$/.test(e)){r+=String.fromCharCode(parseInt(e,16)),t+=3;continue}}r+=e,t+=2}else r+=n[t],t++;return r}function validateDnValue(e,n){if(null==e||"string"!=typeof e)throw new Error(`${n} must be a string`);if(0===e.trim().length)throw new Error(`${n} must be a non-empty string`);if(/[\x00-\x1F\x7F]/.test(e))throw new Error(`${n} contains invalid control characters`);if(/[\u200B-\u200F\uFEFF]/.test(e))throw new Error(`${n} contains invalid invisible characters`)}function parseDn(n){if("string"!=typeof n)throw new e("DN must be a string");const r=[];let t="",i=!1;for(let e=0;e<n.length;e++){const o=n[e];i?(t+=o,i=!1):"\\"===o?(t+=o,i=!0):","===o?(r.push(t.trim()),t=""):t+=o}return t&&r.push(t.trim()),r}function getParentDn(e){const n=parseDn(e);return n.length<=1?e:n.slice(1).join(",")}function getRdn(e){return parseDn(e)[0]||""}function rdnValue(e){const n=/^[^=]+=((?:\\.|[^,])*)/.exec(e);return n?n[1].replace(/\\(.)/g,"$1"):""}function isChildOf(e,n){const r=e.toLowerCase(),t=n.toLowerCase();if(!r.endsWith(t))return!1;if(r.length===t.length)return!1;return r.substring(0,r.length-t.length).endsWith(",")}function normalizeDn(e){return parseDn(e).map(e=>e.split("+").map(e=>{const n=e.indexOf("=");return(-1===n?e.trim():`${e.slice(0,n).trim()}=${e.slice(n+1).trim()}`).toLowerCase()}).sort().join("+")).filter(e=>e.length>0).join(",")}function isDummyMemberDn(e,n){if(!n||"string"!=typeof e)return!1;if(e===n)return!0;try{return normalizeDn(e)===normalizeDn(n)}catch{return!1}}function organizationLink(e){const n=Array.isArray(e)?e[0]:e;if(null==n)return;const r=String(n);return r.length>0?r:void 0}function isDnInBranch(e,n){const r=normalizeDn(n).split(",").filter(Boolean);if(0===r.length)return!1;const t=normalizeDn(e).split(",").filter(Boolean);if(t.length<r.length)return!1;return t.slice(t.length-r.length).every((e,n)=>e===r[n])}const asyncHandler=e=>(n,r,t)=>{Promise.resolve(e(n,r,t)).catch(t)};export{asyncHandler,escapeDnValue,escapeLdapFilter,escapeRegex,getCompiledRegex,getParentDn,getRdn,hookOwner,isAuthzRefusal,isChildOf,isDnInBranch,isDummyMemberDn,launchHooks,launchHooksChained,normalizeDn,organizationLink,parseDn,rdnValue,recordHookOwner,substringSearchFilter,transformSchemas,unescapeDnValue,validateDnValue};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHmac as e,createHash as t,timingSafeEqual as
|
|
1
|
+
import{createHmac as e,createHash as t,timingSafeEqual as r}from"crypto";import{unauthorized as i}from"../../lib/expressFormatedResponses.js";import{rawBodyOf as s}from"../../lib/rawBody.js";import n from"../../lib/auth/base.js";class AuthHmac extends n{knownIdentities(){return[...this.services.values()].map(e=>e.name)}identitySource(){return"req.user and req.userName: the service name given in --auth-hmac"}constructor(...e){super(...e),this.name="authHmac",this.roles=["auth"],this.services=new Map;const t=this.config.auth_hmac;this.timeWindow=this.config.auth_hmac_window??12e4,t&&Array.isArray(t)&&t.forEach((e,t)=>{const r=e.split(":"),i=r[0].trim(),s=(r[1]??"").trim(),n=r.slice(2).join(":").trim();if(r.length<3||!i||!s||!n)throw new Error(`Invalid --auth-hmac entry at index ${t}: expected "service-id:secret:name", none of them empty; entries are separated by \`,\`, \`;\` or newlines`);s.length<32&&this.logger.warn(`HMAC secret for service "${i}" is too short (minimum 32 characters recommended)`),r.length>4&&this.logger.warn(`HMAC entry ${t} for service "${i}" has ${r.length} fields: if several services were given, separate them with \`,\` or \`;\``),this.services.set(i,{id:i,secret:s,name:n})}),0===this.services.size?this.logger.warn("No valid HMAC services configured"):(this.logger.info(`HMAC authentication initialized with ${this.services.size} service(s): ${Array.from(this.services.keys()).join(", ")}`),this.logger.info(`Time window for replay protection: ${this.timeWindow}ms`))}authMethod(e,t,r){const s=e.headers.authorization;if(!s||!s.startsWith("HMAC-SHA256 "))return this.logger.warn("Missing or invalid Authorization header (expected HMAC-SHA256)"),i(t);const n=s.substring(12).split(":");if(3!==n.length)return this.logger.warn("Invalid HMAC authorization format (expected service-id:timestamp:signature)"),i(t);const[a,o,h]=n,c=this.services.get(a);if(!c)return this.logger.warn(`Unknown service ID: ${a}`),i(t);const u=parseInt(o,10);if(isNaN(u)||u<=0)return this.logger.warn(`Invalid timestamp: ${o}`),i(t);const g=Date.now(),m=Math.abs(g-u);if(m>this.timeWindow)return this.logger.warn(`Timestamp outside allowed window: ${m}ms (max: ${this.timeWindow}ms) for service ${a}`),i(t);const l=this.calculateBodyHash(e);if(void 0===l)return this.logger.warn(`Refusing ${e.method} ${e.originalUrl||e.url} from service ${a}: its body (${e.headers["content-type"]||"no Content-Type"}) cannot be checked against the signature`),i(t);const d=`${e.method.toUpperCase()}|${e.originalUrl||e.url}|${u}|${l}`,f=this.calculateHmac(c.secret,d);if(!this.secureCompare(h,f))return this.logger.warn(`Invalid signature for service ${a} (${c.name}). Expected: ${f.substring(0,8)}..., Got: ${h.substring(0,8)}...`),this.logger.debug(`Signing string: ${d}`),i(t);this.logger.debug(`HMAC authentication successful for service: ${a} (${c.name})`),this.publishIdentity(e,c.name),r()}calculateHmac(t,r){const i=e("sha256",t);return i.update(r),i.digest("hex")}calculateBodyHash(e){const r=e.method.toUpperCase(),i=s(e),n=parseInt(e.headers["content-length"]??"",10),a=!i&&(void 0!==e.headers["transfer-encoding"]||n>0);if("GET"===r||"DELETE"===r||"HEAD"===r)return i?.bytes.length||a?void 0:"";if(!a){if(!i?.bytes.length)return"";if("utf-8"===i.encoding)return t("sha256").update(i.bytes).digest("hex")}}secureCompare(e,t){if(e.length!==t.length)return!1;try{const i=Buffer.from(e,"utf8"),s=Buffer.from(t,"utf8");return r(i,s)}catch{return!1}}}export{AuthHmac as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHmac as t}from"crypto";import{unauthorized as e}from"../../lib/expressFormatedResponses.js";import s from"../../lib/auth/base.js";class AuthTotp extends s{knownIdentities(){return this.totpUsers.map(t=>t.name)}identitySource(){return"req.user and req.userName: the name given in --auth-totp"}constructor(...t){super(...t),this.name="authTotp",this.roles=["auth"],this.totpUsers=[];const e=this.config.auth_totp;this.window=this.config.auth_totp_window??1,this.step=this.config.auth_totp_step??30,e&&Array.isArray(e)&&e.forEach((t,e)=>{const s=t.split(":")
|
|
1
|
+
import{createHmac as t}from"crypto";import{unauthorized as e}from"../../lib/expressFormatedResponses.js";import s from"../../lib/auth/base.js";class AuthTotp extends s{knownIdentities(){return this.totpUsers.map(t=>t.name)}identitySource(){return"req.user and req.userName: the name given in --auth-totp"}constructor(...t){super(...t),this.name="authTotp",this.roles=["auth"],this.totpUsers=[];const e=this.config.auth_totp;this.window=this.config.auth_totp_window??1,this.step=this.config.auth_totp_step??30,e&&Array.isArray(e)&&e.forEach((t,e)=>{const s=t.split(":"),[r,i,o]=s.map(t=>t.trim()),n=void 0===o?6:Number(o),fail=t=>{throw new Error(`Invalid --auth-totp entry at index ${e}: ${t}; entries are separated by \`,\`, \`;\` or newlines`)};(s.length<2||s.length>3||!r||!i)&&fail('expected "secret:name[:digits]"'),this.isValidBase32(r)||fail("the secret is not Base32"),(""===o||!Number.isInteger(n)||n<6||n>10)&&fail("digits must be an integer from 6 to 10"),this.totpUsers.push({name:i,secret:r,digits:n})}),0===this.totpUsers.length?this.logger.warn("No valid TOTP users configured"):this.logger.info(`TOTP authentication initialized with ${this.totpUsers.length} user(s)`)}authMethod(t,s,r){const i=t.headers.authorization;if(!i||!/^Bearer .+/.test(i))return this.logger.warn("Missing or invalid Authorization header"),e(s);const o=i.split(" ")[1];for(const e of this.totpUsers)if(this.verifyTotp(e.secret,o,e.digits))return this.logger.debug(`TOTP authentication successful for user: ${e.name}`),this.publishIdentity(t,e.name),r();return this.logger.warn(`Unauthorized TOTP token: ${o}`),e(s)}verifyTotp(t,e,s){const r=Math.floor(Date.now()/1e3);for(let i=-this.window;i<=this.window;i++){const o=Math.floor(r/this.step)+i;if(this.generateTotp(t,o,s)===e)return!0}return!1}generateTotp(e,s,r){const i=this.base32Decode(e),o=Buffer.alloc(8);o.writeBigUInt64BE(BigInt(s));const n=t("sha1",i);n.update(o);const a=n.digest(),h=15&a[a.length-1];return(((127&a[h])<<24|(255&a[h+1])<<16|(255&a[h+2])<<8|255&a[h+3])%Math.pow(10,r)).toString().padStart(r,"0")}base32Decode(t){let e=t.toUpperCase();for(;e.endsWith("=");)e=e.slice(0,-1);let s=0,r=0;const i=[];for(let t=0;t<e.length;t++){const o="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".indexOf(e[t]);if(-1===o)throw new Error(`Invalid Base32 character: ${e[t]}`);r=r<<5|o,s+=5,s>=8&&(i.push(r>>>s-8&255),s-=8)}return Buffer.from(i)}isValidBase32(t){return/^[A-Z2-7]+=*$/.test(t.toUpperCase())}}export{AuthTotp as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import e from"../../abstract/plugin.js";import{launchHooks as t}from"../../lib/utils.js";import{currentOperation as n}from"../../lib/operation.js";const normalize=e=>(void 0===e?[]:Array.isArray(e)?e:[e]).map(e=>Buffer.isBuffer(e)?e.toString("base64"):String(e)).sort();function diffEntries(e,t){const index=e=>new Map(Object.keys(e||{}).filter(e=>"dn"!==e).map(e=>[e.toLowerCase(),e])),n=index(e),i=index(t),a={};for(const r of new Set([...n.keys(),...i.keys()])){const s=n.get(r),o=i.get(r),d=s?e[s]:void 0,h=o?t[o]:void 0,l=normalize(d),c=normalize(h);l.length===c.length&&l.every((e,t)=>e===c[t])||(a[o||s]=[l.length?d:null,c.length?h:null])}return a}const i={mail_attribute:"onLdapMailChange",quota_attribute:"onLdapQuotaChange",alias_attribute:"onLdapAliasChange",forward_attribute:"onLdapForwardChange",display_name_attribute:"onLdapDisplayNameChange",drive_quota_attribute:"onLdapDriveQuotaChange"};class OnLdapChange extends e{constructor(){super(...arguments),this.name="onLdapChange",this.roles=["consistency"],this.stack={},this.pendingDeletions=new Map,this.pendingRenames=new Map,this.hooks={ldapadddone:async([e,t],n)=>{const i=await this.read(e)||{dn:e,...t};this.publish(e,null,i,n)},ldapmodifyrequest:async([e,t,n,i])=>{const a=await this.read(e);return a?this.stack[n]=a:this.logger.warn(`Could not read ${e} before modification`),[e,t,n,i]},ldapmodifydone:async([e,t,n],i)=>{const a=this.stack[n];if(delete this.stack[n],!a)return void this.logger.warn(`Received a ldapmodifydone for an unknown operation (${n})`);if(0===Object.keys(t).length)return;const r=await this.read(e);r?this.publish(e,a,r,i):this.logger.warn(`Could not read ${e} after modification`)},ldapmodifyend:e=>{delete this.stack[e]},ldaprenamerequest:async([e,t,i])=>{const a=n(),r=void 0===a?void 0:await this.read(e);return void 0!==a&&r&&this.pendingRenames.set(a,{dn:e,entry:r}),[e,t,i]},ldaprenamedone:async([e,t],i)=>{const a=n(),r=void 0===a?void 0:this.pendingRenames.get(a);if(!r||r.dn!==e)return void this.logger.debug(`${e}: no snapshot: a move made outside a request`);this.pendingRenames.delete(a);const s=await this.read(t);s?this.publish(t,r.entry,s,i):this.logger.warn(`Could not read ${t} after the rename of ${e}`)},ldaprenameend:e=>{this.pendingRenames.delete(e)},ldapdeleterequest:async([e,t])=>{const i=n();if(void 0===i)return[e,t];for(const t of Array.isArray(e)?e:[e]){const e=await this.read(t);if(!e)continue;let n=this.pendingDeletions.get(i);n||this.pendingDeletions.set(i,n=new Map),n.set(t,e)}return[e,t]},ldapdeletedone:(e,t)=>{const i=n(),a=void 0===i?void 0:this.pendingDeletions.get(i);for(const n of Array.isArray(e)?e:[e]){const e=a?.get(n);e&&(a.delete(n),this.publish(n,e,null,t))}},ldapdeleteend:e=>{this.pendingDeletions.delete(e)}}}async read(e){const t=new Set(Object.values(this.server.loadedPlugins).flatMap(e=>e.followedOperationalAttributes||[]));try{const n=(await this.server.ldap.search({paged:!1,scope:"base",...t.size?{attributes:["*",...t]}:{}},e)).searchEntries[0];if(!n)return;return Object.fromEntries(Object.entries(n).filter(([,e])=>!Array.isArray(e)||e.length>0))}catch(e){return}}publish(e,n,i,a={}){const r=diffEntries(n,i);0===Object.keys(r).length&&n?.dn===i?.dn||(t(this.server.hooks.onLdapEntryChange,e,n,i,a),this.notify(e,r,n,i))}notify(e,n,a,r){t(this.server.hooks.onLdapChange,e,n);for(const[a,s]of Object.entries(i))if(this.config[a]&&n[this.config[a]])if("onLdapQuotaChange"===s||"onLdapForwardChange"===s||"onLdapAliasChange"===s)this.notifyAttributeChangeWithMail(this.config[a],s,e,n,r);else if("onLdapMailChange"===s)this.notifyAttributeChange(this.config[a],s,e,n);else if("onLdapDriveQuotaChange"===s){const[i,r]=n[this.config[a]]||[],s=i?Number(i):null,o=r?Number(r):null;s!==o&&t(this.server.hooks.onLdapDriveQuotaChange,e,s,o)}const s=this.reconstructDisplayName(a),o=this.reconstructDisplayName(r);s!==o&&t(this.server.hooks.onLdapDisplayNameChange,e,s,o)}notifyAttributeChange(e,n,i,a,r=!1){const[s,o]=a[e]||[];void 0===s&&void 0===o||(r&&(Array.isArray(s)||Array.isArray(o))?this.logger.error(`Attribute ${e} change detected but one of the values is an array, cannot handle that`):s!==o&&t(this.server.hooks[n],i,s,o))}notifyAttributeChangeWithMail(e,n,i,a,r){const[s,o]=a[e]||[];if(void 0===s&&void 0===o)return;const d=this.config.mail_attribute||"mail",h=a[d];let l;if(h)l=Array.isArray(h[1])?String(h[1][0]):String(h[1]);else{if(!r?.[d])return void this.logger.warn(`Could not find mail for ${i}, skipping ${n} notification`);{const e=r[d];l=Array.isArray(e)?String(e[0]):String(e)}}if("onLdapQuotaChange"===n){const e=s?Number(s):0,a=o?Number(o):0;e!==a&&t(this.server.hooks[n],i,l,e,a)}else if("onLdapForwardChange"===n||"onLdapAliasChange"===n){const e=s?Array.isArray(s)?s:[s]:[],a=o?Array.isArray(o)?o:[o]:[];(e.length>0||a.length>0)&&t(this.server.hooks[n],i,l,e,a)}}reconstructDisplayName(e){const getValue=t=>{const n=e?.[t];return n?Array.isArray(n)?n.length>0?String(n[0]):null:String(n):null},t=getValue("cn");if(t)return t;const n=getValue("givenName"),i=getValue("sn");if(n||i){const e=[];return n&&e.push(n),i&&e.push(i),e.join(" ")}return null}}export{OnLdapChange as default,diffEntries};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{HttpError as r}from"../../lib/errors.js";import{extractLdapCode as e}from"../../lib/ldapCodes.js";import{SCHEMA_ERROR as
|
|
1
|
+
import{HttpError as r,AUTHZ_REFUSED as t}from"../../lib/errors.js";import{extractLdapCode as e}from"../../lib/ldapCodes.js";import{SCHEMA_ERROR as i}from"./types.js";const o="application/scim+json";class ScimError extends r{constructor(r,t,e){super(t,r),this.name="ScimError",this.scimType=e}}const scimInvalidFilter=(r="Invalid filter")=>new ScimError(400,r,"invalidFilter"),scimInvalidPath=(r="Invalid path")=>new ScimError(400,r,"invalidPath"),scimInvalidValue=(r="Invalid value")=>new ScimError(400,r,"invalidValue"),scimInvalidSyntax=(r="Invalid syntax")=>new ScimError(400,r,"invalidSyntax"),scimNoTarget=(r="No target")=>new ScimError(400,r,"noTarget"),scimMutability=(r="Immutable attribute")=>new ScimError(400,r,"mutability"),scimUniqueness=(r="Uniqueness violation")=>new ScimError(409,r,"uniqueness"),scimTooMany=(r="Too many results")=>new ScimError(400,r,"tooMany"),scimNotFound=(r="Resource not found")=>new ScimError(404,r);function writeScimError(r,t,e,n){const s={schemas:[i],status:String(t),...n?{scimType:n}:{},detail:e};r.status(t).type(o).json(s)}function scimErrorFromException(o,n=500){const sanitize=r=>/\[authz-forbidden\]/.test(r)?t:r,body=(r,t,e)=>({schemas:[i],status:String(r),...e?{scimType:e}:{},detail:t});if(o instanceof ScimError)return body(o.statusCode,sanitize(o.message),o.scimType);if(o instanceof r)return body(o.statusCode,sanitize(o.message));const s=o instanceof Error?o.message:String(o);if(/\[authz-forbidden\]/.test(s))return body(403,t);const c=e(o);return 32===c?body(404,"Resource not found"):68===c?body(409,sanitize(s),"uniqueness"):17===c||65===c?body(400,`The directory rejected this write against its schema: ${sanitize(s)}`,"invalidValue"):body(n,sanitize(s)||"Internal error")}function writeScimErrorFromException(r,t,e=500){const i=scimErrorFromException(t,e);r.status(Number(i.status)).type(o).json(i)}const scimAsyncHandler=r=>(t,e,i)=>{Promise.resolve().then(()=>r(t,e,i)).catch(r=>{e.headersSent?i(r):writeScimErrorFromException(e,r)})};export{o as SCIM_CONTENT_TYPE,ScimError,e as extractLdapCode,scimAsyncHandler,scimErrorFromException,scimInvalidFilter,scimInvalidPath,scimInvalidSyntax,scimInvalidValue,scimMutability,scimNoTarget,scimNotFound,scimTooMany,scimUniqueness,writeScimError,writeScimErrorFromException};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fileURLToPath as s}from"url";import{join as e,dirname as
|
|
1
|
+
import{fileURLToPath as s}from"url";import{join as e,dirname as i}from"path";import t from"../../abstract/plugin.js";import{setChangeSource as o}from"../../lib/changeContext.js";import{jsonBodyParser as r}from"../../lib/rawBody.js";import{BaseResolver as a}from"./baseResolver.js";import{ScimUsers as c}from"./users.js";import{ScimGroups as n}from"./groups.js";import{ScimBulk as p}from"./bulk.js";import{ScimDiscovery as m}from"./discovery.js";import{SCIM_CONTENT_TYPE as u,scimAsyncHandler as d,writeScimError as h}from"./errors.js";export{writeScimErrorFromException}from"./errors.js";import{projectResource as l,assertProjection as g,projectList as f}from"./projection.js";function scimBodyParser(s){return r({type:["application/json","application/scim+json"],limit:`${s.scim_bulk_max_payload_size||1048576}b`})}class Scim extends t{constructor(t){super(t),this.name="scim",this.roles=["api","configurable"],this.ldap=t.ldap,this.scimPrefix=this.config.scim_prefix||"/scim/v2",this.baseResolver=new a(this.config),this.users=new c({ldap:this.ldap,config:this.config,logger:this.logger,baseResolver:this.baseResolver,hooks:this.registeredHooks}),this.groups=new n({ldap:this.ldap,config:this.config,logger:this.logger,baseResolver:this.baseResolver,users:this.users,hooks:this.registeredHooks}),this.bulk=new p({config:this.config,logger:this.logger,users:this.users,groups:this.groups,scimPrefix:this.scimPrefix,hooks:this.registeredHooks});const o=this.config.schemas_path||e(i(s(import.meta.url)),"..","..","..","static","schemas");this.discovery=new m({config:this.config,schemaDir:e(o,"scim"),scimPrefix:this.scimPrefix,loadedPlugins:t.loadedPlugins})}scimJson(s,e,i){s.status(e).type(u).json(i)}scimResource(s,e,i,t,o={}){const r=i.id?`${this.scimPrefix}/${t}/${encodeURIComponent(i.id)}`:void 0,a=(this.config.scim_base_url||"")&&i.meta?.location?i.meta.location:r;a&&s.set("Location",a),this.scimJson(s,e,l(i,o))}parseProjection(s){const toCsv=s=>{if("string"!=typeof s)return;const e=s.split(",").map(s=>s.trim()).filter(Boolean);return e.length>0?e:void 0},e={attributes:toCsv(s.query.attributes),excludedAttributes:toCsv(s.query.excludedAttributes)};return g(e),e}readScimBody(s){const e=String(s.headers["content-type"]||"");if(e.startsWith("application/scim+json")||e.startsWith("application/json"))return s.body}api(s){const e=this.scimPrefix;this.server.registerBodyParser(e,scimBodyParser(this.config)),s.use(e,(s,e,i)=>{o(s,"scim"),i()}),s.use(e,d((s,e,i)=>{this.parseProjection(s),i()})),s.get(`${e}/Users`,d(async(s,e)=>{const i=this.parseListQuery(s);g(i);const t=await this.users.list(s,i);this.scimJson(e,200,f(t,i))})),s.get(`${e}/Users/:id`,d(async(s,e)=>{const i=this.parseProjection(s),t=await this.users.get(s,decodeURIComponent(s.params.id));this.scimJson(e,200,l(t,i))})),s.post(`${e}/Users`,d(async(s,e)=>{const i=this.readScimBody(s);if(!i||"object"!=typeof i)return h(e,400,"Missing body","invalidSyntax");const t=await this.users.create(s,i);this.scimResource(e,201,t,"Users",this.parseProjection(s))})),s.put(`${e}/Users/:id`,d(async(s,e)=>{const i=this.readScimBody(s);if(!i||"object"!=typeof i)return h(e,400,"Missing body","invalidSyntax");const t=await this.users.replace(s,decodeURIComponent(s.params.id),i);this.scimResource(e,200,t,"Users",this.parseProjection(s))})),s.patch(`${e}/Users/:id`,d(async(s,e)=>{const i=this.readScimBody(s);if(!i||"object"!=typeof i)return h(e,400,"Missing body","invalidSyntax");const t=await this.users.patch(s,decodeURIComponent(s.params.id),i);this.scimResource(e,200,t,"Users",this.parseProjection(s))})),s.delete(`${e}/Users/:id`,d(async(s,e)=>{await this.users.delete(s,decodeURIComponent(s.params.id)),e.status(204).end()})),s.get(`${e}/Groups`,d(async(s,e)=>{const i=this.parseListQuery(s);g(i);const t=await this.groups.list(s,i);this.scimJson(e,200,f(t,i))})),s.get(`${e}/Groups/:id`,d(async(s,e)=>{const i=this.parseProjection(s),t=await this.groups.get(s,decodeURIComponent(s.params.id));this.scimJson(e,200,l(t,i))})),s.post(`${e}/Groups`,d(async(s,e)=>{const i=this.readScimBody(s);if(!i||"object"!=typeof i)return h(e,400,"Missing body","invalidSyntax");const t=await this.groups.create(s,i);this.scimResource(e,201,t,"Groups",this.parseProjection(s))})),s.put(`${e}/Groups/:id`,d(async(s,e)=>{const i=this.readScimBody(s);if(!i||"object"!=typeof i)return h(e,400,"Missing body","invalidSyntax");const t=await this.groups.replace(s,decodeURIComponent(s.params.id),i);this.scimResource(e,200,t,"Groups",this.parseProjection(s))})),s.patch(`${e}/Groups/:id`,d(async(s,e)=>{const i=this.readScimBody(s);if(!i||"object"!=typeof i)return h(e,400,"Missing body","invalidSyntax");const t=await this.groups.patch(s,decodeURIComponent(s.params.id),i);this.scimResource(e,200,t,"Groups",this.parseProjection(s))})),s.delete(`${e}/Groups/:id`,d(async(s,e)=>{await this.groups.delete(s,decodeURIComponent(s.params.id)),e.status(204).end()})),s.post(`${e}/Bulk`,d(async(s,e)=>{const i=this.readScimBody(s);if(!i||"object"!=typeof i)return h(e,400,"Missing body","invalidSyntax");const t=this.config.scim_bulk_max_payload_size||1048576,o=JSON.stringify(i).length;if(o>t)return h(e,413,`Bulk payload too large: ${o} > ${t}`,"invalidValue");const r=await this.bulk.execute(s,i);this.scimJson(e,200,r)})),s.get(`${e}/ServiceProviderConfig`,d((s,e)=>{this.scimJson(e,200,this.discovery.serviceProviderConfig(s))})),s.get(`${e}/ResourceTypes`,d((s,e)=>{const i=this.discovery.resourceTypes(s);this.scimJson(e,200,{schemas:["urn:ietf:params:scim:api:messages:2.0:ListResponse"],totalResults:i.length,Resources:i,startIndex:1,itemsPerPage:i.length})})),s.get(`${e}/ResourceTypes/:name`,d((s,e)=>{const i=this.discovery.resourceType(s.params.name,s);if(!i)return h(e,404,"ResourceType not found");this.scimJson(e,200,i)})),s.get(`${e}/Schemas`,d((s,e)=>{const i=this.discovery.schemas(s);this.scimJson(e,200,{schemas:["urn:ietf:params:scim:api:messages:2.0:ListResponse"],totalResults:i.length,Resources:i,startIndex:1,itemsPerPage:i.length})})),s.get(`${e}/Schemas/:id`,d((s,e)=>{const i=this.discovery.schema(decodeURIComponent(s.params.id),s);if(!i)return h(e,404,"Schema not found");this.scimJson(e,200,i)}))}parseListQuery(s){const e=s.query,toInt=s=>{if("string"!=typeof s)return;const e=parseInt(s,10);return Number.isFinite(e)?e:void 0},toCsv=s=>{if("string"==typeof s)return s.split(",").map(s=>s.trim()).filter(Boolean)},i="string"==typeof e.sortOrder?e.sortOrder:void 0;return{filter:"string"==typeof e.filter?e.filter:void 0,startIndex:toInt(e.startIndex),count:toInt(e.count),attributes:toCsv(e.attributes),excludedAttributes:toCsv(e.excludedAttributes),sortBy:"string"==typeof e.sortBy?e.sortBy:void 0,sortOrder:"ascending"===i||"descending"===i?i:void 0}}getConfigApiData(){const s=this.scimPrefix;let e;if(this.server.loadedPlugins.static){const s=this.config.static_name||"static";e={user:`/${s}/schemas/scim/User.json`,group:`/${s}/schemas/scim/Group.json`,defaultMapping:`/${s}/schemas/scim/default-mapping.json`}}const i=this.users.userMapping,t=this.groups.groupMapping,o=i.entries.map(s=>s.scim).concat(["id","active"]),r=t.entries.map(s=>s.scim).concat(["id"]);return{enabled:!0,version:"2.0",prefix:s,endpoints:{users:`${s}/Users`,user:`${s}/Users/:id`,groups:`${s}/Groups`,group:`${s}/Groups/:id`,bulk:`${s}/Bulk`,serviceProviderConfig:`${s}/ServiceProviderConfig`,resourceTypes:`${s}/ResourceTypes`,schemas:`${s}/Schemas`},schemaUrls:e,capabilities:{patch:!0,bulk:!0,filter:!0,sort:!0,etag:Boolean(this.config.scim_etag),changePassword:!1,maxResults:this.config.scim_max_results||200,maxScanned:this.config.scim_max_scanned||1e4,bulkMaxOperations:this.config.scim_bulk_max_operations||100,bulkMaxPayloadSize:this.config.scim_bulk_max_payload_size||1048576},resourceTypes:[{id:"User",endpoint:`${s}/Users`,schema:"urn:ietf:params:scim:schemas:core:2.0:User",rdnAttribute:this.config.scim_user_rdn_attribute||"uid",objectClass:this.config.scim_user_object_class,filterableAttributes:o,mapping:i.entries},{id:"Group",endpoint:`${s}/Groups`,schema:"urn:ietf:params:scim:schemas:core:2.0:Group",rdnAttribute:this.config.scim_group_rdn_attribute||"cn",objectClass:this.config.scim_group_object_class,filterableAttributes:r,mapping:t.entries}],idStrategy:this.config.scim_id_attribute||"rdn",baseResolution:{userBaseTemplate:this.config.scim_user_base_template||void 0,groupBaseTemplate:this.config.scim_group_base_template||void 0,hasBaseMap:Boolean(this.config.scim_base_map)}}}}export{Scim as default,scimBodyParser};
|