ldap-rest 0.3.4 → 0.3.6
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/abstract/plugin.js +1 -1
- package/dist/config/args.js +1 -1
- package/dist/plugins/rabbitmq.js +1 -0
- package/dist/plugins/scim/baseResolver.js +1 -1
- package/dist/plugins/twake/appAccountsConsistency.js +1 -1
- package/dist/plugins/twake/clouderyProvision.js +1 -0
- package/dist/plugins/twake/cozyProvision.js +1 -1
- package/dist/src/abstract/plugin.d.ts +10 -0
- package/dist/src/config/args.d.ts +15 -1
- package/dist/src/plugins/rabbitmq.d.ts +94 -0
- package/dist/src/plugins/scim/baseResolver.d.ts +5 -0
- package/dist/src/plugins/twake/clouderyProvision.d.ts +61 -0
- package/dist/src/plugins/twake/cozyProvision.d.ts +17 -16
- package/package.json +9 -1
package/dist/abstract/plugin.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{asyncHandler,escapeDnValue,escapeLdapFilter,unescapeDnValue,validateDnValue}from"../lib/utils.js";export{BadGatewayError,BadRequestError,ConflictError,ForbiddenError,GatewayTimeoutError,HttpError,NotFoundError,ServiceUnavailableError,TooManyRequestsError,UnauthorizedError,UriTooLongError}from"../lib/errors.js";class DmPlugin{constructor(r){this.registeredHooks={},this.server=r,this.config=r.config,this.logger=r.logger,this.registeredHooks=r.hooks}opNumber(){return this.server.operationSequence++}}export{DmPlugin as default};
|
|
1
|
+
export{asyncHandler,escapeDnValue,escapeLdapFilter,unescapeDnValue,validateDnValue}from"../lib/utils.js";export{BadGatewayError,BadRequestError,ConflictError,ForbiddenError,GatewayTimeoutError,HttpError,NotFoundError,ServiceUnavailableError,TooManyRequestsError,UnauthorizedError,UriTooLongError}from"../lib/errors.js";class DmPlugin{constructor(r){this.registeredHooks={},this.server=r,this.config=r.config,this.logger=r.logger,this.registeredHooks=r.hooks}opNumber(){return this.server.operationSequence++}requirePlugin(r){const e=this.server.loadedPlugins[r];return e||(this.missingPluginsWarned||(this.missingPluginsWarned=new Set),this.missingPluginsWarned.has(r)||(this.missingPluginsWarned.add(r),this.logger.warn(`${this.name}: required plugin '${r}' is not loaded — its features will be skipped`)),null)}}export{DmPlugin as default};
|
package/dist/config/args.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fileURLToPath as _}from"url";import{join as a,dirname as e}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",300,"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"],["--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"],["--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",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"],["--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"],["--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",""],["--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"],["--rabbitmq-url","DM_RABBITMQ_URL",""],["--applicative-account-base","DM_APPLICATIVE_ACCOUNT_BASE",""],["--max-app-accounts","DM_MAX_APP_ACCOUNTS",5,"number"],["--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"],["--auth-token","DM_AUTH_TOKENS",[],"array","--auth-tokens"],["--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-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-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-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-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-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-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-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 _}from"url";import{join as a,dirname as e}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",300,"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"],["--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"],["--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",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"],["--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"],["--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",""],["--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"],["--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-fqdn-attribute","DM_CLOUDERY_FQDN_ATTRIBUTE","twakeWorkspaceUrl"],["--cloudery-org-id-attribute","DM_CLOUDERY_ORG_ID_ATTRIBUTE","twakeOrganizationId"],["--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"],["--rabbitmq-url","DM_RABBITMQ_URL",""],["--applicative-account-base","DM_APPLICATIVE_ACCOUNT_BASE",""],["--max-app-accounts","DM_MAX_APP_ACCOUNTS",5,"number"],["--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"],["--auth-token","DM_AUTH_TOKENS",[],"array","--auth-tokens"],["--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-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-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-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-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-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-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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"../abstract/plugin.js";class RabbitMq extends t{constructor(t){super(t),this.name="rabbitmq",this.client=null,this.clientInit=null,this.rabbitmqUrl=this.config.rabbitmq_url||"",this.rabbitmqUrl||this.logger.warn(`${this.name}: rabbitmq_url is empty — AMQP publishes/subscribes will be skipped`),this.registerShutdown()}isAvailable(){return this.rabbitmqUrl.length>0}async publish(t,i,n,e){const s=await this.getClient();s&&await s.publish(t,i,n,e)}async subscribe(t,i,n,e,s){const r=await this.getClient();r&&await r.subscribe(t,i,n,e,s)}async unsubscribe(t){const i=await this.getClient();i&&await i.unsubscribe(t)}async getRawClient(){return this.getClient()}async getClient(){return this.client?this.client:this.rabbitmqUrl?(this.clientInit||(this.clientInit=(async()=>{try{const t=await this.connect();return this.client=t,this.logger.info(`${this.name}: connected to RabbitMQ at ${function(t){try{const i=new URL(t);return i.username="",i.password="",i.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 disabled",error:`${t}`}),this.clientInit=null,null}})()),this.clientInit):null}async connect(){const t=new((await import("@linagora/rabbitmq-client")).RabbitMQClient)({url:this.rabbitmqUrl});return await t.init(),t}registerShutdown(){!function(t){if(i.push(t),n)return;n=!0;const fanOut=()=>{for(const t of i.splice(0))try{t()}catch{}};process.once("SIGTERM",fanOut),process.once("SIGINT",fanOut)}(()=>{const t=this.client;this.client=null,this.clientInit=null,t&&t.close().catch(t=>{this.logger.warn({plugin:this.name,event:"shutdown",error:`${t}`})})})}}const i=[];let n=!1;export{RabbitMq as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"fs";import{escapeDnValue as s}from"../../lib/utils.js";class BaseResolver{constructor(s){const r=s.ldap_base||"";this.defaultUserBase=s.scim_user_base||r,this.defaultGroupBase=s.scim_group_base||r,this.userTemplate=s.scim_user_base_template||"",this.groupTemplate=s.scim_group_base_template||"";const t=s.scim_base_map||"";if(t)try{const s=e.readFileSync(t,"utf8");this.map=JSON.parse(s)}catch(e){throw new Error(`Failed to load SCIM base map from ${t}: ${e instanceof Error?e.message:String(e)}`)}}applyTemplate(e,r){const t=r?s(r):"";return e.replace(/\{user\}/g,t)}
|
|
1
|
+
import e from"fs";import{escapeDnValue as s,isChildOf as r}from"../../lib/utils.js";class BaseResolver{constructor(s){const r=s.ldap_base||"";this.defaultUserBase=s.scim_user_base||r,this.defaultGroupBase=s.scim_group_base||r,this.userTemplate=s.scim_user_base_template||"",this.groupTemplate=s.scim_group_base_template||"",this.userBaseHeader=(s.scim_user_base_header||"").toLowerCase(),this.groupBaseHeader=(s.scim_group_base_header||"").toLowerCase(),this.headerRoot=s.scim_base_header_root||"";const t=s.scim_base_map||"";if(t)try{const s=e.readFileSync(t,"utf8");this.map=JSON.parse(s)}catch(e){throw new Error(`Failed to load SCIM base map from ${t}: ${e instanceof Error?e.message:String(e)}`)}}applyTemplate(e,r){const t=r?s(r):"";return e.replace(/\{user\}/g,t)}readHeader(e,s){if(!e||"object"!=typeof e)return;const r=e;if("function"==typeof r.get){const e=r.get(s);if("string"==typeof e&&e.length>0)return e}const t=r.headers?.[s];return"string"==typeof t&&t.length>0?t:Array.isArray(t)&&"string"==typeof t[0]&&t[0].length>0?t[0]:void 0}resolve(e,s){const t=s&&"object"==typeof s&&"user"in s?s.user:void 0;if(this.map&&t&&this.map[t]){const s=this.map[t];if("user"===e&&s.userBase)return s.userBase;if("group"===e&&s.groupBase)return s.groupBase}const a="user"===e?this.userBaseHeader:this.groupBaseHeader;if(a&&this.headerRoot){const e=this.readHeader(s,a)?.trim();if(e&&!/[\u0000-\u001f\u007f]/.test(e)&&(e.toLowerCase()===this.headerRoot.toLowerCase()||r(e,this.headerRoot)))return e}if(this.map&&this.map["*"]){const s=this.map["*"];if("user"===e&&s.userBase)return this.applyTemplate(s.userBase,t);if("group"===e&&s.groupBase)return this.applyTemplate(s.groupBase,t)}const o="user"===e?this.userTemplate:this.groupTemplate;return o?this.applyTemplate(o,t):"user"===e?this.defaultUserBase:this.defaultGroupBase}userBase(e){return this.resolve("user",e)}groupBase(e){return this.resolve("group",e)}}export{BaseResolver};
|
|
@@ -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,
|
|
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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{setTimeout as e}from"node:timers/promises";import t from"node-fetch";import r from"../../abstract/plugin.js";import{escapeDnValue as i,isChildOf as a}from"../../lib/utils.js";import{BaseResolver as s}from"../scim/baseResolver.js";class ClouderyProvision extends r{constructor(e){super(e),this.name="clouderyProvision",this.roles=["consistency"],this.dependencies={rabbitmq:"core/rabbitmq"},this.createStash=new Map,this.deleteStash=new Map,this.hooks={scimusercreate:e=>(this.captureCreate(e[0],e[1]),e),scimusercreatedone:async e=>{await this.provision(e)},scimuserdelete:async e=>(await this.captureDelete(e[0],e[1]),e),scimuserdeletedone:async e=>{await this.deprovision(e)}};const t=this.config;this.managerUrl=(t.cloudery_manager_url||"").replace(/\/$/,""),this.managerToken=t.cloudery_manager_token||"",this.offer=t.cloudery_offer||"b2b_twake_default",this.clouderyDomain=t.cloudery_domain||"",this.userBranch=t.cloudery_user_branch||"",this.orgIdHeader=(t.cloudery_org_id_header||"x-cloudery-org-id").toLowerCase(),this.fqdnAttribute=t.cloudery_fqdn_attribute||"twakeWorkspaceUrl",this.orgIdAttribute=t.cloudery_org_id_attribute||"twakeOrganizationId",this.defaultLocale=t.cloudery_default_locale||"en",this.rdnAttribute=t.scim_user_rdn_attribute||"uid",this.authExchange=t.cozy_auth_exchange||"auth",this.b2bExchange=t.cozy_b2b_exchange||"b2b",this.pollIntervalMs=Number(t.cloudery_workflow_poll_interval_ms)||2e3,this.maxPollAttempts=Number(t.cloudery_workflow_max_attempts)||60,this.baseResolver=new s(this.config),this.managerUrl||this.logger.warn(`${this.name}: cloudery_manager_url is empty — instance provisioning will be skipped`),this.userBranch||this.logger.warn(`${this.name}: cloudery_user_branch is empty — no users will be provisioned`)}captureCreate(e,t){if(this.pruneStash(),!this.managerUrl||!this.userBranch)return;const r=this.extractId(e);if(!r)return;const i=this.baseResolver.userBase(t);if(!this.isAtOrUnder(i,this.userBranch))return;const a=this.readHeader(t,this.orgIdHeader);if(!a)return void this.logger.warn({plugin:this.name,event:"captureCreate",userName:r,message:`B2B branch create without ${this.orgIdHeader} — skipping provisioning`});const s=this.extractPrimaryEmail(e);if(!s)return void this.logger.warn({plugin:this.name,event:"captureCreate",userName:r,message:"B2B branch create without a primary email — skipping provisioning"});const n=s.trim();this.createStash.set(n.toLowerCase(),{orgId:a.trim(),email:n,base:i,ts:Date.now()})}async provision(e){const t=this.extractId(e);if(!t)return;const r=this.extractPrimaryEmail(e);if(!r)return;const a=r.trim().toLowerCase(),s=this.createStash.get(a);if(!s)return;this.createStash.delete(a);const n=s.email,o=emailDomain(n),l=t.replace(/\./g,"")+s.orgId;const c=this.extractPrimaryPhone(e),h=this.extractPublicName(e)||t,u={plugin:this.name,event:"provision",userName:t,orgId:s.orgId,slug:l};let d;try{d=await this.createInstance({email:n,publicName:h,locale:this.extractLocale(e),oidc:l,phone:c||"",slug:l,public_name:h,offer:this.offer,domain:this.clouderyDomain,skip_email_validation:!0,internal_email:n,org_domain:o,org_id:s.orgId})}catch(e){return void this.logger.error({...u,result:"error",error:`${e}`})}if(!(!d.workflow||await this.waitForWorkflow(d.workflow)))return void this.logger.error({...u,result:"workflow_failed",workflow:d.workflow});const m=d.fqdn,g=`${this.rdnAttribute}=${i(t)},${s.base}`;try{await this.server.ldap.modify(g,{replace:{[this.fqdnAttribute]:m,[this.orgIdAttribute]:s.orgId}})}catch(e){this.logger.error({...u,event:"writeFqdn",dn:g,error:`${e}`})}await this.publishUserCreated({twakeId:t,orgDomain:o,orgId:s.orgId,fqdn:m,email:n,mobile:c}),this.logger.info({...u,result:"success",fqdn:m})}async captureDelete(e,t){if(!this.managerUrl||!this.userBranch)return;const r=this.baseResolver.userBase(t);if(!this.isAtOrUnder(r,this.userBranch))return;const a=`${this.rdnAttribute}=${i(e)},${r}`;try{const t=await this.server.ldap.search({paged:!1,scope:"base",attributes:[this.fqdnAttribute,"mail"]},a),r=t.searchEntries?.[0],i=r?firstValue(r[this.fqdnAttribute]):void 0,s=i?i.trim().replace(/^[a-z][a-z0-9+.-]*:\/\//i,"").split("/")[0]:void 0;if(!s)return;const n=r?firstValue(r.mail):void 0;this.deleteStash.set(e,{fqdn:s,domain:n?emailDomain(n):""})}catch(t){this.logger.warn({plugin:this.name,event:"captureDelete",id:e,error:`${t}`})}}async deprovision(e){const t=this.deleteStash.get(e);if(!t)return;this.deleteStash.delete(e);const r={plugin:this.name,event:"deprovision",id:e,fqdn:t.fqdn};let i=!1;try{const e=await this.findInstanceUuidByFqdn(t.fqdn);e?(await this.deleteInstance(e),i=!0,this.logger.info({...r,result:"deleted",uuid:e})):this.logger.warn({...r,result:"not_found"})}catch(e){this.logger.error({...r,result:"error",error:`${e}`})}i&&await this.publishUserDeleted(t.fqdn,t.domain)}async createInstance(e){const r=await t(`${this.managerUrl}/api/v1/instances`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.managerToken}`},body:JSON.stringify(e)});if(!r.ok)throw new Error(`Cloudery createInstance HTTP ${r.status}`);return await r.json()}async waitForWorkflow(r){for(let i=1;i<=this.maxPollAttempts;i++){try{const e=await t(`${this.managerUrl}/api/v1/workflows/${encodeURIComponent(r)}`,{headers:{Authorization:`Bearer ${this.managerToken}`}});if(e.ok){const t=await e.json();if("succeeded"===t.status)return!0;if("failed"===t.status||"error"===t.status)return!1}}catch(e){this.logger.warn({plugin:this.name,event:"waitForWorkflow",workflow:r,attempt:i,error:`${e}`})}i<this.maxPollAttempts&&await e(this.pollIntervalMs)}return!1}async findInstanceUuidByFqdn(e){const r=new URL(`${this.managerUrl}/api/v2/instances`);r.searchParams.set("fqdn",e),r.searchParams.append("only","_id"),r.searchParams.set("limit","1");const i=await t(r.toString(),{headers:{Authorization:`Bearer ${this.managerToken}`}});if(!i.ok)throw new Error(`Cloudery searchInstances HTTP ${i.status}`);const a=await i.json();return a.items&&a.items.length>0?a.items[0]._id:null}async deleteInstance(e){const r=new URL(`${this.managerUrl}/api/v1/instances/${encodeURIComponent(e)}`);r.searchParams.set("user_request","true");const i=await t(r.toString(),{method:"DELETE",headers:{Authorization:`Bearer ${this.managerToken}`}});if(!i.ok)throw new Error(`Cloudery deleteInstance HTTP ${i.status}`)}async publishUserCreated(e){const t=this.requirePlugin("rabbitmq");if(!t)return;const r={twakeId:e.twakeId,domain:e.orgDomain,organizationDomain:e.orgDomain,workplaceFqdn:e.fqdn,organizationId:e.orgId};e.email&&(r.internalEmail=e.email),e.mobile&&(r.mobile=e.mobile);try{await t.publish(this.authExchange,"user.created",r)}catch(t){this.logger.error({plugin:this.name,event:"publishUserCreated",twakeId:e.twakeId,error:`${t}`})}}async publishUserDeleted(e,t){const r=this.requirePlugin("rabbitmq");if(r)try{await r.publish(this.b2bExchange,"domain.user.deleted",{workplaceFqdn:e,domain:t})}catch(t){this.logger.error({plugin:this.name,event:"publishUserDeleted",workplaceFqdn:e,error:`${t}`})}}readHeader(e,t){if(!e)return;const r=e.get?e.get(t):void 0;if("string"==typeof r&&r.length>0)return r;const i=e.query?.[t];return"string"==typeof i&&i.length>0?i:void 0}isAtOrUnder(e,t){return e.trim().toLowerCase()===t.trim().toLowerCase()||a(e,t)}pruneStash(){const e=Date.now()-3e5;for(const[t,r]of this.createStash)r.ts<e&&this.createStash.delete(t)}extractId(e){return"string"==typeof e.userName&&e.userName.length>0?e.userName:"string"==typeof e.id&&e.id.length>0?e.id:null}extractPrimaryEmail(e){const t=e.emails;if(!t||0===t.length)return null;const r=(t.find(e=>e.primary)||t[0]).value;return"string"==typeof r&&r.length>0?r:null}extractPrimaryPhone(e){const t=e.phoneNumbers;if(!t||0===t.length)return null;const r=(t.find(e=>e.primary)||t[0]).value;return"string"==typeof r&&r.length>0?r:null}extractPublicName(e){if("string"==typeof e.displayName&&e.displayName.trim().length>0)return e.displayName.trim();const t=e.name?.formatted;if("string"==typeof t&&t.trim().length>0)return t.trim();const r=`${e.name?.givenName?.trim()??""} ${e.name?.familyName?.trim()??""}`.trim();return r.length>0?r:null}extractLocale(e){return"string"==typeof e.locale&&e.locale.length>0?e.locale:"string"==typeof e.preferredLanguage&&e.preferredLanguage.length>0?e.preferredLanguage:this.defaultLocale}}function emailDomain(e){const t=e.lastIndexOf("@");return t>=0?e.slice(t+1):""}function firstValue(e){if(Array.isArray(e)){const t=e[0];return"string"==typeof t?t:void 0}return"string"==typeof e?e:void 0}export{ClouderyProvision as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Buffer as t}from"buffer";import e from"node-fetch";import
|
|
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.dependencies={rabbitmq:"core/rabbitmq"},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.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`)}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=this.rabbitmq();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=this.rabbitmq();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}`})}}rabbitmq(){return this.requirePlugin("rabbitmq")}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}}export{CozyProvision as default};
|
|
@@ -42,4 +42,14 @@ export default abstract class DmPlugin {
|
|
|
42
42
|
* @returns uniq operation number
|
|
43
43
|
*/
|
|
44
44
|
opNumber(): number;
|
|
45
|
+
/** Names already warned about by requirePlugin, so each warns at most once. */
|
|
46
|
+
private missingPluginsWarned?;
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a sibling plugin by its registry name (the value of its `name`
|
|
49
|
+
* property). Declare it in `dependencies` so it loads first. Returns null and
|
|
50
|
+
* logs a single warning if it is absent, letting the caller no-op cleanly
|
|
51
|
+
* rather than throw. Use this instead of indexing `server.loadedPlugins`
|
|
52
|
+
* directly so consumers share one typed, log-once lookup.
|
|
53
|
+
*/
|
|
54
|
+
protected requirePlugin<T extends DmPlugin>(name: string): T | null;
|
|
45
55
|
}
|
|
@@ -130,10 +130,21 @@ export interface Config {
|
|
|
130
130
|
cozy_apps?: string;
|
|
131
131
|
cozy_auth_exchange?: string;
|
|
132
132
|
cozy_b2b_exchange?: string;
|
|
133
|
+
cloudery_manager_url?: string;
|
|
134
|
+
cloudery_manager_token?: string;
|
|
135
|
+
cloudery_offer?: string;
|
|
136
|
+
cloudery_domain?: string;
|
|
137
|
+
cloudery_user_branch?: string;
|
|
138
|
+
cloudery_org_id_header?: string;
|
|
139
|
+
cloudery_fqdn_attribute?: string;
|
|
140
|
+
cloudery_org_id_attribute?: string;
|
|
141
|
+
cloudery_default_locale?: string;
|
|
142
|
+
cloudery_workflow_poll_interval_ms?: number;
|
|
143
|
+
cloudery_workflow_max_attempts?: number;
|
|
133
144
|
rabbitmq_url?: string;
|
|
134
145
|
applicative_account_base?: string;
|
|
135
146
|
max_app_accounts?: number;
|
|
136
|
-
|
|
147
|
+
ldap_operational_attribute?: string[];
|
|
137
148
|
trash_base?: string;
|
|
138
149
|
trash_watched_bases?: string;
|
|
139
150
|
trash_add_metadata?: string;
|
|
@@ -152,6 +163,9 @@ export interface Config {
|
|
|
152
163
|
scim_group_base?: string;
|
|
153
164
|
scim_user_base_template?: string;
|
|
154
165
|
scim_group_base_template?: string;
|
|
166
|
+
scim_user_base_header?: string;
|
|
167
|
+
scim_group_base_header?: string;
|
|
168
|
+
scim_base_header_root?: string;
|
|
155
169
|
scim_base_map?: string;
|
|
156
170
|
scim_user_object_class?: string[];
|
|
157
171
|
scim_user_rdn_attribute?: string;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module plugins/rabbitmq
|
|
3
|
+
*
|
|
4
|
+
* Shared RabbitMQ transport. Owns a single broker connection for the whole
|
|
5
|
+
* process and exposes publish/consume so other plugins do not each bundle
|
|
6
|
+
* (and re-open) their own AMQP client.
|
|
7
|
+
*
|
|
8
|
+
* The connection is lazy: it opens on the first publish/subscribe call, not at
|
|
9
|
+
* load time, so a broker that is down or unconfigured never blocks startup.
|
|
10
|
+
* `@linagora/rabbitmq-client` is an optional dependency; if the package is not
|
|
11
|
+
* installed or `rabbitmq_url` is empty, every method is a logged no-op.
|
|
12
|
+
*
|
|
13
|
+
* Consumers declare it as a dependency and reach it through the loaded-plugin
|
|
14
|
+
* registry:
|
|
15
|
+
*
|
|
16
|
+
* dependencies = { rabbitmq: 'core/rabbitmq' };
|
|
17
|
+
* const rmq = this.server.loadedPlugins['rabbitmq'] as RabbitMq;
|
|
18
|
+
* await rmq.publish('auth', 'user.created', message);
|
|
19
|
+
*/
|
|
20
|
+
import DmPlugin from '../abstract/plugin';
|
|
21
|
+
import type { DM } from '../bin';
|
|
22
|
+
/** JSON-serialisable message payload. */
|
|
23
|
+
export type RabbitMessage = Record<string, unknown>;
|
|
24
|
+
/** Async handler invoked for each consumed message. */
|
|
25
|
+
export type RabbitMessageHandler = (message: RabbitMessage) => Promise<void>;
|
|
26
|
+
/** Options forwarded to the underlying client's `publish()`. */
|
|
27
|
+
export interface PublishOptions {
|
|
28
|
+
maxAttempts?: number;
|
|
29
|
+
headers?: Record<string, unknown>;
|
|
30
|
+
correlationId?: string;
|
|
31
|
+
messageId?: string;
|
|
32
|
+
expiration?: string;
|
|
33
|
+
}
|
|
34
|
+
/** Options forwarded to the underlying client's `subscribe()`. */
|
|
35
|
+
export interface SubscribeOptions {
|
|
36
|
+
queueArguments?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Subset of `@linagora/rabbitmq-client`'s `RabbitMQClient` that this plugin
|
|
40
|
+
* relies on. Declared locally because the package is an optional dependency
|
|
41
|
+
* and may be absent at type-check time.
|
|
42
|
+
*/
|
|
43
|
+
interface RabbitClient {
|
|
44
|
+
init(): Promise<void>;
|
|
45
|
+
publish(exchange: string, routingKey: string, message: RabbitMessage, options?: PublishOptions): Promise<void>;
|
|
46
|
+
subscribe(exchange: string, routingKey: string, queue: string, handler: RabbitMessageHandler, options?: SubscribeOptions): Promise<void>;
|
|
47
|
+
unsubscribe(queue: string): Promise<void>;
|
|
48
|
+
close(clearSubscriptions?: boolean): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
export default class RabbitMq extends DmPlugin {
|
|
51
|
+
name: string;
|
|
52
|
+
private readonly rabbitmqUrl;
|
|
53
|
+
private client;
|
|
54
|
+
private clientInit;
|
|
55
|
+
constructor(server: DM);
|
|
56
|
+
/**
|
|
57
|
+
* Whether the plugin is configured with a broker URL. Lets consumers decide
|
|
58
|
+
* whether to bother building a message. A `true` value does not guarantee
|
|
59
|
+
* the broker is reachable — the connection is still lazy.
|
|
60
|
+
*/
|
|
61
|
+
isAvailable(): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Publish a JSON message to a topic exchange. No-op (returns silently) when
|
|
64
|
+
* no broker is configured or the client is unavailable.
|
|
65
|
+
*/
|
|
66
|
+
publish(exchange: string, routingKey: string, message: RabbitMessage, options?: PublishOptions): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Subscribe a handler to a topic exchange/routing-key on a named queue.
|
|
69
|
+
* No-op when no broker is configured or the client is unavailable.
|
|
70
|
+
*/
|
|
71
|
+
subscribe(exchange: string, routingKey: string, queue: string, handler: RabbitMessageHandler, options?: SubscribeOptions): Promise<void>;
|
|
72
|
+
/** Stop consuming from a queue. No-op when the client is unavailable. */
|
|
73
|
+
unsubscribe(queue: string): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* The live broker client, or null when unavailable. Advanced consumers that
|
|
76
|
+
* need features beyond publish/subscribe can reach the raw client here.
|
|
77
|
+
*/
|
|
78
|
+
getRawClient(): Promise<RabbitClient | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Lazy-init the shared AMQP client. The `@linagora/rabbitmq-client`
|
|
81
|
+
* dependency is optional — if the package or the broker is unreachable,
|
|
82
|
+
* return null so callers can no-op cleanly.
|
|
83
|
+
*
|
|
84
|
+
* Override this in tests to inject a stub client.
|
|
85
|
+
*/
|
|
86
|
+
protected getClient(): Promise<RabbitClient | null>;
|
|
87
|
+
/**
|
|
88
|
+
* Open and initialise the underlying client. Split out so the caching and
|
|
89
|
+
* retry policy in `getClient()` can be tested without a live broker.
|
|
90
|
+
*/
|
|
91
|
+
protected connect(): Promise<RabbitClient>;
|
|
92
|
+
private registerShutdown;
|
|
93
|
+
}
|
|
94
|
+
export {};
|
|
@@ -11,8 +11,13 @@ export declare class BaseResolver {
|
|
|
11
11
|
private readonly userTemplate;
|
|
12
12
|
private readonly groupTemplate;
|
|
13
13
|
private readonly map;
|
|
14
|
+
private readonly userBaseHeader;
|
|
15
|
+
private readonly groupBaseHeader;
|
|
16
|
+
private readonly headerRoot;
|
|
14
17
|
constructor(config: Config);
|
|
15
18
|
private applyTemplate;
|
|
19
|
+
/** Read a request header by name, tolerating Express req or a plain object. */
|
|
20
|
+
private readHeader;
|
|
16
21
|
private resolve;
|
|
17
22
|
userBase(req?: DmRequest | {
|
|
18
23
|
user?: string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import DmPlugin, { type Role } from '../../abstract/plugin';
|
|
2
|
+
import type { DM } from '../../bin';
|
|
3
|
+
import { Hooks } from '../../hooks';
|
|
4
|
+
export default class ClouderyProvision extends DmPlugin {
|
|
5
|
+
name: string;
|
|
6
|
+
roles: Role[];
|
|
7
|
+
dependencies: {
|
|
8
|
+
rabbitmq: string;
|
|
9
|
+
};
|
|
10
|
+
private readonly managerUrl;
|
|
11
|
+
private readonly managerToken;
|
|
12
|
+
private readonly offer;
|
|
13
|
+
private readonly clouderyDomain;
|
|
14
|
+
private readonly userBranch;
|
|
15
|
+
private readonly orgIdHeader;
|
|
16
|
+
private readonly fqdnAttribute;
|
|
17
|
+
private readonly orgIdAttribute;
|
|
18
|
+
private readonly defaultLocale;
|
|
19
|
+
private readonly rdnAttribute;
|
|
20
|
+
private readonly authExchange;
|
|
21
|
+
private readonly b2bExchange;
|
|
22
|
+
private readonly pollIntervalMs;
|
|
23
|
+
private readonly maxPollAttempts;
|
|
24
|
+
private readonly baseResolver;
|
|
25
|
+
private readonly createStash;
|
|
26
|
+
private readonly deleteStash;
|
|
27
|
+
constructor(server: DM);
|
|
28
|
+
hooks: Hooks;
|
|
29
|
+
/** Pre-create: stash the request-derived context for the post-create hook. */
|
|
30
|
+
private captureCreate;
|
|
31
|
+
/**
|
|
32
|
+
* Post-create: provision via Cloudery, wait for the workflow, then write the
|
|
33
|
+
* FQDN back and publish user.created. Gated on workflow success so nothing is
|
|
34
|
+
* emitted for an instance that does not exist yet.
|
|
35
|
+
*/
|
|
36
|
+
private provision;
|
|
37
|
+
/**
|
|
38
|
+
* Pre-delete: read the stored FQDN (and domain from mail) while the entry
|
|
39
|
+
* still exists, so the post-delete hook can tear the instance down. No stored
|
|
40
|
+
* FQDN means this user was not provisioned here.
|
|
41
|
+
*/
|
|
42
|
+
private captureDelete;
|
|
43
|
+
/** Post-delete: delete the Cloudery instance by FQDN and publish the event. */
|
|
44
|
+
private deprovision;
|
|
45
|
+
private createInstance;
|
|
46
|
+
/** Poll the workflow until it succeeds. Returns false on failure/timeout. */
|
|
47
|
+
private waitForWorkflow;
|
|
48
|
+
private findInstanceUuidByFqdn;
|
|
49
|
+
private deleteInstance;
|
|
50
|
+
private publishUserCreated;
|
|
51
|
+
private publishUserDeleted;
|
|
52
|
+
/** Read a request header by name, falling back to a query parameter. */
|
|
53
|
+
private readHeader;
|
|
54
|
+
private isAtOrUnder;
|
|
55
|
+
private pruneStash;
|
|
56
|
+
private extractId;
|
|
57
|
+
private extractPrimaryEmail;
|
|
58
|
+
private extractPrimaryPhone;
|
|
59
|
+
private extractPublicName;
|
|
60
|
+
private extractLocale;
|
|
61
|
+
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import DmPlugin, { type Role } from '../../abstract/plugin';
|
|
2
2
|
import type { DM } from '../../bin';
|
|
3
3
|
import { Hooks } from '../../hooks';
|
|
4
|
-
interface RabbitPublisher {
|
|
5
|
-
init(): Promise<void>;
|
|
6
|
-
publish(exchange: string, routingKey: string, message: Record<string, unknown>): Promise<void>;
|
|
7
|
-
close(clearSubscriptions?: boolean): Promise<void>;
|
|
8
|
-
}
|
|
9
4
|
export default class CozyProvision extends DmPlugin {
|
|
10
5
|
name: string;
|
|
11
6
|
roles: Role[];
|
|
7
|
+
dependencies: {
|
|
8
|
+
rabbitmq: string;
|
|
9
|
+
};
|
|
12
10
|
private readonly cozyAdminUrl;
|
|
13
11
|
private readonly cozyAdminPassphrase;
|
|
14
12
|
private readonly cozyAdminUser;
|
|
@@ -17,11 +15,9 @@ export default class CozyProvision extends DmPlugin {
|
|
|
17
15
|
private readonly cozyDefaultLocale;
|
|
18
16
|
private readonly cozyContextName;
|
|
19
17
|
private readonly cozyApps;
|
|
20
|
-
private readonly rabbitmqUrl;
|
|
21
18
|
private readonly authExchange;
|
|
22
19
|
private readonly b2bExchange;
|
|
23
|
-
private
|
|
24
|
-
private publisherInit;
|
|
20
|
+
private readonly cozyAdminAuthHeader;
|
|
25
21
|
constructor(server: DM);
|
|
26
22
|
hooks: Hooks;
|
|
27
23
|
/**
|
|
@@ -38,6 +34,15 @@ export default class CozyProvision extends DmPlugin {
|
|
|
38
34
|
* the user gets dropped onto a setup wizard at first OIDC login.
|
|
39
35
|
*/
|
|
40
36
|
private markInstanceOnboarded;
|
|
37
|
+
/**
|
|
38
|
+
* DELETE /instances/<domain> on the cozy-stack admin API. The SCIM delete
|
|
39
|
+
* hook only removed the LDAP entry; without this, a subsequent re-import
|
|
40
|
+
* of the same userName re-attaches to a stale cozy instance whose
|
|
41
|
+
* per-user state (oidc_id, sharings, contacts, ...) no longer matches
|
|
42
|
+
* the operator's intent. A 404 from cozy-stack is treated as success so
|
|
43
|
+
* the lifecycle stays idempotent.
|
|
44
|
+
*/
|
|
45
|
+
private destroyCozyInstance;
|
|
41
46
|
/**
|
|
42
47
|
* Publish user.created on the `auth` topic exchange. Payload matches what
|
|
43
48
|
* cozy-stack's stack.user.created consumer expects: a `twakeId` identifier,
|
|
@@ -53,13 +58,11 @@ export default class CozyProvision extends DmPlugin {
|
|
|
53
58
|
*/
|
|
54
59
|
private publishUserDeleted;
|
|
55
60
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* Override this in tests to inject a stub publisher.
|
|
61
|
+
* Resolve the shared `rabbitmq` plugin (declared as a dependency, so it loads
|
|
62
|
+
* first). Returns null and warns once if it is absent, letting the publish
|
|
63
|
+
* paths no-op cleanly.
|
|
61
64
|
*/
|
|
62
|
-
|
|
65
|
+
private rabbitmq;
|
|
63
66
|
private extractId;
|
|
64
67
|
private extractPrimaryEmail;
|
|
65
68
|
private extractPrimaryPhone;
|
|
@@ -74,6 +77,4 @@ export default class CozyProvision extends DmPlugin {
|
|
|
74
77
|
*/
|
|
75
78
|
private extractPublicName;
|
|
76
79
|
private extractLocale;
|
|
77
|
-
private registerShutdown;
|
|
78
80
|
}
|
|
79
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ldap-rest",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "Light directory manager",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Identity-Manager"
|
|
@@ -136,6 +136,10 @@
|
|
|
136
136
|
"types": "./dist/src/plugins/ldap/trash.d.ts",
|
|
137
137
|
"import": "./dist/plugins/ldap/trash.js"
|
|
138
138
|
},
|
|
139
|
+
"./plugin-rabbitmq": {
|
|
140
|
+
"types": "./dist/src/plugins/rabbitmq.d.ts",
|
|
141
|
+
"import": "./dist/plugins/rabbitmq.js"
|
|
142
|
+
},
|
|
139
143
|
"./plugin-scim-baseresolver": {
|
|
140
144
|
"types": "./dist/src/plugins/scim/baseResolver.d.ts",
|
|
141
145
|
"import": "./dist/plugins/scim/baseResolver.js"
|
|
@@ -196,6 +200,10 @@
|
|
|
196
200
|
"types": "./dist/src/plugins/twake/calendarResources.d.ts",
|
|
197
201
|
"import": "./dist/plugins/twake/calendarResources.js"
|
|
198
202
|
},
|
|
203
|
+
"./plugin-twake-clouderyprovision": {
|
|
204
|
+
"types": "./dist/src/plugins/twake/clouderyProvision.d.ts",
|
|
205
|
+
"import": "./dist/plugins/twake/clouderyProvision.js"
|
|
206
|
+
},
|
|
199
207
|
"./plugin-twake-cozyprovision": {
|
|
200
208
|
"types": "./dist/src/plugins/twake/cozyProvision.d.ts",
|
|
201
209
|
"import": "./dist/plugins/twake/cozyProvision.js"
|