ldap-rest 0.3.4 → 0.3.5

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.
@@ -1 +1 @@
1
- import t from"../../abstract/plugin.js";import{escapeDnValue as e}from"../../lib/utils.js";class AppAccountsConsistency extends t{constructor(t){if(super(t),this.name="appAccountsConsistency",this.roles=["consistency"],this.dependencies={onLdapChange:"core/ldap/onChange"},this.hooks={onLdapMailChange:async(t,e,i)=>{try{const a=null!=e?Array.isArray(e)?String(e[0]):String(e):null,s=null!=i?Array.isArray(i)?String(i[0]):String(i):null;if(!a&&s)return void await this.createApplicativeAccount(t,s);if(a&&!s)return void await this.deleteApplicativeAccount(a);if(a&&s&&a!==s)return void await this.updateApplicativeAccount(t,a,s)}catch(e){this.logger.error(`${this.name}: Failed to handle mail change for ${t}:`,e)}}},this.mailAttr=this.config.mail_attribute||"mail",this.applicativeAccountBase=this.config.applicative_account_base,this.operationalAttributes=this.config.ldap_operational_attributes||[],!this.applicativeAccountBase)throw new Error(`${this.name}: applicative_account_base configuration is required`);this.logger.info(`${this.name}: initialized with applicative_account_base=${this.applicativeAccountBase}`)}shouldExcludeAttribute(t){return this.operationalAttributes.includes(t)}async createApplicativeAccount(t,i){const a=`uid=${e(i)},${this.applicativeAccountBase}`;try{const e=await this.server.ldap.search({scope:"base",paged:!1},t),s=e.searchEntries?.[0];if(!s)return void this.logger.warn(`${this.name}: Could not find user ${t} to create applicative account`);const c={};for(const[t,e]of Object.entries(s))this.shouldExcludeAttribute(t)||null!=e&&(Array.isArray(e)&&0===e.length||(c[t]=e));c.uid=i,await this.server.ldap.add(a,c),this.logger.info(`${this.name}: Created applicative account ${a} for user ${t}`)}catch(e){if(68===e.code||e.message?.includes("AlreadyExists"))return void this.logger.debug(`${this.name}: Applicative account ${a} already exists`);this.logger.error(`${this.name}: Failed to create applicative account for ${t}:`,e)}}async deleteApplicativeAccount(t){try{const e=`(${this.mailAttr}=${t})`,i=(await this.server.ldap.search({filter:e,scope:"sub",paged:!1},this.applicativeAccountBase)).searchEntries||[];if(0===i.length)return void this.logger.debug(`${this.name}: No applicative accounts found for mail ${t}`);for(const t of i){const e=t.dn;try{await this.server.ldap.delete(e),this.logger.info(`${this.name}: Deleted applicative account ${e}`)}catch(t){32===t.code||t.message?.includes("NoSuchObject")?this.logger.debug(`${this.name}: Applicative account ${e} already deleted`):this.logger.error(`${this.name}: Failed to delete applicative account ${e}:`,t)}}}catch(e){if(32===e.code||e.message?.includes("NoSuchObject"))return void this.logger.debug(`${this.name}: Applicative account base does not exist or no accounts found for mail ${t}`);this.logger.error(`${this.name}: Failed to delete applicative account for mail ${t}:`,e)}}async updateApplicativeAccount(t,i,a){try{const s=`(${this.mailAttr}=${i})`,c=(await this.server.ldap.search({filter:s,scope:"sub",paged:!1},this.applicativeAccountBase)).searchEntries||[];if(0===c.length)return void this.logger.debug(`${this.name}: No applicative account found for old mail ${i}`);for(const s of c){const c=s.dn,r=Array.isArray(s.uid)?String(s.uid[0]):String(s.uid),o=r===i;let n;n=o?`uid=${e(a)},${this.applicativeAccountBase}`:`uid=${e(r)},${this.applicativeAccountBase}`;try{const e={};for(const[t,i]of Object.entries(s))this.shouldExcludeAttribute(t)||null!=i&&(Array.isArray(i)&&0===i.length||(e[t]=i));try{await this.server.ldap.delete(c),this.logger.info(`${this.name}: Deleted old applicative account ${c}`)}catch(t){if(32!==t.code&&!t.message?.includes("NoSuchObject"))throw t;this.logger.debug(`${this.name}: Applicative account ${c} already deleted`)}const l={...e},u=await this.server.ldap.search({scope:"base",paged:!1},t),d=u.searchEntries?.[0];if(!d)return void this.logger.warn(`${this.name}: Could not find user ${t} to update applicative account`);for(const[t,e]of Object.entries(d))this.shouldExcludeAttribute(t)||null!=e&&(Array.isArray(e)&&0===e.length||(l[t]=e));l.uid=o?a:r,await this.server.ldap.add(n,l),this.logger.info(`${this.name}: Created new applicative account ${n} (mail changed from ${i} to ${a})`)}catch(t){if(68===t.code||t.message?.includes("AlreadyExists"))return void this.logger.debug(`${this.name}: Applicative account ${n} already exists after mail change`);this.logger.error(`${this.name}: Failed to update applicative account for mail change:`,t)}}}catch(e){this.logger.error(`${this.name}: Failed to update applicative account for ${t}:`,e)}}}export{AppAccountsConsistency as default};
1
+ import t from"../../abstract/plugin.js";import{escapeDnValue as e}from"../../lib/utils.js";class AppAccountsConsistency extends t{constructor(t){if(super(t),this.name="appAccountsConsistency",this.roles=["consistency"],this.dependencies={onLdapChange:"core/ldap/onChange"},this.hooks={onLdapMailChange:async(t,e,i)=>{try{const a=null!=e?Array.isArray(e)?String(e[0]):String(e):null,c=null!=i?Array.isArray(i)?String(i[0]):String(i):null;if(!a&&c)return void await this.createApplicativeAccount(t,c);if(a&&!c)return void await this.deleteApplicativeAccount(a);if(a&&c&&a!==c)return void await this.updateApplicativeAccount(t,a,c)}catch(e){this.logger.error(`${this.name}: Failed to handle mail change for ${t}:`,e)}}},this.mailAttr=this.config.mail_attribute||"mail",this.applicativeAccountBase=this.config.applicative_account_base,this.operationalAttributes=this.config.ldap_operational_attribute||[],!this.applicativeAccountBase)throw new Error(`${this.name}: applicative_account_base configuration is required`);this.logger.info(`${this.name}: initialized with applicative_account_base=${this.applicativeAccountBase}`)}shouldExcludeAttribute(t){return"dn"===t||this.operationalAttributes.includes(t)}async createApplicativeAccount(t,i){const a=`uid=${e(i)},${this.applicativeAccountBase}`;try{const e=await this.server.ldap.search({scope:"base",paged:!1},t),c=e.searchEntries?.[0];if(!c)return void this.logger.warn(`${this.name}: Could not find user ${t} to create applicative account`);const s={};for(const[t,e]of Object.entries(c))this.shouldExcludeAttribute(t)||null!=e&&(Array.isArray(e)&&0===e.length||(s[t]=e));s.uid=i,await this.server.ldap.add(a,s),this.logger.info(`${this.name}: Created applicative account ${a} for user ${t}`)}catch(e){if(68===e.code||e.message?.includes("AlreadyExists"))return void this.logger.debug(`${this.name}: Applicative account ${a} already exists`);this.logger.error(`${this.name}: Failed to create applicative account for ${t}:`,e)}}async deleteApplicativeAccount(t){try{const e=`(${this.mailAttr}=${t})`,i=(await this.server.ldap.search({filter:e,scope:"sub",paged:!1},this.applicativeAccountBase)).searchEntries||[];if(0===i.length)return void this.logger.debug(`${this.name}: No applicative accounts found for mail ${t}`);for(const t of i){const e=t.dn;try{await this.server.ldap.delete(e),this.logger.info(`${this.name}: Deleted applicative account ${e}`)}catch(t){32===t.code||t.message?.includes("NoSuchObject")?this.logger.debug(`${this.name}: Applicative account ${e} already deleted`):this.logger.error(`${this.name}: Failed to delete applicative account ${e}:`,t)}}}catch(e){if(32===e.code||e.message?.includes("NoSuchObject"))return void this.logger.debug(`${this.name}: Applicative account base does not exist or no accounts found for mail ${t}`);this.logger.error(`${this.name}: Failed to delete applicative account for mail ${t}:`,e)}}async updateApplicativeAccount(t,i,a){try{const c=`(${this.mailAttr}=${i})`,s=(await this.server.ldap.search({filter:c,scope:"sub",paged:!1},this.applicativeAccountBase)).searchEntries||[];if(0===s.length)return void this.logger.debug(`${this.name}: No applicative account found for old mail ${i}`);for(const c of s){const s=c.dn,r=Array.isArray(c.uid)?String(c.uid[0]):String(c.uid),o=r===i;let n;n=o?`uid=${e(a)},${this.applicativeAccountBase}`:`uid=${e(r)},${this.applicativeAccountBase}`;try{const e={};for(const[t,i]of Object.entries(c))this.shouldExcludeAttribute(t)||null!=i&&(Array.isArray(i)&&0===i.length||(e[t]=i));try{await this.server.ldap.delete(s),this.logger.info(`${this.name}: Deleted old applicative account ${s}`)}catch(t){if(32!==t.code&&!t.message?.includes("NoSuchObject"))throw t;this.logger.debug(`${this.name}: Applicative account ${s} already deleted`)}const l={...e},u=await this.server.ldap.search({scope:"base",paged:!1},t),d=u.searchEntries?.[0];if(!d)return void this.logger.warn(`${this.name}: Could not find user ${t} to update applicative account`);for(const[t,e]of Object.entries(d))this.shouldExcludeAttribute(t)||null!=e&&(Array.isArray(e)&&0===e.length||(l[t]=e));l.uid=o?a:r,await this.server.ldap.add(n,l),this.logger.info(`${this.name}: Created new applicative account ${n} (mail changed from ${i} to ${a})`)}catch(t){if(68===t.code||t.message?.includes("AlreadyExists"))return void this.logger.debug(`${this.name}: Applicative account ${n} already exists after mail change`);this.logger.error(`${this.name}: Failed to update applicative account for mail change:`,t)}}}catch(e){this.logger.error(`${this.name}: Failed to update applicative account for ${t}:`,e)}}}export{AppAccountsConsistency as default};
@@ -1 +1 @@
1
- import{Buffer as t}from"buffer";import e from"node-fetch";import i from"../../abstract/plugin.js";class CozyProvision extends i{constructor(t){super(t),this.name="cozyProvision",this.roles=["consistency"],this.publisher=null,this.publisherInit=null,this.hooks={scimusercreatedone:async t=>{await this.createCozyInstance(t)&&await this.publishUserCreated(t)},scimuserdeletedone:async t=>{await this.publishUserDeleted(t)}},this.cozyAdminUrl=(this.config.cozy_admin_url||"").replace(/\/$/,""),this.cozyAdminUser=this.config.cozy_admin_user||"admin",this.cozyAdminPassphrase=this.config.cozy_admin_passphrase||"",this.cozyOrgId=this.config.cozy_org_id||"",this.cozyOrgDomain=this.config.cozy_org_domain||"",this.cozyDefaultLocale=this.config.cozy_default_locale||"fr",this.cozyContextName=this.config.cozy_context_name||"default",this.cozyApps=this.config.cozy_apps??"",this.rabbitmqUrl=this.config.rabbitmq_url||"",this.authExchange=this.config.cozy_auth_exchange||"auth",this.b2bExchange=this.config.cozy_b2b_exchange||"b2b",this.cozyAdminUrl||this.logger.warn(`${this.name}: cozy_admin_url is empty — Cozy instance creation will be skipped`),this.cozyOrgDomain||this.logger.warn(`${this.name}: cozy_org_domain is empty — workplaceFqdn cannot be composed`),this.rabbitmqUrl||this.logger.warn(`${this.name}: rabbitmq_url is empty — AMQP publishes will be skipped`),this.registerShutdown()}async createCozyInstance(i){if(!this.cozyAdminUrl)return!1;const r=this.extractId(i);if(!r)return this.logger.warn({plugin:this.name,event:"createCozyInstance",message:"user has no userName/id — skipping"}),!1;if(!this.cozyOrgDomain)return this.logger.error({plugin:this.name,event:"createCozyInstance",id:r,message:"cozy_org_domain not configured — cannot compose Domain"}),!1;const s=`${r}.${this.cozyOrgDomain}`,n=new URLSearchParams;n.set("Domain",s),n.set("Locale",this.extractLocale(i));const a=this.extractPrimaryEmail(i);a&&n.set("Email",a);const o=this.extractPublicName(i);o&&n.set("PublicName",o);const c=this.extractPrimaryPhone(i);c&&n.set("Phone",c),this.cozyOrgId&&n.set("OrgID",this.cozyOrgId),this.cozyOrgDomain&&n.set("OrgDomain",this.cozyOrgDomain),this.cozyContextName&&n.set("ContextName",this.cozyContextName),this.cozyApps&&n.set("Apps",this.cozyApps);const h=`${this.cozyAdminUrl}/instances?${n.toString()}`,l=t.from(`${this.cozyAdminUser}:${this.cozyAdminPassphrase}`).toString("base64"),u={plugin:this.name,event:"createCozyInstance",id:r,domain:s,email:a||void 0};try{const t=await e(h,{method:"POST",headers:{Authorization:`Basic ${l}`,Accept:"application/json"}});return t.ok?(this.logger.info({...u,result:"success",http_status:t.status}),await this.markInstanceOnboarded(s),!0):409===t.status?(this.logger.info({...u,result:"already_exists",http_status:t.status}),!0):(this.logger.error({...u,result:"error",http_status:t.status,http_status_text:t.statusText}),!1)}catch(t){return this.logger.error({...u,result:"error",error:`${t}`}),!1}}async markInstanceOnboarded(i){const r=`${this.cozyAdminUrl}/instances/${encodeURIComponent(i)}?OnboardingFinished=true`,s=t.from(`${this.cozyAdminUser}:${this.cozyAdminPassphrase}`).toString("base64"),n={plugin:this.name,event:"markInstanceOnboarded",domain:i};try{const t=await e(r,{method:"PATCH",headers:{Authorization:`Basic ${s}`,Accept:"application/json"}});if(!t.ok)return void this.logger.error({...n,result:"error",http_status:t.status,http_status_text:t.statusText});this.logger.info({...n,result:"success",http_status:t.status})}catch(t){this.logger.error({...n,result:"error",error:`${t}`})}}async publishUserCreated(t){const e=await this.getPublisher();if(!e)return;const i=this.extractId(t);if(!i)return;const r=this.extractPrimaryEmail(t),s=this.extractPrimaryPhone(t),n={twakeId:i,domain:this.cozyOrgDomain,organizationDomain:this.cozyOrgDomain,workplaceFqdn:`${i}.${this.cozyOrgDomain}`};r&&(n.internalEmail=r),s&&(n.mobile=s);try{await e.publish(this.authExchange,"user.created",n),this.logger.info({plugin:this.name,event:"publishUserCreated",result:"success",exchange:this.authExchange,routingKey:"user.created",twakeId:i})}catch(t){this.logger.error({plugin:this.name,event:"publishUserCreated",twakeId:i,error:`${t}`})}}async publishUserDeleted(t){const e=await this.getPublisher();if(!e)return;if(!this.cozyOrgDomain)return void this.logger.error({plugin:this.name,event:"publishUserDeleted",id:t,message:"cozy_org_domain not configured — cannot compose workplaceFqdn"});const i={workplaceFqdn:`${t}.${this.cozyOrgDomain}`,domain:this.cozyOrgDomain};try{await e.publish(this.b2bExchange,"domain.user.deleted",i),this.logger.info({plugin:this.name,event:"publishUserDeleted",result:"success",exchange:this.b2bExchange,routingKey:"domain.user.deleted",workplaceFqdn:i.workplaceFqdn})}catch(e){this.logger.error({plugin:this.name,event:"publishUserDeleted",id:t,error:`${e}`})}}async getPublisher(){return this.publisher?this.publisher:this.rabbitmqUrl?(this.publisherInit||(this.publisherInit=(async()=>{try{const t=new((await import("@linagora/rabbitmq-client")).RabbitMQClient)({url:this.rabbitmqUrl});return await t.init(),this.publisher=t,this.logger.info(`${this.name}: connected to RabbitMQ at ${function(t){try{const e=new URL(t);return e.username="",e.password="",e.toString()}catch{return"amqp://[broker]"}}(this.rabbitmqUrl)}`),t}catch(t){return this.logger.warn({plugin:this.name,event:"rabbitmq_init",message:"@linagora/rabbitmq-client unavailable or broker unreachable — AMQP publishes disabled",error:`${t}`}),null}})()),this.publisherInit):null}extractId(t){return"string"==typeof t.userName&&t.userName.length>0?t.userName:"string"==typeof t.id&&t.id.length>0?t.id:null}extractPrimaryEmail(t){const e=t.emails;if(!e||0===e.length)return null;const i=(e.find(t=>t.primary)||e[0]).value;return"string"==typeof i&&i.length>0?i:null}extractPrimaryPhone(t){const e=t.phoneNumbers;if(!e||0===e.length)return null;const i=(e.find(t=>t.primary)||e[0]).value;return"string"==typeof i&&i.length>0?i:null}extractPublicName(t){if("string"==typeof t.displayName&&t.displayName.trim().length>0)return t.displayName.trim();const e=t.name?.formatted;if("string"==typeof e&&e.trim().length>0)return e.trim();const i=`${t.name?.givenName?.trim()??""} ${t.name?.familyName?.trim()??""}`.trim();return i.length>0?i:this.extractId(t)}extractLocale(t){return"string"==typeof t.locale&&t.locale.length>0?t.locale:"string"==typeof t.preferredLanguage&&t.preferredLanguage.length>0?t.preferredLanguage:this.cozyDefaultLocale}registerShutdown(){!function(t){if(r.push(t),s)return;s=!0;const fanOut=()=>{for(const t of r.splice(0))try{t()}catch{}};process.once("SIGTERM",fanOut),process.once("SIGINT",fanOut)}(()=>{const t=this.publisher;this.publisher=null,this.publisherInit=null,t&&t.close().catch(t=>{this.logger.warn({plugin:this.name,event:"shutdown",error:`${t}`})})})}}const r=[];let s=!1;export{CozyProvision as default};
1
+ import{Buffer as t}from"buffer";import e from"node-fetch";import r from"../../abstract/plugin.js";class CozyProvision extends r{constructor(e){super(e),this.name="cozyProvision",this.roles=["consistency"],this.publisher=null,this.publisherInit=null,this.hooks={scimusercreatedone:async t=>{await this.createCozyInstance(t)&&await this.publishUserCreated(t)},scimuserdeletedone:async t=>{await this.destroyCozyInstance(t)&&await this.publishUserDeleted(t)}},this.cozyAdminUrl=(this.config.cozy_admin_url||"").replace(/\/$/,""),this.cozyAdminUser=this.config.cozy_admin_user||"admin",this.cozyAdminPassphrase=this.config.cozy_admin_passphrase||"",this.cozyOrgId=this.config.cozy_org_id||"",this.cozyOrgDomain=this.config.cozy_org_domain||"",this.cozyDefaultLocale=this.config.cozy_default_locale||"fr",this.cozyContextName=this.config.cozy_context_name||"default",this.cozyApps=this.config.cozy_apps??"",this.rabbitmqUrl=this.config.rabbitmq_url||"",this.authExchange=this.config.cozy_auth_exchange||"auth",this.b2bExchange=this.config.cozy_b2b_exchange||"b2b",this.cozyAdminAuthHeader=`Basic ${t.from(`${this.cozyAdminUser}:${this.cozyAdminPassphrase}`).toString("base64")}`,this.cozyAdminUrl||this.logger.warn(`${this.name}: cozy_admin_url is empty — Cozy instance creation will be skipped`),this.cozyOrgDomain||this.logger.warn(`${this.name}: cozy_org_domain is empty — workplaceFqdn cannot be composed`),this.rabbitmqUrl||this.logger.warn(`${this.name}: rabbitmq_url is empty — AMQP publishes will be skipped`),this.registerShutdown()}async createCozyInstance(t){if(!this.cozyAdminUrl)return!1;const r=this.extractId(t);if(!r)return this.logger.warn({plugin:this.name,event:"createCozyInstance",message:"user has no userName/id — skipping"}),!1;if(!this.cozyOrgDomain)return this.logger.error({plugin:this.name,event:"createCozyInstance",id:r,message:"cozy_org_domain not configured — cannot compose Domain"}),!1;const s=`${r}.${this.cozyOrgDomain}`,i=new URLSearchParams;i.set("Domain",s),i.set("Locale",this.extractLocale(t));const n=this.extractPrimaryEmail(t);n&&i.set("Email",n);const o=this.extractPublicName(t);o&&i.set("PublicName",o);const a=this.extractPrimaryPhone(t);a&&i.set("Phone",a),this.cozyOrgId&&i.set("OrgID",this.cozyOrgId),this.cozyOrgDomain&&i.set("OrgDomain",this.cozyOrgDomain),this.cozyContextName&&i.set("ContextName",this.cozyContextName),this.cozyApps&&i.set("Apps",this.cozyApps),i.set("OIDCID",r);const c=`${this.cozyAdminUrl}/instances?${i.toString()}`,h={plugin:this.name,event:"createCozyInstance",id:r,domain:s,email:n||void 0};try{const t=await e(c,{method:"POST",headers:{Authorization:this.cozyAdminAuthHeader,Accept:"application/json"}});return t.ok?(this.logger.info({...h,result:"success",http_status:t.status}),await this.markInstanceOnboarded(s),!0):409===t.status?(this.logger.info({...h,result:"already_exists",http_status:t.status}),!0):(this.logger.error({...h,result:"error",http_status:t.status,http_status_text:t.statusText}),!1)}catch(t){return this.logger.error({...h,result:"error",error:`${t}`}),!1}}async markInstanceOnboarded(t){const r=`${this.cozyAdminUrl}/instances/${encodeURIComponent(t)}?OnboardingFinished=true`,s={plugin:this.name,event:"markInstanceOnboarded",domain:t};try{const t=await e(r,{method:"PATCH",headers:{Authorization:this.cozyAdminAuthHeader,Accept:"application/json"}});if(!t.ok)return void this.logger.error({...s,result:"error",http_status:t.status,http_status_text:t.statusText});this.logger.info({...s,result:"success",http_status:t.status})}catch(t){this.logger.error({...s,result:"error",error:`${t}`})}}async destroyCozyInstance(t){if(!this.cozyAdminUrl)return!1;if(!this.cozyOrgDomain)return this.logger.error({plugin:this.name,event:"destroyCozyInstance",id:t,message:"cozy_org_domain not configured — cannot compose Domain"}),!1;const r=`${t}.${this.cozyOrgDomain}`,s=`${this.cozyAdminUrl}/instances/${encodeURIComponent(r)}`,i={plugin:this.name,event:"destroyCozyInstance",id:t,domain:r};try{const t=await e(s,{method:"DELETE",headers:{Authorization:this.cozyAdminAuthHeader,Accept:"application/json"}});return t.ok||404===t.status?(this.logger.info({...i,result:t.ok?"success":"not_found",http_status:t.status}),!0):(this.logger.error({...i,result:"error",http_status:t.status,http_status_text:t.statusText}),!1)}catch(t){return this.logger.error({...i,result:"error",error:`${t}`}),!1}}async publishUserCreated(t){const e=await this.getPublisher();if(!e)return;const r=this.extractId(t);if(!r)return;const s=this.extractPrimaryEmail(t),i=this.extractPrimaryPhone(t),n={twakeId:r,domain:this.cozyOrgDomain,organizationDomain:this.cozyOrgDomain,workplaceFqdn:`${r}.${this.cozyOrgDomain}`};s&&(n.internalEmail=s),i&&(n.mobile=i);try{await e.publish(this.authExchange,"user.created",n),this.logger.info({plugin:this.name,event:"publishUserCreated",result:"success",exchange:this.authExchange,routingKey:"user.created",twakeId:r})}catch(t){this.logger.error({plugin:this.name,event:"publishUserCreated",twakeId:r,error:`${t}`})}}async publishUserDeleted(t){const e=await this.getPublisher();if(!e)return;if(!this.cozyOrgDomain)return void this.logger.error({plugin:this.name,event:"publishUserDeleted",id:t,message:"cozy_org_domain not configured — cannot compose workplaceFqdn"});const r={workplaceFqdn:`${t}.${this.cozyOrgDomain}`,domain:this.cozyOrgDomain};try{await e.publish(this.b2bExchange,"domain.user.deleted",r),this.logger.info({plugin:this.name,event:"publishUserDeleted",result:"success",exchange:this.b2bExchange,routingKey:"domain.user.deleted",workplaceFqdn:r.workplaceFqdn})}catch(e){this.logger.error({plugin:this.name,event:"publishUserDeleted",id:t,error:`${e}`})}}async getPublisher(){return this.publisher?this.publisher:this.rabbitmqUrl?(this.publisherInit||(this.publisherInit=(async()=>{try{const t=new((await import("@linagora/rabbitmq-client")).RabbitMQClient)({url:this.rabbitmqUrl});return await t.init(),this.publisher=t,this.logger.info(`${this.name}: connected to RabbitMQ at ${function(t){try{const e=new URL(t);return e.username="",e.password="",e.toString()}catch{return"amqp://[broker]"}}(this.rabbitmqUrl)}`),t}catch(t){return this.logger.warn({plugin:this.name,event:"rabbitmq_init",message:"@linagora/rabbitmq-client unavailable or broker unreachable — AMQP publishes disabled",error:`${t}`}),null}})()),this.publisherInit):null}extractId(t){return"string"==typeof t.userName&&t.userName.length>0?t.userName:"string"==typeof t.id&&t.id.length>0?t.id:null}extractPrimaryEmail(t){const e=t.emails;if(!e||0===e.length)return null;const r=(e.find(t=>t.primary)||e[0]).value;return"string"==typeof r&&r.length>0?r:null}extractPrimaryPhone(t){const e=t.phoneNumbers;if(!e||0===e.length)return null;const r=(e.find(t=>t.primary)||e[0]).value;return"string"==typeof r&&r.length>0?r:null}extractPublicName(t){if("string"==typeof t.displayName&&t.displayName.trim().length>0)return t.displayName.trim();const e=t.name?.formatted;if("string"==typeof e&&e.trim().length>0)return e.trim();const r=`${t.name?.givenName?.trim()??""} ${t.name?.familyName?.trim()??""}`.trim();return r.length>0?r:this.extractId(t)}extractLocale(t){return"string"==typeof t.locale&&t.locale.length>0?t.locale:"string"==typeof t.preferredLanguage&&t.preferredLanguage.length>0?t.preferredLanguage:this.cozyDefaultLocale}registerShutdown(){!function(t){if(s.push(t),i)return;i=!0;const fanOut=()=>{for(const t of s.splice(0))try{t()}catch{}};process.once("SIGTERM",fanOut),process.once("SIGINT",fanOut)}(()=>{const t=this.publisher;this.publisher=null,this.publisherInit=null,t&&t.close().catch(t=>{this.logger.warn({plugin:this.name,event:"shutdown",error:`${t}`})})})}}const s=[];let i=!1;export{CozyProvision as default};
@@ -133,7 +133,7 @@ export interface Config {
133
133
  rabbitmq_url?: string;
134
134
  applicative_account_base?: string;
135
135
  max_app_accounts?: number;
136
- ldap_operational_attributes?: string[];
136
+ ldap_operational_attribute?: string[];
137
137
  trash_base?: string;
138
138
  trash_watched_bases?: string;
139
139
  trash_add_metadata?: string;
@@ -20,6 +20,7 @@ export default class CozyProvision extends DmPlugin {
20
20
  private readonly rabbitmqUrl;
21
21
  private readonly authExchange;
22
22
  private readonly b2bExchange;
23
+ private readonly cozyAdminAuthHeader;
23
24
  private publisher;
24
25
  private publisherInit;
25
26
  constructor(server: DM);
@@ -38,6 +39,15 @@ export default class CozyProvision extends DmPlugin {
38
39
  * the user gets dropped onto a setup wizard at first OIDC login.
39
40
  */
40
41
  private markInstanceOnboarded;
42
+ /**
43
+ * DELETE /instances/<domain> on the cozy-stack admin API. The SCIM delete
44
+ * hook only removed the LDAP entry; without this, a subsequent re-import
45
+ * of the same userName re-attaches to a stale cozy instance whose
46
+ * per-user state (oidc_id, sharings, contacts, ...) no longer matches
47
+ * the operator's intent. A 404 from cozy-stack is treated as success so
48
+ * the lifecycle stays idempotent.
49
+ */
50
+ private destroyCozyInstance;
41
51
  /**
42
52
  * Publish user.created on the `auth` topic exchange. Payload matches what
43
53
  * cozy-stack's stack.user.created consumer expects: a `twakeId` identifier,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ldap-rest",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Light directory manager",
5
5
  "keywords": [
6
6
  "Identity-Manager"