ldap-rest 0.18.0 → 0.19.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/abstract/twakePlugin.js +1 -1
- package/dist/config/args.js +1 -1
- package/dist/plugins/ldap/externalUsersInGroups.js +1 -1
- package/dist/plugins/twake/james.js +1 -1
- package/dist/src/config/args.d.ts +1 -0
- package/dist/src/plugins/ldap/externalUsersInGroups.d.ts +11 -0
- package/dist/src/plugins/twake/james.d.ts +54 -3
- package/docs/client-development/api/rest-api.md +2 -0
- package/docs/usage/configuration.md +13 -12
- package/docs/usage/plugins/integrations/james-mail.md +67 -10
- package/docs/usage/plugins/integrations/james-mailboxes.md +14 -7
- package/docs/usage/plugins/ldap/external-users.md +23 -2
- package/docs/usage/upgrading.md +18 -0
- package/package.json +1 -1
- package/static/schemas/twake/groups.json +34 -17
- package/static/schemas/twake/nomenclature/twakeAccountStatus.json +16 -8
- package/static/schemas/twake/nomenclature/twakeDeliveryMode.json +14 -7
- package/static/schemas/twake/nomenclature/twakeDomain.json +14 -7
- package/static/schemas/twake/nomenclature/twakeListType.json +14 -7
- package/static/schemas/twake/nomenclature/twakeMailboxType.json +14 -7
- package/static/schemas/twake/nomenclature/twakeTitle.json +24 -12
- package/static/schemas/twake/organizations.json +26 -13
- package/static/schemas/twake/positions.json +10 -5
- package/static/schemas/twake/users.json +76 -38
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"node-fetch";import e from"p-limit";import r from"./plugin.js";class TwakePlugin extends r{constructor(t,r,s,i){if(super(t),this.retryAttempts=1,this.retryDelay=0,this.webadminUrl=this.config[r]||"",this.webadminToken=this.config[s]||"",!this.webadminUrl)throw new Error(`Twake plugin: ${r} is required`);this.webadminToken||this.logger.warn(`Twake plugin: No authentication token configured (${s}) - requests will be unauthenticated`);const a="number"==typeof this.config[i]?this.config[i]:10;this.requestLimit=e(a),this.logger.info(`Twake plugin HTTP request concurrency limit set to ${a}`),this.mailAttr=this.config.mail_attribute||"mail",this.displayNameAttr=this.config.display_name_attribute||"displayName"}createHeaders(t){const e={};return this.webadminToken&&(e.Authorization=`Bearer ${this.webadminToken}`),t&&(e["Content-Type"]=t),e}async callWebAdminApi(e,r,s,i,a,n,o=[]){const u={plugin:this.name,event:e,result:"error",dn:i,...n},l={method:s,headers:this.createHeaders(a?"application/json":void 0)};a&&(l.body=a);for(let s=1;;s++){try{const i=await this.requestLimit(()=>t(r,l));if(i.ok)this.logger.info({...u,result:"success",http_status:i.status,url:r});else{if(this.shouldRetry(i.status,o,s)){this.logger.warn({...u,result:"retry",attempt:s,http_status:i.status,http_status_text:i.statusText,url:r}),await this.waitBeforeRetry(s);continue}this.shouldIgnoreError(i.status,e)?this.logger.debug({...u,result:"ignored",http_status:i.status,http_status_text:i.statusText,url:r}):this.logger.error({...u,...s>1?{attempts:s}:{},http_status:i.status,http_status_text:i.statusText,url:r})}}catch(t){this.logger.error({...u,error:t,url:r})}return}}shouldRetry(t,e,r){return r<this.retryAttempts&&e.includes(t)}waitBeforeRetry(t){return new Promise(e=>setTimeout(e,this.retryDelay*t))}shouldIgnoreError(t,e){return 409===t
|
|
1
|
+
import t from"node-fetch";import e from"p-limit";import r from"./plugin.js";class TwakePlugin extends r{constructor(t,r,s,i){if(super(t),this.retryAttempts=1,this.retryDelay=0,this.webadminUrl=this.config[r]||"",this.webadminToken=this.config[s]||"",!this.webadminUrl)throw new Error(`Twake plugin: ${r} is required`);this.webadminToken||this.logger.warn(`Twake plugin: No authentication token configured (${s}) - requests will be unauthenticated`);const a="number"==typeof this.config[i]?this.config[i]:10;this.requestLimit=e(a),this.logger.info(`Twake plugin HTTP request concurrency limit set to ${a}`),this.mailAttr=this.config.mail_attribute||"mail",this.displayNameAttr=this.config.display_name_attribute||"displayName"}createHeaders(t){const e={};return this.webadminToken&&(e.Authorization=`Bearer ${this.webadminToken}`),t&&(e["Content-Type"]=t),e}async callWebAdminApi(e,r,s,i,a,n,o=[]){const u={plugin:this.name,event:e,result:"error",dn:i,...n},l={method:s,headers:this.createHeaders(a?"application/json":void 0)};a&&(l.body=a);for(let s=1;;s++){try{const i=await this.requestLimit(()=>t(r,l));if(i.ok)this.logger.info({...u,result:"success",http_status:i.status,url:r});else{if(this.shouldRetry(i.status,o,s)){this.logger.warn({...u,result:"retry",attempt:s,http_status:i.status,http_status_text:i.statusText,url:r}),await this.waitBeforeRetry(s);continue}this.shouldIgnoreError(i.status,e)?this.logger.debug({...u,result:"ignored",http_status:i.status,http_status_text:i.statusText,url:r}):this.logger.error({...u,...s>1?{attempts:s}:{},http_status:i.status,http_status_text:i.statusText,url:r})}}catch(t){this.logger.error({...u,error:t,url:r})}return}}shouldRetry(t,e,r){return r<this.retryAttempts&&e.includes(t)}waitBeforeRetry(t){return new Promise(e=>setTimeout(e,this.retryDelay*t))}shouldIgnoreError(t,e){return 409===t&&/alias/i.test(e)}attributeToString(t){return t?Array.isArray(t)?t.length>0?String(t[0]):null:String(t):null}async ldapGetAttributes(t,e){try{const r=e&&e.length>0?e:void 0,s=await this.server.ldap.search({paged:!1,scope:"base",attributes:r},t);return s.searchEntries&&s.searchEntries.length>0?s.searchEntries[0]:null}catch(e){return this.logger.debug(`Could not fetch attributes from DN ${t}: ${e}`),null}}getAliases(t){if(!t)return[];return(Array.isArray(t)?t:[t]).map(t=>Buffer.isBuffer(t)?t.toString("utf-8"):String(t)).map(t=>this.normalizeAlias(t))}normalizeAlias(t){return t.toLowerCase().startsWith("smtp:")?t.substring(5):t}extractMailDomain(t){const e=t.split("@");return 2===e.length?e[1]:null}extractDomainFromDn(t){const e=t.match(/dc=([^,]+)/gi);return e?e.map(t=>t.substring(3)).join("."):"example.com"}}export{TwakePlugin,TwakePlugin as default};
|
package/dist/config/args.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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","mailQuotaSize"],["--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-retry-attempts","DM_JAMES_RETRY_ATTEMPTS",5,"number"],["--james-retry-delay","DM_JAMES_RETRY_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-space-sync-queue","DM_TWAKE_SPACE_SYNC_QUEUE","twake.space.sync.requested.ldap-rest"],["--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};
|
|
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","mailQuotaSize"],["--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-retry-attempts","DM_JAMES_RETRY_ATTEMPTS",5,"number"],["--james-retry-delay","DM_JAMES_RETRY_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"],["--james-sync-forwards","DM_JAMES_SYNC_FORWARDS",!1,"boolean"],["--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-space-sync-queue","DM_TWAKE_SPACE_SYNC_QUEUE","twake.space.sync.requested.ldap-rest"],["--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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import r from"../../abstract/plugin.js";import{HttpError as e}from"../../lib/errors.js";import{launchHooksChained as s,launchHooks as a}from"../../lib/utils.js";class ExternalUsersInGroups extends r{constructor(r){super(r),this.name="externalUsersInGroups",this.roles=["consistency"],this.dependencies={ldapGroups:"core/ldap/groups"},this.hooks={ldapgroupvalidatemembers:async([r,
|
|
1
|
+
import r from"../../abstract/plugin.js";import{HttpError as e}from"../../lib/errors.js";import{launchHooksChained as s,launchHooks as t,parseDn as a,normalizeDn as n,unescapeDnValue as i}from"../../lib/utils.js";class ExternalUsersInGroups extends r{constructor(r){super(r),this.name="externalUsersInGroups",this.roles=["consistency","configurable"],this.dependencies={ldapGroups:"core/ldap/groups"},this.hooks={ldapgroupvalidatemembers:async([r,a])=>(await Promise.all(a.map(r=>this.server.ldap.queryLimit(async()=>{const a=this.externalAddress(r);if(null===a)return!1;try{return await this.ldap.search({paged:!1,scope:"base"},r),!0}catch{if(!a)throw new Error(`Malformed member ${r}`);const n=a.split("@")[1]?.toLowerCase();if(n&&this.managedDomains().includes(n))throw new Error(`Cannot create external user with managed domain: ${a}`);let i={objectClass:this.config.external_branch_class||this.config.user_class,mail:a,[this.config.ldap_groups_main_attribute]:a,sn:a};[r,i]=await s(this.registeredHooks.externaluserentry,[r,i]);try{return await this.ldap.add(r,i),t(this.registeredHooks.externaluseradded,r,i),!0}catch(s){if(s instanceof e)throw s;throw new Error(`Unable to insert ${r}: ${s instanceof Error?s.message:String(s)}`)}}}))),[r,a])},this.ldap=r.ldap}externalAddress(r){const e=this.config.external_members_branch;if(!e)return null;const[s,...t]=a(r);if(!s||n(t.join(","))!==n(e))return null;const o=s.indexOf("=");if(o<0||"mail"!==s.slice(0,o).trim().toLowerCase())return null;const l=s.slice(o+1).trim();return/(^|[^\\])(\\\\)*\+/.test(l)?null:i(l)}managedDomains(){const r=this.config.mail_domain;return Array.isArray(r)?r.map(r=>String(r).toLowerCase()):[]}getConfigApiData(){return{enabled:!0,branch:this.config.external_members_branch,rdnAttribute:"mail",managedDomains:this.managedDomains()}}}export{ExternalUsersInGroups as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"node-fetch";import{TwakePlugin as e}from"../../abstract/twakePlugin.js";import{asyncHandler as a,escapeLdapFilter as i,isDummyMemberDn as s,normalizeDn as r}from"../../lib/utils.js";import{wantJson as n}from"../../lib/expressFormatedResponses.js";const o=[400,404],members=t=>{const e=t?.member;return void 0===e?[]:(Array.isArray(e)?e:[e]).map(String)},memberKey=t=>{try{return r(t)}catch{return t.toLowerCase()}},l=["groupofnames","groupofuniquenames","groupofurls"],isGroup=t=>(Array.isArray(t)?t:t?[t]:[]).some(t=>l.includes(String(t).toLowerCase()));class James extends e{constructor(t){super(t,"james_webadmin_url","james_webadmin_token","james_concurrency"),this.name="james",this.roles=["consistency","api"],this.dependencies={onLdapChange:"core/ldap/onChange",ldapGroups:"core/ldap/groups"},this.hooks={ldapadddone:async t=>{const[e,a]=t,i=a[this.mailAttr],s=a[this.quotaAttr],r=a[this.aliasAttr];if(!i||isGroup(a.objectClass))return;const n=Array.isArray(i)?String(i[0]):String(i);if(this.initDelay>0&&await new Promise(t=>setTimeout(t,this.initDelay)),s){const t=Array.isArray(s)?Number(s[0]):Number(s);!isNaN(t)&&t>0&&await this.callWebAdminApi("ldapadddone:quota",`${this.webadminUrl}/quota/users/${n}/size`,"PUT",e,t.toString(),{mail:n,quota:t},o)}if(r){const t=this.getAliases(r);await Promise.all(t.map(t=>this.callWebAdminApi("ldapadddone:alias",`${this.webadminUrl}/address/aliases/${n}/sources/${t}`,"PUT",e,null,{mail:n,alias:t},o)))}const l=this.getDisplayNameFromAttributes(a);l&&await this.updateJamesIdentity(e,n,l)},onLdapMailChange:async(t,e,a)=>{const i=e?Array.isArray(e)?String(e[0]):String(e):null,s=a?Array.isArray(a)?String(a[0]):String(a):null;if(i)if(s){if(!await this.isGroupDn(t)){await this.callWebAdminApi("onLdapMailChange",`${this.webadminUrl}/users/${i}/rename/${s}?action=rename&force`,"POST",t,null,{oldmail:i,newmail:s},o);try{const e=await this.server.ldap.search({paged:!1,scope:"base",attributes:[this.aliasAttr]},t);if(e.searchEntries&&e.searchEntries.length>0){const a=this.getAliases(e.searchEntries[0][this.aliasAttr]);a.length>0&&await Promise.all([...a.map(e=>this.callWebAdminApi("onLdapMailChange-delete",`${this.webadminUrl}/address/aliases/${i}/sources/${e}`,"DELETE",t,null,{oldmail:i,alias:e})),...a.map(e=>this.callWebAdminApi("onLdapMailChange-create",`${this.webadminUrl}/address/aliases/${s}/sources/${e}`,"PUT",t,null,{newmail:s,alias:e},o))])}}catch(t){this.logger.debug("Could not fetch aliases for mail change:",t)}}}else this.logger.debug(`Skipping mail rename for ${t}: newmail is empty (mail attribute was deleted, not changed)`);else this.logger.debug(`Skipping mail rename for ${t}: oldmail is empty (mail attribute was added, not changed)`)},onLdapAliasChange:async(t,e,a,i)=>{if(await this.isGroupDn(t))return;const s=a.map(t=>this.normalizeAlias(t)),r=i.map(t=>this.normalizeAlias(t)),n=s.filter(t=>!r.includes(t)),l=r.filter(t=>!s.includes(t));await Promise.all([...n.map(a=>this.callWebAdminApi("onLdapAliasChange-delete",`${this.webadminUrl}/address/aliases/${e}/sources/${a}`,"DELETE",t,null,{mail:e,alias:a,action:"delete"})),...l.map(a=>this.callWebAdminApi("onLdapAliasChange-add",`${this.webadminUrl}/address/aliases/${e}/sources/${a}`,"PUT",t,null,{mail:e,alias:a,action:"add"},o))])},onLdapQuotaChange:async(t,e,a,i)=>{if(!await this.isGroupDn(t))return this.callWebAdminApi("onLdapQuotaChange",`${this.webadminUrl}/quota/users/${e}/size`,"PUT",t,i.toString(),{oldQuota:a,newQuota:i},o)},onLdapForwardChange:async(t,e,a,i)=>{if(await this.isGroupDn(t))return;const s=this.extractMailDomain(e);if(!s)return void this.logger.error(`Cannot extract domain from mail ${e} for forward management`);const r=a.filter(t=>!i.includes(t)),n=i.filter(t=>!a.includes(t));await Promise.all([...r.map(a=>this.callWebAdminApi("onLdapForwardChange-delete",`${this.webadminUrl}/address/forwards/${e}/targets/${a}`,"DELETE",t,null,{mail:e,forward:a,domain:s,action:"delete"})),...n.map(a=>this.callWebAdminApi("onLdapForwardChange-add",`${this.webadminUrl}/address/forwards/${e}/targets/${a}`,"PUT",t,null,{mail:e,forward:a,domain:s,action:"add"},o))])},onLdapChange:async(t,e)=>{this.config.delegation_attribute&&e[this.config.delegation_attribute]&&!await this.isGroupDn(t)&&await this._handleDelegationChange(t,e)},onLdapDisplayNameChange:async t=>{const e=[this.mailAttr,this.displayNameAttr,"cn","givenName","sn","objectClass"],a=await this.ldapGetAttributes(t,e);if(!a)return void this.logger.warn(`Cannot update James identity: entry not found for ${t}`);if(isGroup(a.objectClass))return;const i=this.attributeToString(a[this.mailAttr]);if(!i)return void this.logger.warn(`Cannot update James identity: no mail found for ${t}`);const s=this.getDisplayNameFromAttributes(a);if(s)return this.updateJamesIdentity(t,i,s);this.logger.warn(`Cannot update James identity: no display name found for ${t}`)},onLdapEntryChange:(t,e,a)=>this.followTeamMailbox(e,a)},this.teamMailboxQueue=new Map,this.identityQueue=new Map,this.quotaAttr=this.config.quota_attribute,this.aliasAttr=this.config.alias_attribute||"mailAlternateAddress",this.mailboxTypeAttr=this.config.james_mailbox_type_attribute||"twakeMailboxType",this.initDelay="number"==typeof this.config.james_init_delay?this.config.james_init_delay:1e3,this.retryAttempts="number"==typeof this.config.james_retry_attempts?Math.max(1,this.config.james_retry_attempts):5,this.retryDelay="number"==typeof this.config.james_retry_delay?Math.max(0,this.config.james_retry_delay):1e3}api(t){const e=this.config.api_prefix||"/api";t.get(`${e}/v1/users/:user/quota-usage`,a((t,e)=>this.getUserQuota(t,e))),this.logger.info(`James API registered at ${e}/v1/users/:user/quota-usage`)}async getUserQuota(e,a){if(!n(e,a))return;const s=e.params.user;try{const e=i(s),r=await this.server.ldap.search({scope:"sub",filter:`(uid=${e})`,paged:!1,attributes:[this.mailAttr]},this.config.ldap_base||""),n=r.searchEntries?.[0];if(!n)return void a.status(404).json({error:`User ${s} not found`});const o=n[this.mailAttr];if(!o)return void a.status(400).json({error:`User ${s} has no mail attribute`});const l=Array.isArray(o)?String(o[0]):String(o),u=`${this.webadminUrl}/quota/users/${l}`,m=await this.requestLimit(()=>t(u,{method:"GET",headers:this.createHeaders()}));if(!m.ok)return void(404===m.status?a.status(404).json({error:`Quota not found for user ${s}`}):a.status(502).json({error:`Failed to retrieve quota from James: ${m.statusText}`}));const d=await m.json();a.json(d)}catch(t){this.logger.error({plugin:this.name,event:"getUserQuota",username:s,error:t instanceof Error?t.message:String(t)}),a.status(500).json({error:"Internal server error while retrieving quota"})}}teamMailboxOf(t){if(!t||"teamMailbox"!==this.getMailboxType(t))return null;const e=this.attributeToString(t.mail);if(!e)return null;const a=String(t.dn);return{mail:e,refused:(this.config.james_mailing_list_branch||[]).some(t=>a===t||a.endsWith(","+t))}}async followTeamMailbox(t,e){const a=this.teamMailboxOf(t),i=this.teamMailboxOf(e);if(!a&&!i)return;const s=String((e??t).dn);i?.refused&&!a?.refused&&this.logger.error({plugin:this.name,event:"teamMailbox",dn:s,error:`Team mailbox cannot be in mailing list branches: ${(this.config.james_mailing_list_branch||[]).join(", ")}`});const r=a&&!a.refused?a.mail:null,n=i&&!i.refused?i.mail:null;if(!r&&!n)return;const o=memberKey(s),follow=async()=>{if(r&&n&&r.toLowerCase()===n.toLowerCase()){const a=new Map(members(t).map(t=>[memberKey(t),t])),i=[];for(const t of members(e))a.delete(memberKey(t))||i.push(t);return void await Promise.all([this.teamMailboxMembers(s,n,"PUT",i),this.teamMailboxMembers(s,n,"DELETE",[...a.values()])])}r&&await this.teamMailboxMembers(s,r,"DELETE",members(t)),n&&await this.createTeamMailbox(s,e,n)},l=(this.teamMailboxQueue.get(o)??Promise.resolve()).then(follow,follow);this.teamMailboxQueue.set(o,l);try{await l}finally{this.teamMailboxQueue.get(o)===l&&this.teamMailboxQueue.delete(o)}}async teamMailboxMembers(t,e,a,i){const s=this.extractMailDomain(e);if(!s||!i.length)return;const r=await this.getMemberEmails(i);await Promise.all(r.map(i=>this.callWebAdminApi("teamMailbox:"+("PUT"===a?"addMember":"removeMember"),`${this.webadminUrl}/domains/${s}/team-mailboxes/${e}/members/${i}`,a,t,null,{domain:s,teamMailbox:e,memberMail:i})))}getDisplayNameFromAttributes(t){const e=this.attributeToString(t[this.displayNameAttr]);if(e)return e;const a=this.attributeToString(t.cn);if(a)return a;const i=this.attributeToString(t.givenName),s=this.attributeToString(t.sn);if(i||s){const t=[];return i&&t.push(i),s&&t.push(s),t.join(" ")}const r=this.attributeToString(t[this.mailAttr]);return r||null}async isGroupDn(t){const e=await this.ldapGetAttributes(t,["objectClass"]);return isGroup(e?.objectClass)}async getMailFromDN(t){const e=await this.ldapGetAttributes(t,[this.mailAttr]);return e?this.attributeToString(e[this.mailAttr]):null}async getDisplayNameFromDN(t){const e=[this.displayNameAttr,"cn","givenName","sn",this.mailAttr],a=await this.ldapGetAttributes(t,e);return a?this.getDisplayNameFromAttributes(a):null}async generateSignature(t){const e=this.config.james_signature_template;if(!e)return null;const a=await this.ldapGetAttributes(t);if(!a)return null;let i=e;return i=i.replace(/\{(\w+)\}/g,(t,e)=>"string"==typeof e&&a&&"object"==typeof a&&Object.prototype.hasOwnProperty.call(a,e)&&this.attributeToString(a[e])||""),i}async updateJamesIdentity(t,e,a){const i=(this.identityQueue.get(e)??Promise.resolve()).then(()=>this.doUpdateJamesIdentity(t,e,a));this.identityQueue.set(e,i);try{await i}finally{this.identityQueue.get(e)===i&&this.identityQueue.delete(e)}}async doUpdateJamesIdentity(e,a,i){const s={plugin:this.name,event:"onLdapDisplayNameChange",result:"error",dn:e,mail:a,displayName:i};for(let r=1;;r++){try{const n=`${this.webadminUrl}/users/${a}/identities`,l=await this.requestLimit(()=>t(`${n}?default=true`,{method:"GET",headers:this.createHeaders()}));let u;if(l.ok){const t=await l.json();u=(Array.isArray(t)?t:[t]).filter(t=>t&&t.id&&!1!==t.mayDelete).sort((t,e)=>(t.sortOrder??0)-(e.sortOrder??0))[0]}else if(404!==l.status){const t="get_default_identity";if(this.shouldRetry(l.status,o,r)){await this.warnAndWait(s,t,r,l);continue}return void this.logger.error({...s,step:t,...r>1?{attempts:r}:{},http_status:l.status,http_status_text:l.statusText})}const m=await this.generateSignature(e),d=u?.id?{id:u.id,email:u.email,name:i}:{email:a,name:i,sortOrder:0};m&&(d.htmlSignature=m);const h=u?.id?"update_identity":"create_identity",c=await this.requestLimit(()=>t(u?.id?`${n}/${u.id}`:n,{method:u?.id?"PUT":"POST",headers:this.createHeaders("application/json"),body:JSON.stringify(d)}));if(c.ok)this.logger.info({...s,step:h,result:"success",http_status:c.status});else{if(this.shouldRetry(c.status,o,r)){await this.warnAndWait(s,h,r,c);continue}this.logger.error({...s,step:h,...r>1?{attempts:r}:{},http_status:c.status,http_status_text:c.statusText})}}catch(t){this.logger.error({...s,error:`${t}`})}return}}async warnAndWait(t,e,a,i){this.logger.warn({...t,step:e,result:"retry",attempt:a,http_status:i.status,http_status_text:i.statusText}),await this.waitBeforeRetry(a)}async deleteUserData(e,a){const i={plugin:this.name,event:"deleteUserData",mail:e,fromStep:a};try{const s=new URL(`${this.webadminUrl}/users/${e}`);s.searchParams.set("action","deleteData"),a&&s.searchParams.set("fromStep",a);const r=await this.requestLimit(()=>t(s.toString(),{method:"POST",headers:this.createHeaders()}));if(!r.ok)return this.logger.error({...i,http_status:r.status,http_status_text:r.statusText}),null;const n=await r.json();return this.logger.info({...i,http_status:r.status,taskId:n.taskId}),n}catch(t){return this.logger.error({...i,error:`${t}`}),null}}getMailboxType(t){const e=t[this.mailboxTypeAttr];if(!e)return null;const a=(Array.isArray(e)?String(e[0]):String(e)).match(/^cn=([^,]+)/);if(!a)return null;const i=a[1];return"group"===i||"mailingList"===i||"teamMailbox"===i?i:null}async createTeamMailbox(t,e,a){const i=this.extractMailDomain(a);i?(await this.callWebAdminApi("teamMailbox:create",`${this.webadminUrl}/domains/${i}/team-mailboxes/${a}`,"PUT",t,null,{domain:i,teamMailbox:a}),await this.teamMailboxMembers(t,a,"PUT",members(e))):this.logger.error({plugin:this.name,event:"createTeamMailbox",dn:t,mail:a,error:"Cannot extract domain from mail address"})}async getMemberEmails(t){const e=t.filter(t=>!s(t,this.config.group_dummy_user)).map(t=>this.server.ldap.queryLimit(async()=>{const e=await this.ldapGetAttributes(t,[this.mailAttr]);return e?this.attributeToString(e[this.mailAttr]):null}));return(await Promise.all(e)).filter(t=>null!==t)}async getGroupMail(t){const e=await this.ldapGetAttributes(t,["mail"]);return e?this.attributeToString(e.mail):null}async getGroupMailboxType(t){try{const e=await this.server.ldap.search({paged:!1,scope:"base",attributes:[this.mailboxTypeAttr]},t);if(e.searchEntries&&e.searchEntries.length>0)return this.getMailboxType(e.searchEntries[0])}catch(e){const a=e instanceof Error?e.message:String(e);this.logger.debug(`Could not get mailbox type for group ${t}: ${a}`)}return null}async _handleDelegationChange(t,e){const a=await this.ldapGetAttributes(t,[this.mailAttr]);if(!a)return void this.logger.warn({plugin:this.name,event:"onLdapChange",dn:t,message:"Could not find user entry to get mail attribute"});const i=a[this.mailAttr];if(!i||"string"!=typeof i)return void this.logger.warn({plugin:this.name,event:"onLdapChange",dn:t,message:"User has no mail attribute, cannot manage delegation"});const s=this.config.delegation_attribute;if(!s)return;const[r,n]=e[s]||[],l=this._normalizeToArray(r),u=this._normalizeToArray(n),m=u.filter(t=>!l.includes(t)),d=l.filter(t=>!u.includes(t)),h=m.map(t=>this.server.ldap.queryLimit(async()=>{const e=await this._getDelegateEmail(t);return{dn:t,email:e}})),c=d.map(t=>this.server.ldap.queryLimit(async()=>{const e=await this._getDelegateEmail(t);return{dn:t,email:e}})),[g,p]=await Promise.all([Promise.all(h),Promise.all(c)]),b=[];for(const{dn:e,email:a}of g)a&&b.push(this.callWebAdminApi("onLdapChange:addDelegation",`${this.webadminUrl}/users/${i}/authorizedUsers/${a}`,"PUT",t,null,{userMail:i,delegateEmail:a,delegateDN:e,action:"add"},o));for(const{dn:e,email:a}of p)a&&b.push(this.callWebAdminApi("onLdapChange:removeDelegation",`${this.webadminUrl}/users/${i}/authorizedUsers/${a}`,"DELETE",t,null,{userMail:i,delegateEmail:a,delegateDN:e,action:"remove"}));await Promise.all(b)}async _getDelegateEmail(t){const e=await this.ldapGetAttributes(t,[this.mailAttr]);return e?this.attributeToString(e[this.mailAttr]):(this.logger.warn({plugin:this.name,event:"getDelegateEmail",dn:t,message:"Could not resolve delegate DN to email"}),null)}_normalizeToArray(t){return t?Array.isArray(t)?t:[t]:[]}}export{James as default};
|
|
1
|
+
import t from"node-fetch";import{TwakePlugin as e}from"../../abstract/twakePlugin.js";import{asyncHandler as a,escapeLdapFilter as i,isDummyMemberDn as s,normalizeDn as r}from"../../lib/utils.js";import{wantJson as n}from"../../lib/expressFormatedResponses.js";const o=[400,404],pickDefaultIdentity=t=>(Array.isArray(t)?t:[t]).filter(t=>t&&t.id&&!1!==t.mayDelete).sort((t,e)=>(t.sortOrder??0)-(e.sortOrder??0))[0],members=t=>{const e=t?.member;return void 0===e?[]:(Array.isArray(e)?e:[e]).map(String)},memberKey=t=>{try{return r(t)}catch{return t.toLowerCase()}},l=["groupofnames","groupofuniquenames","groupofurls"],isGroup=t=>(Array.isArray(t)?t:t?[t]:[]).some(t=>l.includes(String(t).toLowerCase()));class James extends e{constructor(t){super(t,"james_webadmin_url","james_webadmin_token","james_concurrency"),this.name="james",this.roles=["consistency","api"],this.dependencies={onLdapChange:"core/ldap/onChange",ldapGroups:"core/ldap/groups"},this.hooks={ldapadddone:([t,e])=>this.provisionAccount(t,e),onLdapMailChange:async(t,e,a)=>{const i=e?Array.isArray(e)?String(e[0]):String(e):null,s=a?Array.isArray(a)?String(a[0]):String(a):null;if(!i)return void this.logger.debug(`Skipping mail rename for ${t}: oldmail is empty (mail attribute was added, not changed)`);if(!s)return void this.logger.debug(`Skipping mail rename for ${t}: newmail is empty (mail attribute was deleted, not changed)`);if(await this.isGroupDn(t))return;const r=await this.defaultSignatures(i),n=await this.ldapGetAttributes(t,[this.forwardAttr,this.displayNameAttr,"cn","givenName","sn",this.mailAttr]);await this.dropStaleForwards(t,s,this.ownForwards(n?.[this.forwardAttr])),await this.callWebAdminApi("onLdapMailChange",`${this.webadminUrl}/users/${i}/rename/${s}?action=rename&force`,"POST",t,null,{oldmail:i,newmail:s},o);try{const e=await this.server.ldap.search({paged:!1,scope:"base",attributes:[this.aliasAttr]},t);if(e.searchEntries&&e.searchEntries.length>0){const a=this.getAliases(e.searchEntries[0][this.aliasAttr]);a.length>0&&await Promise.all([...a.map(e=>this.callWebAdminApi("onLdapMailChange-delete",`${this.webadminUrl}/address/aliases/${i}/sources/${e}`,"DELETE",t,null,{oldmail:i,alias:e})),...a.map(e=>this.callWebAdminApi("onLdapMailChange-create",`${this.webadminUrl}/address/aliases/${s}/sources/${e}`,"PUT",t,null,{newmail:s,alias:e},o))])}}catch(t){this.logger.debug("Could not fetch aliases for mail change:",t)}const l=n&&this.getDisplayNameFromAttributes(n);l&&await this.updateJamesIdentity(t,s,l,r)},onLdapAliasChange:async(t,e,a,i)=>{if(await this.isGroupDn(t))return;const s=a.map(t=>this.normalizeAlias(t)),r=i.map(t=>this.normalizeAlias(t)),n=s.filter(t=>!r.includes(t)),l=r.filter(t=>!s.includes(t));await Promise.all([...n.map(a=>this.callWebAdminApi("onLdapAliasChange-delete",`${this.webadminUrl}/address/aliases/${e}/sources/${a}`,"DELETE",t,null,{mail:e,alias:a,action:"delete"})),...l.map(async a=>{await this.dropStaleForwards(t,a,[]),await this.callWebAdminApi("onLdapAliasChange-add",`${this.webadminUrl}/address/aliases/${e}/sources/${a}`,"PUT",t,null,{mail:e,alias:a,action:"add"},o)})])},onLdapQuotaChange:async(t,e,a,i)=>{if(!await this.isGroupDn(t))return this.callWebAdminApi("onLdapQuotaChange",`${this.webadminUrl}/quota/users/${e}/size`,"PUT",t,i.toString(),{oldQuota:a,newQuota:i},o)},onLdapForwardChange:async(t,e,a,i)=>{if(!this.config.james_sync_forwards)return;if(await this.isGroupDn(t))return;const s=this.extractMailDomain(e);if(!s)return void this.logger.error(`Cannot extract domain from mail ${e} for forward management`);const r=a.filter(t=>!i.includes(t)),n=i.filter(t=>!a.includes(t));await Promise.all([...r.map(a=>this.callWebAdminApi("onLdapForwardChange-delete",`${this.webadminUrl}/address/forwards/${e}/targets/${a}`,"DELETE",t,null,{mail:e,forward:a,domain:s,action:"delete"})),...n.map(a=>this.callWebAdminApi("onLdapForwardChange-add",`${this.webadminUrl}/address/forwards/${e}/targets/${a}`,"PUT",t,null,{mail:e,forward:a,domain:s,action:"add"},o))])},onLdapChange:async(t,e)=>{this.config.delegation_attribute&&e[this.config.delegation_attribute]&&!await this.isGroupDn(t)&&await this._handleDelegationChange(t,e)},onLdapDisplayNameChange:async t=>{const e=[this.mailAttr,this.displayNameAttr,"cn","givenName","sn","objectClass"],a=await this.ldapGetAttributes(t,e);if(!a)return void this.logger.warn(`Cannot update James identity: entry not found for ${t}`);if(isGroup(a.objectClass))return;const i=this.attributeToString(a[this.mailAttr]);if(!i)return void this.logger.warn(`Cannot update James identity: no mail found for ${t}`);const s=this.getDisplayNameFromAttributes(a);if(s)return this.updateJamesIdentity(t,i,s);this.logger.warn(`Cannot update James identity: no display name found for ${t}`)},onLdapEntryChange:async(t,e,a)=>{e&&a&&!e[this.mailAttr]&&a[this.mailAttr]&&await this.provisionAccount(t,a),await this.followTeamMailbox(e,a)}},this.teamMailboxQueue=new Map,this.identityQueue=new Map,this.quotaAttr=this.config.quota_attribute,this.aliasAttr=this.config.alias_attribute||"mailAlternateAddress",this.mailboxTypeAttr=this.config.james_mailbox_type_attribute||"twakeMailboxType",this.forwardAttr=this.config.forward_attribute||"mailForwardingAddress",this.initDelay="number"==typeof this.config.james_init_delay?this.config.james_init_delay:1e3,this.retryAttempts="number"==typeof this.config.james_retry_attempts?Math.max(1,this.config.james_retry_attempts):5,this.retryDelay="number"==typeof this.config.james_retry_delay?Math.max(0,this.config.james_retry_delay):1e3}api(t){const e=this.config.api_prefix||"/api";t.get(`${e}/v1/users/:user/quota-usage`,a((t,e)=>this.getUserQuota(t,e))),this.logger.info(`James API registered at ${e}/v1/users/:user/quota-usage`)}async getUserQuota(e,a){if(!n(e,a))return;const s=e.params.user;try{const e=i(s),r=await this.server.ldap.search({scope:"sub",filter:`(uid=${e})`,paged:!1,attributes:[this.mailAttr]},this.config.ldap_base||""),n=r.searchEntries?.[0];if(!n)return void a.status(404).json({error:`User ${s} not found`});const o=n[this.mailAttr];if(!o)return void a.status(400).json({error:`User ${s} has no mail attribute`});const l=Array.isArray(o)?String(o[0]):String(o),d=`${this.webadminUrl}/quota/users/${l}`,u=await this.requestLimit(()=>t(d,{method:"GET",headers:this.createHeaders()}));if(!u.ok)return void(404===u.status?a.status(404).json({error:`Quota not found for user ${s}`}):a.status(502).json({error:`Failed to retrieve quota from James: ${u.statusText}`}));const m=await u.json();a.json(m)}catch(t){this.logger.error({plugin:this.name,event:"getUserQuota",username:s,error:t instanceof Error?t.message:String(t)}),a.status(500).json({error:"Internal server error while retrieving quota"})}}teamMailboxOf(t){if(!t||"teamMailbox"!==this.getMailboxType(t))return null;const e=this.attributeToString(t.mail);if(!e)return null;const a=String(t.dn),i=this.config.james_mailing_list_branch||[];if(i.some(t=>a===t||a.endsWith(","+t)))return{mail:e,refused:`Team mailbox cannot be in mailing list branches: ${i.join(", ")}`};const s=e.lastIndexOf("@");return/^[#a-zA-Z0-9_-]{1,255}$/.test(e.slice(0,s))&&this.extractMailDomain(e)?{mail:e,refused:null}:{mail:e,refused:"A team mailbox address is a name of letters, digits, #, - and _ at a domain"}}async provisionAccount(t,e){const a=e[this.mailAttr],i=e[this.quotaAttr],s=e[this.aliasAttr];if(!a||isGroup(e.objectClass))return;const r=Array.isArray(a)?String(a[0]):String(a);if(this.initDelay>0&&await new Promise(t=>setTimeout(t,this.initDelay)),i){const e=Array.isArray(i)?Number(i[0]):Number(i);!isNaN(e)&&e>0&&await this.callWebAdminApi("ldapadddone:quota",`${this.webadminUrl}/quota/users/${r}/size`,"PUT",t,e.toString(),{mail:r,quota:e},o)}const n=this.getAliases(s);await Promise.all([this.dropStaleForwards(t,r,this.ownForwards(e[this.forwardAttr])),...n.map(e=>this.dropStaleForwards(t,e,[]))]),n.length>0&&await Promise.all(n.map(e=>this.callWebAdminApi("ldapadddone:alias",`${this.webadminUrl}/address/aliases/${r}/sources/${e}`,"PUT",t,null,{mail:r,alias:e},o)));const l=this.getDisplayNameFromAttributes(e);l&&await this.updateJamesIdentity(t,r,l)}async followTeamMailbox(t,e){const a=this.teamMailboxOf(t),i=this.teamMailboxOf(e);if(!a&&!i)return;const s=String((e??t).dn);i?.refused&&i.refused!==a?.refused&&this.logger.error({plugin:this.name,event:"teamMailbox",dn:s,mail:i.mail,error:i.refused});const r=a&&!a.refused?a.mail:null,n=i&&!i.refused?i.mail:null;if(!r&&!n)return;const o=memberKey(s),follow=async()=>{if(r&&n&&r.toLowerCase()===n.toLowerCase()){const a=new Map(members(t).map(t=>[memberKey(t),t])),i=[];for(const t of members(e))a.delete(memberKey(t))||i.push(t);return void await Promise.all([this.teamMailboxMembers(s,n,"PUT",i),this.teamMailboxMembers(s,n,"DELETE",[...a.values()])])}r&&await this.teamMailboxMembers(s,r,"DELETE",members(t)),n&&await this.createTeamMailbox(s,e,n)},l=(this.teamMailboxQueue.get(o)??Promise.resolve()).then(follow,follow);this.teamMailboxQueue.set(o,l);try{await l}finally{this.teamMailboxQueue.get(o)===l&&this.teamMailboxQueue.delete(o)}}teamMailboxUrl(t){const e=t.toLowerCase(),a=e.lastIndexOf("@");return`${this.webadminUrl}/domains/${e.slice(a+1)}/team-mailboxes/${encodeURIComponent(e.slice(0,a))}`}async teamMailboxMembers(t,e,a,i){if(!i.length)return;const s=this.teamMailboxUrl(e),r=await this.getMemberEmails(i);await Promise.all(r.map(i=>this.callWebAdminApi("teamMailbox:"+("PUT"===a?"addMember":"removeMember"),`${s}/members/${i}`,a,t,null,{teamMailbox:e,memberMail:i})))}getDisplayNameFromAttributes(t){const e=this.attributeToString(t[this.displayNameAttr]);if(e)return e;const a=this.attributeToString(t.cn);if(a)return a;const i=this.attributeToString(t.givenName),s=this.attributeToString(t.sn);if(i||s){const t=[];return i&&t.push(i),s&&t.push(s),t.join(" ")}const r=this.attributeToString(t[this.mailAttr]);return r||null}async isGroupDn(t){const e=await this.ldapGetAttributes(t,["objectClass"]);return isGroup(e?.objectClass)}async getMailFromDN(t){const e=await this.ldapGetAttributes(t,[this.mailAttr]);return e?this.attributeToString(e[this.mailAttr]):null}async getDisplayNameFromDN(t){const e=[this.displayNameAttr,"cn","givenName","sn",this.mailAttr],a=await this.ldapGetAttributes(t,e);return a?this.getDisplayNameFromAttributes(a):null}async generateSignature(t){const e=this.config.james_signature_template;if(!e)return null;const a=await this.ldapGetAttributes(t);if(!a)return null;let i=e;return i=i.replace(/\{(\w+)\}/g,(t,e)=>"string"==typeof e&&a&&"object"==typeof a&&Object.prototype.hasOwnProperty.call(a,e)&&this.attributeToString(a[e])||""),i}async updateJamesIdentity(t,e,a,i={}){const s=(this.identityQueue.get(e)??Promise.resolve()).then(()=>this.doUpdateJamesIdentity(t,e,a,i));this.identityQueue.set(e,s);try{await s}finally{this.identityQueue.get(e)===s&&this.identityQueue.delete(e)}}async doUpdateJamesIdentity(e,a,i,s){const r={plugin:this.name,event:"onLdapDisplayNameChange",result:"error",dn:e,mail:a,displayName:i};for(let n=1;;n++){try{const l=`${this.webadminUrl}/users/${a}/identities`,d=await this.requestLimit(()=>t(`${l}?default=true`,{method:"GET",headers:this.createHeaders()}));let u;if(d.ok)u=pickDefaultIdentity(await d.json());else if(404!==d.status){const t="get_default_identity";if(this.shouldRetry(d.status,o,n)){await this.warnAndWait(r,t,n,d);continue}return void this.logger.error({...r,step:t,...n>1?{attempts:n}:{},http_status:d.status,http_status_text:d.statusText})}const m=await this.generateSignature(e),h=u?.id?{id:u.id,email:u.email,name:i}:{email:a,name:i,sortOrder:0};m?h.htmlSignature=m:Object.assign(h,s);const c=u?.id?"update_identity":"create_identity",g=await this.requestLimit(()=>t(u?.id?`${l}/${u.id}`:l,{method:u?.id?"PUT":"POST",headers:this.createHeaders("application/json"),body:JSON.stringify(h)}));if(g.ok)this.logger.info({...r,step:c,result:"success",http_status:g.status});else{if(this.shouldRetry(g.status,o,n)){await this.warnAndWait(r,c,n,g);continue}this.logger.error({...r,step:c,...n>1?{attempts:n}:{},http_status:g.status,http_status_text:g.statusText})}}catch(t){this.logger.error({...r,error:`${t}`})}return}}async defaultSignatures(e){try{const a=await this.requestLimit(()=>t(`${this.webadminUrl}/users/${e}/identities?default=true`,{method:"GET",headers:this.createHeaders()}));if(!a.ok)return{};const i=pickDefaultIdentity(await a.json()),s={};return i?.htmlSignature&&(s.htmlSignature=i.htmlSignature),i?.textSignature&&(s.textSignature=i.textSignature),s}catch(t){return this.logger.debug(`Could not read the identities of ${e}: ${t}`),{}}}ownForwards(t){return this.config.james_sync_forwards?this.getAliases(t):[]}async dropStaleForwards(e,a,i){let s;try{const r=await this.requestLimit(()=>t(`${this.webadminUrl}/address/forwards/${a}`,{method:"GET",headers:this.createHeaders()}));if(!r.ok)return void(404!==r.status&&this.logger.warn({plugin:this.name,event:"dropStaleForward",result:"error",dn:e,address:a,step:"list_forwards",http_status:r.status,http_status_text:r.statusText}));const n=i.map(t=>t.toLowerCase());s=(await r.json()).map(t=>t.mailAddress).filter(t=>t&&!n.includes(t.toLowerCase()))}catch(t){return void this.logger.debug(`Could not read the forwards of ${a}: ${t}`)}await Promise.all(s.map(t=>this.callWebAdminApi("dropStaleForward",`${this.webadminUrl}/address/forwards/${a}/targets/${t}`,"DELETE",e,null,{address:a,forward:t})))}async warnAndWait(t,e,a,i){this.logger.warn({...t,step:e,result:"retry",attempt:a,http_status:i.status,http_status_text:i.statusText}),await this.waitBeforeRetry(a)}async deleteUserData(e,a){const i={plugin:this.name,event:"deleteUserData",mail:e,fromStep:a};try{const s=new URL(`${this.webadminUrl}/users/${e}`);s.searchParams.set("action","deleteData"),a&&s.searchParams.set("fromStep",a);const r=await this.requestLimit(()=>t(s.toString(),{method:"POST",headers:this.createHeaders()}));if(!r.ok)return this.logger.error({...i,http_status:r.status,http_status_text:r.statusText}),null;const n=await r.json();return this.logger.info({...i,http_status:r.status,taskId:n.taskId}),n}catch(t){return this.logger.error({...i,error:`${t}`}),null}}getMailboxType(t){const e=t[this.mailboxTypeAttr];if(!e)return null;const a=(Array.isArray(e)?String(e[0]):String(e)).match(/^cn=([^,]+)/);if(!a)return null;const i=a[1];return"group"===i||"mailingList"===i||"teamMailbox"===i?i:null}async createTeamMailbox(t,e,a){const i=this.teamMailboxUrl(a);await this.callWebAdminApi("teamMailbox:create",i,"PUT",t,null,{teamMailbox:a}),await this.teamMailboxMembers(t,a,"PUT",members(e))}async getMemberEmails(t){const e=t.filter(t=>!s(t,this.config.group_dummy_user)).map(t=>this.server.ldap.queryLimit(async()=>{const e=await this.ldapGetAttributes(t,[this.mailAttr]);return e?this.attributeToString(e[this.mailAttr]):null}));return(await Promise.all(e)).filter(t=>null!==t)}async getGroupMail(t){const e=await this.ldapGetAttributes(t,["mail"]);return e?this.attributeToString(e.mail):null}async getGroupMailboxType(t){try{const e=await this.server.ldap.search({paged:!1,scope:"base",attributes:[this.mailboxTypeAttr]},t);if(e.searchEntries&&e.searchEntries.length>0)return this.getMailboxType(e.searchEntries[0])}catch(e){const a=e instanceof Error?e.message:String(e);this.logger.debug(`Could not get mailbox type for group ${t}: ${a}`)}return null}async _handleDelegationChange(t,e){const a=await this.ldapGetAttributes(t,[this.mailAttr]);if(!a)return void this.logger.warn({plugin:this.name,event:"onLdapChange",dn:t,message:"Could not find user entry to get mail attribute"});const i=a[this.mailAttr];if(!i||"string"!=typeof i)return void this.logger.warn({plugin:this.name,event:"onLdapChange",dn:t,message:"User has no mail attribute, cannot manage delegation"});const s=this.config.delegation_attribute;if(!s)return;const[r,n]=e[s]||[],l=this._normalizeToArray(r),d=this._normalizeToArray(n),u=d.filter(t=>!l.includes(t)),m=l.filter(t=>!d.includes(t)),h=u.map(t=>this.server.ldap.queryLimit(async()=>{const e=await this._getDelegateEmail(t);return{dn:t,email:e}})),c=m.map(t=>this.server.ldap.queryLimit(async()=>{const e=await this._getDelegateEmail(t);return{dn:t,email:e}})),[g,p]=await Promise.all([Promise.all(h),Promise.all(c)]),f=[];for(const{dn:e,email:a}of g)a&&f.push(this.callWebAdminApi("onLdapChange:addDelegation",`${this.webadminUrl}/users/${i}/authorizedUsers/${a}`,"PUT",t,null,{userMail:i,delegateEmail:a,delegateDN:e,action:"add"},o));for(const{dn:e,email:a}of p)a&&f.push(this.callWebAdminApi("onLdapChange:removeDelegation",`${this.webadminUrl}/users/${i}/authorizedUsers/${a}`,"DELETE",t,null,{userMail:i,delegateEmail:a,delegateDN:e,action:"remove"}));await Promise.all(f)}async _getDelegateEmail(t){const e=await this.ldapGetAttributes(t,[this.mailAttr]);return e?this.attributeToString(e[this.mailAttr]):(this.logger.warn({plugin:this.name,event:"getDelegateEmail",dn:t,message:"Could not resolve delegate DN to email"}),null)}_normalizeToArray(t){return t?Array.isArray(t)?t:[t]:[]}}export{James as default};
|
|
@@ -170,6 +170,7 @@ export interface Config {
|
|
|
170
170
|
james_retry_delay?: number;
|
|
171
171
|
james_mailing_list_branch?: string[];
|
|
172
172
|
james_mailbox_type_attribute?: string;
|
|
173
|
+
james_sync_forwards?: boolean;
|
|
173
174
|
twake_drive_webadmin_url?: string;
|
|
174
175
|
twake_drive_webadmin_token?: string;
|
|
175
176
|
twake_drive_concurrency?: number;
|
|
@@ -18,4 +18,15 @@ export default class ExternalUsersInGroups extends DmPlugin {
|
|
|
18
18
|
ldap: ldapActions;
|
|
19
19
|
constructor(server: DM);
|
|
20
20
|
hooks: Hooks;
|
|
21
|
+
/**
|
|
22
|
+
* The address of an external member, unescaped: the value of a `mail` RDN
|
|
23
|
+
* right under the branch. Null for any other member.
|
|
24
|
+
*/
|
|
25
|
+
externalAddress(member: string): string | null;
|
|
26
|
+
/** `--mail-domain`, lower-cased: the domains no external member may use */
|
|
27
|
+
managedDomains(): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Where a client sends an external member: `mail=<escaped address>,<branch>`
|
|
30
|
+
*/
|
|
31
|
+
getConfigApiData(): Record<string, unknown>;
|
|
21
32
|
}
|
|
@@ -5,6 +5,16 @@ import type { DM } from '../../bin';
|
|
|
5
5
|
import type { AttributeValue } from '../../lib/ldapActions';
|
|
6
6
|
import { Hooks } from '../../hooks';
|
|
7
7
|
import type { ChangesToNotify } from '../ldap/onChange';
|
|
8
|
+
type Identity = {
|
|
9
|
+
id: string;
|
|
10
|
+
email: string;
|
|
11
|
+
mayDelete?: boolean;
|
|
12
|
+
sortOrder?: number;
|
|
13
|
+
htmlSignature?: string;
|
|
14
|
+
textSignature?: string;
|
|
15
|
+
};
|
|
16
|
+
/** Signatures an identity carries over, see {@link Identity} */
|
|
17
|
+
export type Signatures = Pick<Identity, 'htmlSignature' | 'textSignature'>;
|
|
8
18
|
export default class James extends TwakePlugin {
|
|
9
19
|
name: string;
|
|
10
20
|
roles: Role[];
|
|
@@ -15,6 +25,7 @@ export default class James extends TwakePlugin {
|
|
|
15
25
|
private quotaAttr;
|
|
16
26
|
private aliasAttr;
|
|
17
27
|
private mailboxTypeAttr;
|
|
28
|
+
private forwardAttr;
|
|
18
29
|
private initDelay;
|
|
19
30
|
constructor(server: DM);
|
|
20
31
|
/**
|
|
@@ -27,16 +38,30 @@ export default class James extends TwakePlugin {
|
|
|
27
38
|
private getUserQuota;
|
|
28
39
|
hooks: Hooks;
|
|
29
40
|
/**
|
|
30
|
-
* The address of the team mailbox an entry stands for, and
|
|
31
|
-
* refused
|
|
41
|
+
* The address of the team mailbox an entry stands for, and why it is
|
|
42
|
+
* refused, if it is: it lies in a mailing list branch, or TMail cannot
|
|
43
|
+
* name a team mailbox after its address.
|
|
32
44
|
*/
|
|
33
45
|
private teamMailboxOf;
|
|
46
|
+
/**
|
|
47
|
+
* Give James what an account holds: quota, aliases and identity, after
|
|
48
|
+
* dropping the forwards left on its addresses. Runs when an entry is
|
|
49
|
+
* added with a mail, or gains one.
|
|
50
|
+
*/
|
|
51
|
+
private provisionAccount;
|
|
34
52
|
/**
|
|
35
53
|
* Bring James from the team mailbox an entry was to the one it is. A
|
|
36
54
|
* mailbox the entry no longer stands for is kept, emptied of its members.
|
|
37
55
|
*/
|
|
38
56
|
private followTeamMailbox;
|
|
39
57
|
private teamMailboxQueue;
|
|
58
|
+
/**
|
|
59
|
+
* The WebAdmin path of the team mailbox at an address
|
|
60
|
+
* {@link teamMailboxOf} accepted: TMail names it by the local part, under
|
|
61
|
+
* its domain. Lower-cased, so that a change of case only, which keeps the
|
|
62
|
+
* mailbox, keeps its path.
|
|
63
|
+
*/
|
|
64
|
+
private teamMailboxUrl;
|
|
40
65
|
private teamMailboxMembers;
|
|
41
66
|
/**
|
|
42
67
|
* Extract display name from LDAP attributes
|
|
@@ -62,10 +87,35 @@ export default class James extends TwakePlugin {
|
|
|
62
87
|
* Calls for one address run one after the other: an add triggers this twice
|
|
63
88
|
* (ldapadddone, then the display name "change" onChange publishes), and two
|
|
64
89
|
* concurrent first calls would both find no default and both create one.
|
|
90
|
+
*
|
|
91
|
+
* `signatures` are written when `--james-signature-template` is not set:
|
|
92
|
+
* those of the identity the user had before a rename.
|
|
65
93
|
*/
|
|
66
|
-
updateJamesIdentity(dn: string, mail: string, displayName: string): Promise<void>;
|
|
94
|
+
updateJamesIdentity(dn: string, mail: string, displayName: string, signatures?: Signatures): Promise<void>;
|
|
67
95
|
private identityQueue;
|
|
68
96
|
private doUpdateJamesIdentity;
|
|
97
|
+
/**
|
|
98
|
+
* The signatures of an address's default identity, none when it has none
|
|
99
|
+
* or James cannot be read.
|
|
100
|
+
*/
|
|
101
|
+
private defaultSignatures;
|
|
102
|
+
/**
|
|
103
|
+
* The forwards the directory gives an entry, those James is to keep on its
|
|
104
|
+
* address: none unless the directory owns forwards
|
|
105
|
+
* (`--james-sync-forwards`).
|
|
106
|
+
*/
|
|
107
|
+
private ownForwards;
|
|
108
|
+
/**
|
|
109
|
+
* Remove the forwards James holds from an address an entry takes, but
|
|
110
|
+
* those in `keep`: the entry's own. A rename leaves one from the old
|
|
111
|
+
* address to the new, and its next owner would not get its mail.
|
|
112
|
+
*
|
|
113
|
+
* Runs whether forwards are synced or not: Twake Mail keeps a user's
|
|
114
|
+
* forwards under their own address, and none can have been set yet on an
|
|
115
|
+
* address the account is only now taking. The rename calls this before
|
|
116
|
+
* James moves the user's forwards to the new address.
|
|
117
|
+
*/
|
|
118
|
+
private dropStaleForwards;
|
|
69
119
|
private warnAndWait;
|
|
70
120
|
/**
|
|
71
121
|
* Delete user data via James WebAdmin API
|
|
@@ -101,3 +151,4 @@ export default class James extends TwakePlugin {
|
|
|
101
151
|
_getDelegateEmail(dn: string): Promise<string | null>;
|
|
102
152
|
_normalizeToArray(value: AttributeValue | null): string[];
|
|
103
153
|
}
|
|
154
|
+
export {};
|
|
@@ -245,6 +245,8 @@ curl -H "Accept: application/json" \
|
|
|
245
245
|
- `features.ldapFlatGeneric.flatResources`: Array of flat LDAP resources (users, positions, etc.)
|
|
246
246
|
- `features.ldapGroups`: Group management configuration (if enabled)
|
|
247
247
|
- `features.ldapOrganizations`: Organization tree configuration (if enabled)
|
|
248
|
+
- `features.externalUsersInGroups`: where to send an external group member
|
|
249
|
+
(if enabled), [details](../../usage/plugins/ldap/external-users.md#config-api)
|
|
248
250
|
- `features.openidconnect.endpoints.logout`: the logout route, when OpenID Connect
|
|
249
251
|
guards the server ([details](../../usage/plugins/auth/oidc.md#logout))
|
|
250
252
|
|
|
@@ -452,18 +452,19 @@ whichever of the two names it selects. A value other than `req.user` or
|
|
|
452
452
|
|
|
453
453
|
#### `core/twake/james`
|
|
454
454
|
|
|
455
|
-
| CLI | Plural | Env | Default | Description
|
|
456
|
-
| -------------------------------- | ------------------------------- | --------------------------------- | ----------------------- |
|
|
457
|
-
| `--james-webadmin-url` | | `DM_JAMES_WEBADMIN_URL` | `http://localhost:8000` | James WebAdmin API URL
|
|
458
|
-
| `--james-webadmin-token` | | `DM_JAMES_WEBADMIN_TOKEN` | | James authentication token
|
|
459
|
-
| `--james-signature-template` | | `DM_JAMES_SIGNATURE_TEMPLATE` | | Email signature template
|
|
460
|
-
| `--james-concurrency` | | `DM_JAMES_CONCURRENCY` | `10` | James API concurrency
|
|
461
|
-
| `--james-init-delay` | | `DM_JAMES_INIT_DELAY` | `1000` | Init delay (ms)
|
|
462
|
-
| `--james-retry-attempts` | | `DM_JAMES_RETRY_ATTEMPTS` | `5` | Attempts per user call
|
|
463
|
-
| `--james-retry-delay` | | `DM_JAMES_RETRY_DELAY` | `1000` | Base retry delay (ms)
|
|
464
|
-
| `--james-mailing-list-branch` | `--james-mailing-list-branches` | `DM_JAMES_MAILING_LIST_BRANCHES` | `[]` | Branches refused to team mailboxes
|
|
465
|
-
| `--james-mailbox-type-attribute` | | `DM_JAMES_MAILBOX_TYPE_ATTRIBUTE` | `twakeMailboxType` | Mailbox type attribute
|
|
466
|
-
| `--
|
|
455
|
+
| CLI | Plural | Env | Default | Description |
|
|
456
|
+
| -------------------------------- | ------------------------------- | --------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------- |
|
|
457
|
+
| `--james-webadmin-url` | | `DM_JAMES_WEBADMIN_URL` | `http://localhost:8000` | James WebAdmin API URL |
|
|
458
|
+
| `--james-webadmin-token` | | `DM_JAMES_WEBADMIN_TOKEN` | | James authentication token |
|
|
459
|
+
| `--james-signature-template` | | `DM_JAMES_SIGNATURE_TEMPLATE` | | Email signature template |
|
|
460
|
+
| `--james-concurrency` | | `DM_JAMES_CONCURRENCY` | `10` | James API concurrency |
|
|
461
|
+
| `--james-init-delay` | | `DM_JAMES_INIT_DELAY` | `1000` | Init delay (ms) |
|
|
462
|
+
| `--james-retry-attempts` | | `DM_JAMES_RETRY_ATTEMPTS` | `5` | Attempts per user call |
|
|
463
|
+
| `--james-retry-delay` | | `DM_JAMES_RETRY_DELAY` | `1000` | Base retry delay (ms) |
|
|
464
|
+
| `--james-mailing-list-branch` | `--james-mailing-list-branches` | `DM_JAMES_MAILING_LIST_BRANCHES` | `[]` | Branches refused to team mailboxes |
|
|
465
|
+
| `--james-mailbox-type-attribute` | | `DM_JAMES_MAILBOX_TYPE_ATTRIBUTE` | `twakeMailboxType` | Mailbox type attribute |
|
|
466
|
+
| `--james-sync-forwards` | | `DM_JAMES_SYNC_FORWARDS` | `false` | Copy LDAP forwards to Twake Mail — [details](plugins/integrations/james-mail.md#forwards) |
|
|
467
|
+
| `--ldap-concurrency` | | `DM_LDAP_CONCURRENCY` | `10` | LDAP operations concurrency |
|
|
467
468
|
|
|
468
469
|
The wait before each retry grows with the attempt, so the total wait is `--james-retry-delay` × n(n−1)/2 for n attempts: 10 s with the defaults, 190 s with 20 attempts.
|
|
469
470
|
|
|
@@ -39,6 +39,8 @@ DM_JAMES_WEBADMIN_TOKEN="your-admin-token"
|
|
|
39
39
|
- `--mail-attribute`: LDAP attribute for email (default: `mail`)
|
|
40
40
|
- `--quota-attribute`: LDAP attribute for quota (default: `mailQuotaSize`)
|
|
41
41
|
- `--alias-attribute`: LDAP attribute for email aliases (default: `mailAlternateAddress`)
|
|
42
|
+
- `--forward-attribute`: LDAP attribute for forwards (default: `mailForwardingAddress`)
|
|
43
|
+
- `--james-sync-forwards`: copy that attribute to Twake Mail's forwards (default: `false`), see [Forwards](#forwards)
|
|
42
44
|
- `--james-mailbox-type-attribute`: LDAP attribute for mailbox type (default: `twakeMailboxType`)
|
|
43
45
|
- `--james-retry-attempts`: attempts in all of a call about a user James may not see yet (default: `5`), see [Replication lag](#replication-lag)
|
|
44
46
|
- `--james-retry-delay`: base delay between those attempts, in ms (default: `1000`); the total wait is delay × n(n−1)/2 for n attempts
|
|
@@ -87,7 +89,29 @@ When a user's mail attribute changes:
|
|
|
87
89
|
POST /users/old@company.com/rename/john.doe@company.com?action=rename
|
|
88
90
|
```
|
|
89
91
|
|
|
90
|
-
4. **James** renames the mail account and
|
|
92
|
+
4. **James** renames the mail account and its mailboxes, and adds a forward
|
|
93
|
+
from the old address to the new one, so that mail sent to the old address
|
|
94
|
+
still arrives. It leaves the identities under the old address.
|
|
95
|
+
|
|
96
|
+
5. **James plugin** moves the aliases (see
|
|
97
|
+
[Aliases and Mail Changes](#aliases-and-mail-changes)) and gives the new
|
|
98
|
+
address its [default identity](#display-name-and-identity) back, with the
|
|
99
|
+
signatures of the old one when `--james-signature-template` is not set.
|
|
100
|
+
|
|
101
|
+
#### Forwards Left on an Address
|
|
102
|
+
|
|
103
|
+
The forward a rename leaves outlives the account. When an address becomes the
|
|
104
|
+
`mail` or an alias of an account, at its creation, a rename, a mail added to
|
|
105
|
+
an existing entry or an alias add, the plugin removes the forwards James holds
|
|
106
|
+
from it, except the entry's own when the directory owns forwards
|
|
107
|
+
(`--forward-attribute`, with `--james-sync-forwards`):
|
|
108
|
+
|
|
109
|
+
```http
|
|
110
|
+
GET /address/forwards/old@company.com
|
|
111
|
+
DELETE /address/forwards/old@company.com/targets/john.doe@company.com
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Otherwise the new owner of the address would not get its mail.
|
|
91
115
|
|
|
92
116
|
### Quota Changes
|
|
93
117
|
|
|
@@ -115,11 +139,33 @@ When a user's quota changes:
|
|
|
115
139
|
5000000000
|
|
116
140
|
```
|
|
117
141
|
|
|
142
|
+
### Forwards
|
|
143
|
+
|
|
144
|
+
Forwards are a setting of the user in Twake Mail, beside "keep a copy in my
|
|
145
|
+
mailbox", and Twake Mail Admin can change them too. By default the plugin
|
|
146
|
+
does not copy `--forward-attribute` to them. It still removes the
|
|
147
|
+
[forwards left](#forwards-left-on-an-address) on an address an account takes:
|
|
148
|
+
Twake Mail keeps a user's forwards under their own address, and none can have
|
|
149
|
+
been set on an address the account is only now taking.
|
|
150
|
+
|
|
151
|
+
With `--james-sync-forwards`, the directory owns them: each change of
|
|
152
|
+
`--forward-attribute` is applied to Twake Mail, and overwrites what the user
|
|
153
|
+
set there.
|
|
154
|
+
|
|
155
|
+
```http
|
|
156
|
+
PUT /address/forwards/jdoe@company.com/targets/john@partner.com
|
|
157
|
+
DELETE /address/forwards/jdoe@company.com/targets/old@partner.com
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Only the change is applied: a forward the user added in Twake Mail stays
|
|
161
|
+
until the directory names it and removes it, and the directory is never told
|
|
162
|
+
about the forwards set in Twake Mail.
|
|
163
|
+
|
|
118
164
|
### Display Name and Identity
|
|
119
165
|
|
|
120
|
-
When a user is added, or their display name changes, the plugin names
|
|
121
|
-
default identity in Twake Mail after the directory. The name is the first
|
|
122
|
-
found in `--display-name-attribute` (`displayName` by default), `cn`, then
|
|
166
|
+
When a user is added, renamed, or their display name changes, the plugin names
|
|
167
|
+
their default identity in Twake Mail after the directory. The name is the first
|
|
168
|
+
one found in `--display-name-attribute` (`displayName` by default), `cn`, then
|
|
123
169
|
`givenName` and `sn`. The optional signature is `--james-signature-template`.
|
|
124
170
|
|
|
125
171
|
1. **James plugin** reads the default identity, the deletable one with the
|
|
@@ -226,7 +272,8 @@ When the primary email address changes, all aliases are automatically updated:
|
|
|
226
272
|
PUT /address/aliases/john.smith@company.com/sources/jdoe@company.com
|
|
227
273
|
```
|
|
228
274
|
|
|
229
|
-
|
|
275
|
+
James forwards the old address to the new one, see
|
|
276
|
+
[Forwards Left on an Address](#forwards-left-on-an-address).
|
|
230
277
|
|
|
231
278
|
#### Active Directory Format Support
|
|
232
279
|
|
|
@@ -430,14 +477,19 @@ const signature = await james.generateSignature(
|
|
|
430
477
|
|
|
431
478
|
---
|
|
432
479
|
|
|
433
|
-
### updateJamesIdentity(dn, mail, displayName)
|
|
480
|
+
### updateJamesIdentity(dn, mail, displayName, signatures?)
|
|
434
481
|
|
|
435
482
|
Update a user's JMAP identity in James (name and optional signature).
|
|
436
483
|
|
|
437
484
|
**Signature:**
|
|
438
485
|
|
|
439
486
|
```typescript
|
|
440
|
-
async updateJamesIdentity(
|
|
487
|
+
async updateJamesIdentity(
|
|
488
|
+
dn: string,
|
|
489
|
+
mail: string,
|
|
490
|
+
displayName: string,
|
|
491
|
+
signatures?: { htmlSignature?: string; textSignature?: string }
|
|
492
|
+
): Promise<void>
|
|
441
493
|
```
|
|
442
494
|
|
|
443
495
|
**Parameters:**
|
|
@@ -445,6 +497,8 @@ async updateJamesIdentity(dn: string, mail: string, displayName: string): Promis
|
|
|
445
497
|
- `dn` (string): LDAP Distinguished Name (used for signature generation)
|
|
446
498
|
- `mail` (string): User's email address
|
|
447
499
|
- `displayName` (string): New display name for the identity
|
|
500
|
+
- `signatures` (object, optional): written when `--james-signature-template`
|
|
501
|
+
is not set
|
|
448
502
|
|
|
449
503
|
**Example:**
|
|
450
504
|
|
|
@@ -551,6 +605,8 @@ const type = await james.getGroupMailboxType(
|
|
|
551
605
|
| Remove delegation | `/users/{mail}/authorizedUsers/{delegate}` | DELETE |
|
|
552
606
|
| Add alias | `/address/aliases/{mail}/sources/{alias}` | PUT |
|
|
553
607
|
| Remove alias | `/address/aliases/{mail}/sources/{alias}` | DELETE |
|
|
608
|
+
| List forwards | `/address/forwards/{mail}` | GET |
|
|
609
|
+
| Remove forward | `/address/forwards/{mail}/targets/{to}` | DELETE |
|
|
554
610
|
|
|
555
611
|
### Authentication
|
|
556
612
|
|
|
@@ -783,9 +839,10 @@ Quota values are in **bytes**:
|
|
|
783
839
|
|
|
784
840
|
### Scenario 1: User Creation
|
|
785
841
|
|
|
786
|
-
1. Create user in LDAP with mail attribute
|
|
787
|
-
2. James plugin
|
|
788
|
-
|
|
842
|
+
1. Create user in LDAP with mail attribute: James reads its users from LDAP
|
|
843
|
+
2. James plugin sets the quota, the aliases and the default identity, after
|
|
844
|
+
dropping the [forwards left](#forwards-left-on-an-address) on its addresses
|
|
845
|
+
3. A mail added later to an existing entry is handled the same way
|
|
789
846
|
4. Future mail/quota changes are synced automatically
|
|
790
847
|
|
|
791
848
|
### Scenario 2: Mail Address Change
|
|
@@ -97,8 +97,8 @@ await dm.ldap.add('cn=sales-team,ou=groups,dc=example,dc=com', {
|
|
|
97
97
|
|
|
98
98
|
This will:
|
|
99
99
|
|
|
100
|
-
1. Create the team mailbox via `PUT /domains/example.com/team-mailboxes/sales
|
|
101
|
-
2. Add each member via `PUT /domains/example.com/team-mailboxes/sales
|
|
100
|
+
1. Create the team mailbox via `PUT /domains/example.com/team-mailboxes/sales`
|
|
101
|
+
2. Add each member via `PUT /domains/example.com/team-mailboxes/sales/members/{member-email}`
|
|
102
102
|
|
|
103
103
|
### Creating a Mailing List
|
|
104
104
|
|
|
@@ -159,9 +159,16 @@ address cannot be read.
|
|
|
159
159
|
|
|
160
160
|
### Team Mailboxes
|
|
161
161
|
|
|
162
|
-
- **Create**: `PUT /domains/{domain}/team-mailboxes/{
|
|
163
|
-
- **Add member**: `PUT /domains/{domain}/team-mailboxes/{
|
|
164
|
-
- **Delete member**: `DELETE /domains/{domain}/team-mailboxes/{
|
|
162
|
+
- **Create**: `PUT /domains/{domain}/team-mailboxes/{name}`
|
|
163
|
+
- **Add member**: `PUT /domains/{domain}/team-mailboxes/{name}/members/{memberEmail}`
|
|
164
|
+
- **Delete member**: `DELETE /domains/{domain}/team-mailboxes/{name}/members/{memberEmail}`
|
|
165
|
+
|
|
166
|
+
TMail names a team mailbox by the local part of its address, `{name}@{domain}`,
|
|
167
|
+
and accepts only letters, digits, `#`, `-` and `_` in it, up to 255 characters.
|
|
168
|
+
A group whose address has any other character, such as a dot, gets no team
|
|
169
|
+
mailbox: the plugin logs an error once, when the group comes to have such an
|
|
170
|
+
address, and does not call TMail. The name is lower-cased, so a change of case
|
|
171
|
+
of the address keeps the same team mailbox.
|
|
165
172
|
|
|
166
173
|
## Validation Rules
|
|
167
174
|
|
|
@@ -220,7 +227,7 @@ await dm.ldap.modify('cn=sales,ou=groups,dc=example,dc=com', {
|
|
|
220
227
|
|
|
221
228
|
**What happens:**
|
|
222
229
|
|
|
223
|
-
1. Creates the team mailbox (`PUT /domains/{domain}/team-mailboxes/{
|
|
230
|
+
1. Creates the team mailbox (`PUT /domains/{domain}/team-mailboxes/{name}`)
|
|
224
231
|
2. Adds all current members to the team mailbox
|
|
225
232
|
|
|
226
233
|
#### teamMailbox → mailingList
|
|
@@ -236,7 +243,7 @@ await dm.ldap.modify('cn=sales,ou=groups,dc=example,dc=com', {
|
|
|
236
243
|
|
|
237
244
|
**What happens:**
|
|
238
245
|
|
|
239
|
-
1. Removes all members from the team mailbox (`DELETE /domains/{domain}/team-mailboxes/{
|
|
246
|
+
1. Removes all members from the team mailbox (`DELETE /domains/{domain}/team-mailboxes/{name}/members/{member}`)
|
|
240
247
|
2. **Preserves the team mailbox** (does NOT delete it - can be recovered later)
|
|
241
248
|
|
|
242
249
|
#### group → mailingList or teamMailbox
|