Page MenuHomePhorge

D4130.1775450386.diff
No OneTemporary

Authored By
Unknown
Size
132 KB
Referenced Files
None
Subscribers
None

D4130.1775450386.diff

diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -3,8 +3,7 @@
* Make sure you have docker and docker-compose available.
* Change to the base directory of this repository.
* Run 'HOST=kolab.local ADMIN_PASSWORD="simple123" bin/configure.sh config.prod' to configure this deployment.
-* Run 'bin/deploy.sh' to start the deployment.
-* Run 'docker exec -w /src/kolabsrc/ kolab-webapp ./artisan user:password admin@kolab.local simple123' to set your admin password
+* Run 'env ADMIN_PASSWORD="simple123" bin/deploy.sh' to start the deployment.
* Add an /etc/hosts entry "127.0.0.1 kolab.local"
* navigate to https://kolab.local
* login as "admin@kolab.local" with password "simple123" (or whatever you have set), and create your users.
diff --git a/ansible/setup.yml b/ansible/setup.yml
--- a/ansible/setup.yml
+++ b/ansible/setup.yml
@@ -58,6 +58,7 @@
FIREBASE_API_KEY: "{{ firebase_api_key }}"
PUBLIC_IP: "{{ public_ip }}"
ADMIN_PASSWORD: "{{ admin_password }}"
+ KOLAB_GIT_REF: "{{ git_branch }}"
- name: Permit receiving mail
@@ -122,15 +123,10 @@
ansible.builtin.command: bin/deploy.sh
args:
chdir: /home/kolab/kolab
+ environment:
+ ADMIN_PASSWORD: "{{ admin_password }}"
register: result
always:
- name: Print output from previous task with newlines
ansible.builtin.debug:
msg="{{result.stdout_lines}}"
-
- - name: Set admin password
- become: true
- become_user: kolab
- ansible.builtin.command: docker exec -w /src/kolabsrc/ kolab-webapp ./artisan user:password admin@{{ hostname }} {{ admin_password|quote }}
- args:
- chdir: /home/kolab/kolab
diff --git a/bin/configure.sh b/bin/configure.sh
--- a/bin/configure.sh
+++ b/bin/configure.sh
@@ -78,6 +78,10 @@
echo "PASSPORT_PUBLIC_KEY=\"${PASSPORT_PUBLIC_KEY}\"" >> src/.env
fi
+if ! grep -q "KOLAB_GIT_REF=" .env; then
+ echo "KOLAB_GIT_REF=${KOLAB_GIT_REF:-master}" >> src/.env
+fi
+
# Customize configuration
sed -i \
-e "s/{{ host }}/${HOST:-kolab.local}/g" \
diff --git a/bin/deploy.sh b/bin/deploy.sh
--- a/bin/deploy.sh
+++ b/bin/deploy.sh
@@ -1,2 +1,6 @@
#!/bin/bash
bin/quickstart.sh --nodev
+if [[ -n $ADMIN_PASSWORD ]]; then
+ DOMAIN=$(grep APP_DOMAIN .env | tail -n1 | sed "s/APP_DOMAIN=//")
+ docker exec -w /src/kolabsrc/ kolab-webapp ./artisan user:password "admin@$DOMAIN" "$ADMIN_PASSWORD"
+fi
diff --git a/bin/quickstart.sh b/bin/quickstart.sh
--- a/bin/quickstart.sh
+++ b/bin/quickstart.sh
@@ -20,7 +20,7 @@
export DOCKER_BUILDKIT=0
-docker-compose down --remove-orphans
+docker-compose down -t 1 --remove-orphans
docker volume rm kolab_mariadb || :
docker volume rm kolab_imap || :
docker volume rm kolab_ldap || :
@@ -33,8 +33,8 @@
pkill -9 -f swoole || :
bin/regen-certs
-docker-compose build coturn kolab mariadb meet pdns proxy redis haproxy roundcube
-docker-compose up -d coturn kolab mariadb meet pdns redis roundcube
+docker-compose build coturn ldap kolab mariadb meet pdns proxy redis haproxy roundcube
+docker-compose up -d coturn ldap kolab mariadb meet pdns redis roundcube
# Workaround until we have docker-compose --wait (https://github.com/docker/compose/pull/8777)
function wait_for_container {
diff --git a/bin/regen-certs b/bin/regen-certs
--- a/bin/regen-certs
+++ b/bin/regen-certs
@@ -30,7 +30,7 @@
exit 1
fi
-export $(cat ${base_dir}/src/.env | xargs) >/dev/null 2>&1
+APP_DOMAIN=$(grep APP_DOMAIN .env | tail -n1 | sed "s/APP_DOMAIN=//")
for name in kolab.mgmt.com kolab.hosted.com imap.hosted.com {{admin,meet}.,}${APP_DOMAIN}; do
openssl genrsa -out ${cert_dir}/${name}.key 4096
diff --git a/ci/Makefile b/ci/Makefile
--- a/ci/Makefile
+++ b/ci/Makefile
@@ -13,7 +13,7 @@
lint:
docker kill kolab-tests || : ; \
- kolab rm kolab-tests || : ; \
+ docker rm kolab-tests || : ; \
docker run -v ${PWD}/../:/src/kolab.orig -t kolab-tests /lint.sh
test:
diff --git a/config.demo/src/.env b/config.demo/src/.env
--- a/config.demo/src/.env
+++ b/config.demo/src/.env
@@ -18,7 +18,7 @@
APP_WITH_FILES=1
APP_LDAP=1
-APP_IMAP=0
+APP_IMAP=1
APP_HEADER_CSP="connect-src 'self'; child-src 'self'; font-src 'self'; form-action 'self' data:; frame-ancestors 'self'; img-src blob: data: 'self' *; media-src 'self'; object-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; default-src 'self';"
APP_HEADER_XFO=sameorigin
@@ -69,7 +69,7 @@
LDAP_BASE_DN="dc=mgmt,dc=com"
LDAP_DOMAIN_BASE_DN="ou=Domains,dc=mgmt,dc=com"
-LDAP_HOSTS=kolab
+LDAP_HOSTS=ldap
LDAP_PORT=389
LDAP_SERVICE_BIND_DN="uid=kolab-service,ou=Special Users,dc=mgmt,dc=com"
LDAP_SERVICE_BIND_PW="Welcome2KolabSystems"
diff --git a/config.dev/docker-compose.override.yml b/config.dev/docker-compose.override.yml
--- a/config.dev/docker-compose.override.yml
+++ b/config.dev/docker-compose.override.yml
@@ -2,7 +2,6 @@
services:
kolab:
ports:
- - "389:389"
- "8880:8880"
- "8443:8443"
- "10143:10143"
@@ -13,6 +12,9 @@
mariadb:
ports:
- "3306:3306"
+ ldap:
+ ports:
+ - "389:389"
redis:
ports:
- "6379:6379"
diff --git a/config.prod/src/.env b/config.prod/src/.env
--- a/config.prod/src/.env
+++ b/config.prod/src/.env
@@ -68,7 +68,7 @@
LDAP_BASE_DN="dc=mgmt,dc=com"
LDAP_DOMAIN_BASE_DN="ou=Domains,dc=mgmt,dc=com"
-LDAP_HOSTS=kolab
+LDAP_HOSTS=ldap
LDAP_PORT=389
LDAP_SERVICE_BIND_DN="uid=kolab-service,ou=Special Users,dc=mgmt,dc=com"
LDAP_SERVICE_BIND_PW="{{ admin_password }}"
diff --git a/docker-compose.build.yml b/docker-compose.build.yml
--- a/docker-compose.build.yml
+++ b/docker-compose.build.yml
@@ -4,7 +4,7 @@
build:
context: ./docker/swoole/
container_name: kolab-swoole
- image: apheleia/swoole:4.8.x
+ image: apheleia/swoole
tests:
build:
context: ./docker/tests/
diff --git a/docker-compose.yml b/docker-compose.yml
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -24,24 +24,32 @@
DB_KOLAB_DATABASE: kolab
DB_KOLAB_USERNAME: kolab
DB_KOLAB_PASSWORD: ${DB_PASSWORD:?"DB_PASSWORD is missing"}
+ LDAP_HOST: ldap
+ LDAP_ADMIN_BIND_DN: ${LDAP_ADMIN_BIND_DN}
+ LDAP_ADMIN_BIND_PW: ${LDAP_ADMIN_BIND_PW}
+ LDAP_SERVICE_BIND_PW: ${LDAP_SERVICE_BIND_PW}
+ IMAP_ADMIN_LOGIN: ${IMAP_ADMIN_LOGIN}
+ IMAP_ADMIN_PASSWORD: ${IMAP_ADMIN_PASSWORD}
container_name: kolab
privileged: true
restart: on-failure
+ tty: true
depends_on:
mariadb:
condition: service_healthy
pdns:
condition: service_healthy
+ ldap:
+ condition: service_healthy
extra_hosts:
- "kolab.mgmt.com:127.0.0.1"
- "services.${APP_DOMAIN}:172.18.0.4"
environment:
- APP_DOMAIN=${APP_DOMAIN}
- - LDAP_HOST=127.0.0.1
+ - LDAP_HOST=ldap
- LDAP_ADMIN_BIND_DN=${LDAP_ADMIN_BIND_DN}
- LDAP_ADMIN_BIND_PW=${LDAP_ADMIN_BIND_PW}
- LDAP_SERVICE_BIND_PW=${LDAP_SERVICE_BIND_PW}
- - LDAP_HOSTED_BIND_PW=${LDAP_HOSTED_BIND_PW}
- DB_HOST=mariadb
- DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- DB_HKCCP_DATABASE=${DB_DATABASE}
@@ -91,11 +99,45 @@
- ./docker/kolab/utils:/root/utils:ro
- /sys/fs/cgroup:/sys/fs/cgroup:ro
- imap:/imapdata
+
+ ldap:
+ build:
+ context: ./docker/ldap/
+ container_name: kolab-ldap
+ restart: on-failure
+ tty: true
+ hostname: ldap
+ privileged: true
+ environment:
+ - APP_DOMAIN=${APP_DOMAIN}
+ - LDAP_ADMIN_ROOT_DN=${LDAP_ADMIN_ROOT_DN}
+ - LDAP_ADMIN_BIND_DN=${LDAP_ADMIN_BIND_DN}
+ - LDAP_ADMIN_BIND_PW=${LDAP_ADMIN_BIND_PW}
+ - LDAP_SERVICE_BIND_PW=${LDAP_SERVICE_BIND_PW}
+ - LDAP_HOSTED_BIND_PW=${LDAP_HOSTED_BIND_PW}
+ - IMAP_ADMIN_PASSWORD=${IMAP_ADMIN_PASSWORD}
+ healthcheck:
+ interval: 10s
+ test: "systemctl status dirsrv@kolab || exit 1"
+ timeout: 5s
+ retries: 30
+ start_period: 5m
+ image: kolab-ldap
+ networks:
+ kolab:
+ ipv4_address: 172.18.0.12
+ tmpfs:
+ - /run
+ - /tmp
+ - /var/run
+ - /var/tmp
+ volumes:
+ - /sys/fs/cgroup:/sys/fs/cgroup:ro
- ldap:/ldapdata
roundcube:
build:
context: ./docker/roundcube/
- container_name: roundcube
+ container_name: kolab-roundcube
hostname: roundcube.hosted.com
restart: on-failure
depends_on:
@@ -107,7 +149,7 @@
condition: service_healthy
environment:
- APP_DOMAIN=${APP_DOMAIN}
- - LDAP_HOST=kolab
+ - LDAP_HOST=ldap
- LDAP_ADMIN_BIND_DN=${LDAP_ADMIN_BIND_DN}
- LDAP_ADMIN_BIND_PW=${LDAP_ADMIN_BIND_PW}
- LDAP_SERVICE_BIND_PW=${LDAP_SERVICE_BIND_PW}
@@ -198,6 +240,7 @@
DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD}
container_name: kolab-pdns
restart: on-failure
+ tty: true
hostname: pdns
depends_on:
mariadb:
@@ -237,6 +280,8 @@
webapp:
build:
context: ./docker/webapp/
+ args:
+ GIT_REF: ${KOLAB_GIT_REF:-master}
container_name: kolab-webapp
restart: on-failure
image: kolab-webapp
@@ -263,6 +308,8 @@
meet:
build:
context: ./docker/meet/
+ args:
+ GIT_REF: ${KOLAB_GIT_REF:-master}
container_name: kolab-meet
restart: on-failure
healthcheck:
diff --git a/docker/kolab/Dockerfile b/docker/kolab/Dockerfile
--- a/docker/kolab/Dockerfile
+++ b/docker/kolab/Dockerfile
@@ -54,10 +54,22 @@
ARG DB_KOLAB_DATABASE
ARG DB_KOLAB_USERNAME
ARG DB_KOLAB_PASSWORD
+ARG LDAP_HOST
+ARG LDAP_ADMIN_BIND_DN
+ARG LDAP_ADMIN_BIND_PW
+ARG LDAP_SERVICE_BIND_PW
+ARG IMAP_ADMIN_LOGIN
+ARG IMAP_ADMIN_PASSWORD
RUN sed -i -r \
-e "s|DB_KOLAB_DATABASE|$DB_KOLAB_DATABASE|g" \
-e "s|DB_KOLAB_USERNAME|$DB_KOLAB_USERNAME|g" \
-e "s|DB_KOLAB_PASSWORD|$DB_KOLAB_PASSWORD|g" \
+ -e "s|LDAP_HOST|$LDAP_HOST|g" \
+ -e "s|LDAP_ADMIN_BIND_DN|$LDAP_ADMIN_BIND_DN|g" \
+ -e "s|LDAP_ADMIN_BIND_PW|$LDAP_ADMIN_BIND_PW|g" \
+ -e "s|LDAP_SERVICE_BIND_PW|$LDAP_SERVICE_BIND_PW|g" \
+ -e "s|IMAP_ADMIN_LOGIN|$IMAP_ADMIN_LOGIN|g" \
+ -e "s|IMAP_ADMIN_PASSWORD|$IMAP_ADMIN_PASSWORD|g" \
/etc/kolab/kolab.conf
RUN mkdir -p /imapdata/{spool,lib} && \
@@ -66,15 +78,8 @@
chmod -R 777 /imapdata && \
chown cyrus:mail /var/spool/imap /var/lib/imap
-RUN mkdir -p /ldapdata/{config,ssca,run} /var/run/dirsrv && \
- ln -s /ldapdata/config /etc/dirsrv/slapd-kolab && \
- ln -s /ldapdata/ssca /etc/dirsrv/ssca && \
- ln -s /ldapdata/run /var/run/dirsrv && \
- chmod -R 777 /ldapdata /etc/dirsrv
-
VOLUME [ "/sys/fs/cgroup" ]
VOLUME [ "/imapdata" ]
-VOLUME [ "/ldapdata" ]
WORKDIR /root/
diff --git a/docker/kolab/imapd.conf b/docker/kolab/imapd.conf
--- a/docker/kolab/imapd.conf
+++ b/docker/kolab/imapd.conf
@@ -32,6 +32,7 @@
delete_mode: delayed
expunge_mode: delayed
postuser: shared
+sharedprefix: shared
# on systems with cyrus 3+ specify search engine
# search_engine: squat
chatty: 1
diff --git a/docker/kolab/kolab-init.sh b/docker/kolab/kolab-init.sh
--- a/docker/kolab/kolab-init.sh
+++ b/docker/kolab/kolab-init.sh
@@ -4,7 +4,6 @@
./01-reverse-etc-hosts.sh && echo "01 done"
./02-write-my.cnf.sh && echo "02 done"
-./03-setup-ldap.sh && echo "03 ldap done"
./03-setup-kolab.sh && echo "03 kolab done"
./04-reset-mysql-kolab-password.sh && echo "04 done"
./05-adjust-configs.sh && echo "05 done"
diff --git a/docker/kolab/kolab.conf b/docker/kolab/kolab.conf
--- a/docker/kolab/kolab.conf
+++ b/docker/kolab/kolab.conf
@@ -12,14 +12,14 @@
virtual_domains = userid
[ldap]
-ldap_uri = ldap://127.0.0.1:389
+ldap_uri = ldap://LDAP_HOST:389
timeout = 10
supported_controls = 0,2,3
base_dn = dc=mgmt,dc=com
-bind_dn = cn=Directory Manager
-bind_pw =
+bind_dn = LDAP_ADMIN_BIND_DN
+bind_pw = LDAP_ADMIN_BIND_PW
service_bind_dn = uid=kolab-service,ou=Special Users,dc=mgmt,dc=com
-service_bind_pw =
+service_bind_pw = LDAP_SERVICE_BIND_PW
user_base_dn = dc=hosted,dc=com
user_scope = sub
user_filter = (objectclass=inetorgperson)
@@ -66,8 +66,8 @@
[cyrus-imap]
uri = imaps://127.0.0.1:11993
-admin_login = cyrus-admin
-admin_password =
+admin_login = IMAP_ADMIN_LOGIN
+admin_password = IMAP_ADMIN_PASSWORD
[cyrus-sasl]
result_attribute = mail
diff --git a/docker/kolab/utils/03-setup-ldap.sh b/docker/kolab/utils/03-setup-ldap.sh
deleted file mode 100755
--- a/docker/kolab/utils/03-setup-ldap.sh
+++ /dev/null
@@ -1,260 +0,0 @@
-#!/bin/bash
-
-. ./settings.sh
-
-cp -av /bin/true /usr/sbin/ds_systemd_ask_password_acl
-
-if [ -f "/etc/dirsrv/slapd-kolab/dse.ldif" ]; then
- echo "LDAP directory exists, nothing to do"
-
- mkdir -p /var/log/dirsrv/slapd-kolab/
- chmod 777 /var/log/dirsrv/slapd-kolab/
- systemctl start dirsrv@kolab
- mkdir /run/dirsrv
- chmod 777 /run/dirsrv
- mkdir -p /run/lock/dirsrv/slapd-kolab/
- chown dirsrv:dirsrv /run/lock/dirsrv/slapd-kolab/
- chmod 777 /run/lock/dirsrv/slapd-kolab/
- mkdir -p /var/lib/dirsrv/slapd-kolab
- chown dirsrv:dirsrv /var/lib/dirsrv/slapd-kolab
-
- systemctl start dirsrv@kolab
-else
- sed -i -e 's/sys.exit/print("exit") #sys.exit/' /usr/lib/python3.6/site-packages/pykolab/setup/setup_ldap.py
-
- echo "LDAP directory does not exist, setting it up."
- CMD="$(which setup-kolab) ldap \
- --default ${LDAP_HOST} \
- --fqdn=kolab.${domain} \
- --directory-manager-pwd=${LDAP_ADMIN_BIND_PW}"
- ${CMD} 2>&1 | tee -a /root/setup-kolab.log
-
-
- # Create hosted kolab service
- (
- echo "dn: uid=hosted-kolab-service,ou=Special Users,${rootdn}"
- echo "objectclass: top"
- echo "objectclass: inetorgperson"
- echo "objectclass: person"
- echo "uid: hosted-kolab-service"
- echo "cn: Hosted Kolab Service Account"
- echo "sn: Service Account"
- echo "givenname: Hosted Kolab"
- echo "userpassword: ${hosted_kolab_service_pw}"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
-
- # Create ou domain
- (
- echo "dn: ou=Domains,${rootdn}"
- echo "ou: Domains"
- echo "objectClass: top"
- echo "objectClass: organizationalunit"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
-
- # Create management domain
- (
- echo "dn: associateddomain=${domain},${domain_base_dn}"
- echo "aci: (targetattr = \"*\")(version 3.0;acl \"Deny Rest\";deny (all)(userdn != \"ldap:///uid=kolab-service,ou=Special Users,${rootdn} || ldap:///${rootdn}??sub?(objectclass=*)\");)"
- echo "aci: (targetattr = \"*\")(version 3.0;acl \"Deny Hosted Kolab\";deny (all)(userdn = \"ldap:///uid=hosted-kolab-service,ou=Special Users,${rootdn}\");)"
- echo "inetDomainStatus: active"
- echo "objectClass: top"
- echo "objectClass: domainrelatedobject"
- echo "objectClass: inetdomain"
- echo "associatedDomain: ${domain}"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
-
-
- # Create hosted domains
- (
- echo "dn: associateddomain=${hosted_domain},${domain_base_dn}"
- echo "objectclass: top"
- echo "objectclass: domainrelatedobject"
- echo "objectclass: inetdomain"
- echo "inetdomainstatus: active"
- echo "associateddomain: ${hosted_domain}"
- echo "inetdomainbasedn: ${hosted_domain_rootdn}"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
-
- (
- echo "dn: cn=$(echo ${hosted_domain} | sed -e 's/\./_/g'),cn=ldbm database,cn=plugins,cn=config"
- echo "objectClass: top"
- echo "objectClass: extensibleobject"
- echo "objectClass: nsbackendinstance"
- echo "cn: $(echo ${hosted_domain} | sed -e 's/\./_/g')"
- echo "nsslapd-suffix: ${hosted_domain_rootdn}"
- echo "nsslapd-cachesize: -1"
- echo "nsslapd-cachememsize: 10485760"
- echo "nsslapd-readonly: off"
- echo "nsslapd-require-index: off"
- echo "nsslapd-directory: /var/lib/dirsrv/slapd-${DS_INSTANCE_NAME:-$(hostname -s)}/db/$(echo ${hosted_domain} | sed -e 's/\./_/g')"
- echo "nsslapd-dncachememsize: 10485760"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
-
- (
- #On centos7
- #echo "dn: cn=$(echo ${hosted_domain_rootdn} | sed -e 's/=/\\3D/g' -e 's/,/\\2D/g'),cn=mapping tree,cn=config"
- #On centos8
- echo "dn: cn=\"${hosted_domain_rootdn}\",cn=mapping tree,cn=config"
- echo "objectClass: top"
- echo "objectClass: extensibleObject"
- echo "objectClass: nsMappingTree"
- echo "nsslapd-state: backend"
- echo "cn: ${hosted_domain_rootdn}"
- echo "nsslapd-backend: $(echo ${hosted_domain} | sed -e 's/\./_/g')"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
-
- (
- echo "dn: ${hosted_domain_rootdn}"
- echo "aci: (targetattr=\"carLicense || description || displayName || facsimileTelephoneNumber || homePhone || homePostalAddress || initials || jpegPhoto || labeledURI || mobile || pager || photo || postOfficeBox || postalAddress || postalCode || preferredDeliveryMethod || preferredLanguage || registeredAddress || roomNumber || secretary || seeAlso || st || street || telephoneNumber || telexNumber || title || userCertificate || userPassword || userSMIMECertificate || x500UniqueIdentifier\")(version 3.0; acl \"Enable self write for common attributes\"; allow (write) userdn=\"ldap:///self\";)"
- echo "aci: (targetattr =\"*\")(version 3.0;acl \"Directory Administrators Group\";allow (all) (groupdn=\"ldap:///cn=Directory Administrators,${hosted_domain_rootdn}\" or roledn=\"ldap:///cn=kolab-admin,${hosted_domain_rootdn}\");)"
- echo "aci: (targetattr=\"*\")(version 3.0; acl \"Configuration Administrators Group\"; allow (all) groupdn=\"ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot\";)"
- echo "aci: (targetattr=\"*\")(version 3.0; acl \"Configuration Administrator\"; allow (all) userdn=\"ldap:///uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot\";)"
- echo "aci: (targetattr = \"*\")(version 3.0; acl \"SIE Group\"; allow (all) groupdn = \"ldap:///cn=slapd-$(hostname -s),cn=389 Directory Server,cn=Server Group,cn=$(hostname -f),ou=${domain},o=NetscapeRoot\";)"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Search Access\";allow (read,compare,search)(userdn = \"ldap:///${hosted_domain_rootdn}??sub?(objectclass=*)\");)"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Service Search Access\";allow (read,compare,search)(userdn = \"ldap:///uid=kolab-service,ou=Special Users,${rootdn}\");)"
- echo "objectClass: top"
- echo "objectClass: domain"
- echo "dc: $(echo ${hosted_domain} | cut -d'.' -f 1)"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
-
- (
- for role in "2fa-user" "activesync-user" "imap-user"; do
- echo "dn: cn=${role},${hosted_domain_rootdn}"
- echo "cn: ${role}"
- echo "description: ${role} role"
- echo "objectclass: top"
- echo "objectclass: ldapsubentry"
- echo "objectclass: nsmanagedroledefinition"
- echo "objectclass: nsroledefinition"
- echo "objectclass: nssimpleroledefinition"
- echo ""
- done
-
- echo "dn: ou=Groups,${hosted_domain_rootdn}"
- echo "ou: Groups"
- echo "objectClass: top"
- echo "objectClass: organizationalunit"
- echo ""
-
- echo "dn: ou=People,${hosted_domain_rootdn}"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Hosted Kolab Services\";allow (all)(userdn = \"ldap:///uid=hosted-kolab-service,ou=Special Users,${rootdn}\");)"
- echo "ou: People"
- echo "objectClass: top"
- echo "objectClass: organizationalunit"
- echo ""
-
- echo "dn: ou=Special Users,${hosted_domain_rootdn}"
- echo "ou: Special Users"
- echo "objectClass: top"
- echo "objectClass: organizationalunit"
- echo ""
-
- echo "dn: ou=Resources,${hosted_domain_rootdn}"
- echo "ou: Resources"
- echo "objectClass: top"
- echo "objectClass: organizationalunit"
- echo ""
-
- echo "dn: ou=Shared Folders,${hosted_domain_rootdn}"
- echo "ou: Shared Folders"
- echo "objectClass: top"
- echo "objectClass: organizationalunit"
- echo ""
-
- echo "dn: uid=cyrus-admin,ou=Special Users,${hosted_domain_rootdn}"
- echo "sn: Administrator"
- echo "uid: cyrus-admin"
- echo "objectClass: top"
- echo "objectClass: person"
- echo "objectClass: inetorgperson"
- echo "objectClass: organizationalperson"
- echo "givenName: Cyrus"
- echo "cn: Cyrus Administrator"
- echo ""
-
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
-
-
- # Remove cn kolab cn config
- (
- echo "associateddomain=${domain},cn=kolab,cn=config"
- echo "cn=kolab,cn=config"
- ) | ldapdelete -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-
- # Remove hosted service access from mgmt domain
- (
- echo "dn: associateddomain=${domain},ou=Domains,${rootdn}"
- echo "changetype: modify"
- echo "replace: aci"
- echo "aci: (targetattr = \"*\")(version 3.0;acl \"Deny Rest\";deny (all)(userdn != \"ldap:///uid=kolab-service,ou=Special Users,${rootdn} || ldap:///${rootdn}??sub?(objectclass=*)\");)"
- echo "aci: (targetattr = \"*\")(version 3.0;acl \"Deny Hosted Kolab\";deny (all)(userdn = \"ldap:///uid=hosted-kolab-service,ou=Special Users,${rootdn}\");)"
- echo ""
- ) | ldapmodify -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
-
-
- # Add alias attribute index
- #
- export index_attr=alias
-
- (
- echo "dn: cn=${index_attr},cn=index,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
- echo "objectclass: top"
- echo "objectclass: nsindex"
- echo "cn: ${index_attr}"
- echo "nsSystemIndex: false"
- echo "nsindextype: pres"
- echo "nsindextype: eq"
- echo "nsindextype: sub"
-
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-
- (
- echo "dn: cn=${hosted_domain_db} ${index_attr} index,cn=index,cn=tasks,cn=config"
- echo "objectclass: top"
- echo "objectclass: extensibleObject"
- echo "cn: ${hosted_domain_db} ${index_attr} index"
- echo "nsinstance: ${hosted_domain_db}"
- echo "nsIndexAttribute: ${index_attr}:pres"
- echo "nsIndexAttribute: ${index_attr}:eq"
- echo "nsIndexAttribute: ${index_attr}:sub"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
- ldap_complete=0
-
- while [ ${ldap_complete} -ne 1 ]; do
- result=$(
- ldapsearch \
- -x \
- -h "${ldap_host}" \
- -D "${ldap_binddn}" \
- -w "${ldap_bindpw}" \
- -c \
- -LLL \
- -b "cn=${hosted_domain_db} ${index_attr} index,cn=index,cn=tasks,cn=config" \
- '(!(nstaskexitcode=0))' \
- -s base 2>/dev/null
- )
- if [ -z "$result" ]; then
- ldap_complete=1
- echo ""
- else
- echo -n "."
- sleep 1
- fi
- done
-
- ./50-add-vlv-searches.sh
- ./51-add-vlv-indexes.sh
- ./52-run-vlv-index-tasks.sh
-fi
-
diff --git a/docker/kolab/utils/50-add-vlv-searches.sh b/docker/kolab/utils/50-add-vlv-searches.sh
deleted file mode 100755
--- a/docker/kolab/utils/50-add-vlv-searches.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/bash
-
- . ./settings.sh
-
-(
- echo "dn: cn=PVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
- echo "objectClass: top"
- echo "objectClass: vlvSearch"
- echo "cn: PVS"
- echo "vlvBase: ${hosted_domain_rootdn}"
- echo "vlvScope: 2"
- echo "vlvFilter: (objectclass=inetorgperson)"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
- echo ""
-) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-(
- echo "dn: cn=RVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
- echo "objectClass: top"
- echo "objectClass: vlvSearch"
- echo "cn: RVS"
- echo "vlvBase: ${hosted_domain_rootdn}"
- echo "vlvScope: 2"
- echo "vlvFilter: (|(&(objectclass=kolabsharedfolder)(kolabfoldertype=event)(mail=*))(objectclass=groupofuniquenames)(objectclass=groupofurls))"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
- echo ""
-) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-(
- echo "dn: cn=GVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
- echo "objectClass: top"
- echo "objectClass: vlvSearch"
- echo "cn: GVS"
- echo "vlvBase: ${hosted_domain_rootdn}"
- echo "vlvScope: 2"
- echo "vlvFilter: (|(objectclass=groupofuniquenames)(objectclass=groupofurls))"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
- echo ""
-) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-if [ "${domain_base_dn}" != "cn=kolab,cn=config" ]; then
- (
- echo "dn: cn=DVS,cn=${domain_db},cn=ldbm database,cn=plugins,cn=config"
- echo "objectClass: top"
- echo "objectClass: vlvSearch"
- echo "cn: DVS"
- echo "vlvBase: ${domain_base_dn}"
- echo "vlvScope: 2"
- echo "vlvFilter: (objectclass=domainrelatedobject)"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-fi
diff --git a/docker/kolab/utils/51-add-vlv-indexes.sh b/docker/kolab/utils/51-add-vlv-indexes.sh
deleted file mode 100755
--- a/docker/kolab/utils/51-add-vlv-indexes.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/bash
-
- . ./settings.sh
-
-(
- echo "dn: cn=PVI,cn=PVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
- echo "objectClass: top"
- echo "objectClass: vlvIndex"
- echo "cn: PVI"
- echo "vlvSort: displayname sn givenname cn"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
- echo ""
-) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-(
- echo "dn: cn=RVI,cn=RVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
- echo "objectClass: top"
- echo "objectClass: vlvIndex"
- echo "cn: RVI"
- echo "vlvSort: cn"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
- echo ""
-) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-(
- echo "dn: cn=GVI,cn=GVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
- echo "objectClass: top"
- echo "objectClass: vlvIndex"
- echo "cn: GVI"
- echo "vlvSort: cn"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
- echo ""
-) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-if [ "${domain_base_dn}" != "cn=kolab,cn=config" ]; then
- (
- echo "dn: cn=DVI,cn=DVS,cn=${domain_db},cn=ldbm database,cn=plugins,cn=config"
- echo "objectClass: top"
- echo "objectClass: vlvIndex"
- echo "cn: DVI"
- echo "vlvSort: associatedDomain"
- echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
- echo ""
- ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-fi
diff --git a/docker/kolab/utils/52-run-vlv-index-tasks.sh b/docker/kolab/utils/52-run-vlv-index-tasks.sh
deleted file mode 100755
--- a/docker/kolab/utils/52-run-vlv-index-tasks.sh
+++ /dev/null
@@ -1,143 +0,0 @@
-#!/bin/bash
-
- . ./settings.sh
-
-(
- echo "dn: cn=PVI,cn=index,cn=tasks,cn=config"
- echo "objectclass: top"
- echo "objectclass: extensibleObject"
- echo "cn: PVI"
- echo "nsinstance: ${hosted_domain_db}"
- echo "nsIndexVLVAttribute: PVI"
- echo ""
-) | ldapmodify -a -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-ldap_complete=0
-
-while [ ${ldap_complete} -ne 1 ]; do
- result=$(
- ldapsearch \
- -x \
- -h ${ldap_host} \
- -D "${ldap_binddn}" \
- -w "${ldap_bindpw}" \
- -c \
- -LLL \
- -b "cn=PVI,cn=index,cn=tasks,cn=config" \
- '(!(nstaskexitcode=0))' \
- -s base 2>/dev/null
- )
- if [ -z "$result" ]; then
- ldap_complete=1
- echo ""
- else
- echo -n "."
- sleep 1
- fi
-done
-
-(
- echo "dn: cn=RVI,cn=index,cn=tasks,cn=config"
- echo "objectclass: top"
- echo "objectclass: extensibleObject"
- echo "cn: RVI"
- echo "nsinstance: ${hosted_domain_db}"
- echo "nsIndexVLVAttribute: RVI"
- echo ""
-) | ldapmodify -a -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-ldap_complete=0
-
-while [ ${ldap_complete} -ne 1 ]; do
- result=$(
- ldapsearch \
- -x \
- -h ${ldap_host} \
- -D "${ldap_binddn}" \
- -w "${ldap_bindpw}" \
- -c \
- -LLL \
- -b "cn=RVI,cn=index,cn=tasks,cn=config" \
- '(!(nstaskexitcode=0))' \
- -s base 2>/dev/null
- )
- if [ -z "$result" ]; then
- ldap_complete=1
- echo ""
- else
- echo -n "."
- sleep 1
- fi
-done
-
-
-
-(
- echo "dn: cn=GVI,cn=index,cn=tasks,cn=config"
- echo "objectclass: top"
- echo "objectclass: extensibleObject"
- echo "cn: GVI"
- echo "nsinstance: ${hosted_domain_db}"
- echo "nsIndexVLVAttribute: GVI"
- echo ""
-) | ldapmodify -a -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
-ldap_complete=0
-
-while [ ${ldap_complete} -ne 1 ]; do
- result=$(
- ldapsearch \
- -x \
- -h ${ldap_host} \
- -D "${ldap_binddn}" \
- -w "${ldap_bindpw}" \
- -c \
- -LLL \
- -b "cn=GVI,cn=index,cn=tasks,cn=config" \
- '(!(nstaskexitcode=0))' \
- -s base 2>/dev/null
- )
- if [ -z "$result" ]; then
- ldap_complete=1
- echo ""
- else
- echo -n "."
- sleep 1
- fi
-done
-
-if [ "${domain_base_dn}" != "cn=kolab,cn=config" ]; then
- (
- echo "dn: cn=DVI,cn=index,cn=tasks,cn=config"
- echo "objectclass: top"
- echo "objectclass: extensibleObject"
- echo "cn: DVI"
- echo "nsinstance: ${domain_db}"
- echo "nsIndexVLVAttribute: DVI"
- echo ""
- ) | ldapmodify -a -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
-
- ldap_complete=0
-
- while [ ${ldap_complete} -ne 1 ]; do
- result=$(
- ldapsearch \
- -x \
- -h ${ldap_host} \
- -D "${ldap_binddn}" \
- -w "${ldap_bindpw}" \
- -c \
- -LLL \
- -b "cn=DVI,cn=index,cn=tasks,cn=config" \
- '(!(nstaskexitcode=0))' \
- -s base 2>/dev/null
- )
- if [ -z "$result" ]; then
- ldap_complete=1
- echo ""
- else
- echo -n "."
- sleep 1
- fi
- done
-fi
diff --git a/docker/kolab/utils/cyrusadmin.sh b/docker/kolab/utils/cyrusadmin.sh
new file mode 100755
--- /dev/null
+++ b/docker/kolab/utils/cyrusadmin.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+# We use port 12143 because it has plain auth enabled
+echo "$@" | cyradm --auth PLAIN -u cyrus-admin -w Welcome2KolabSystems --port 12143 localhost
diff --git a/docker/kolab/utils/settings.sh b/docker/kolab/utils/settings.sh
--- a/docker/kolab/utils/settings.sh
+++ b/docker/kolab/utils/settings.sh
@@ -11,7 +11,6 @@
export cyrus_admin_pw=${IMAP_ADMIN_PASSWORD}
export kolab_service_pw=${LDAP_SERVICE_BIND_PW}
-export hosted_kolab_service_pw=${LDAP_HOSTED_BIND_PW}
export hosted_domain=${HOSTED_DOMAIN:-"hosted.com"}
export hosted_domain_db=${HOSTED_DOMAIN_DB:-"hosted_com"}
diff --git a/docker/kolab/Dockerfile b/docker/ldap/Dockerfile
copy from docker/kolab/Dockerfile
copy to docker/ldap/Dockerfile
--- a/docker/kolab/Dockerfile
+++ b/docker/ldap/Dockerfile
@@ -24,48 +24,17 @@
rpm -Uvh https://mirror.apheleia-it.ch/repos/Kolab:/16/kolab-16-for-el8stream.rpm
RUN sed -i -e '/^ssl/d' /etc/yum.repos.d/kolab*.repo && \
dnf config-manager --enable kolab-16-testing &&\
- dnf -y --setopt tsflags= install kolab patch &&\
+ dnf -y --setopt tsflags= install kolab-schema 389-ds-base &&\
dnf clean all
+COPY init.sh /init.sh
COPY kolab-init.service /etc/systemd/system/kolab-init.service
COPY kolab-setenv.service /etc/systemd/system/kolab-setenv.service
-COPY utils /root/utils
-
-RUN rm -rf /etc/systemd/system/multi-user.target.wants/{avahi-daemon,sshd}.* && \
- ln -s /etc/systemd/system/kolab-init.service \
- /etc/systemd/system/multi-user.target.wants/kolab-init.service && \
- ln -s /etc/systemd/system/kolab-setenv.service \
- /etc/systemd/system/multi-user.target.wants/kolab-setenv.service
+RUN systemctl disable avahi-daemon sshd; systemctl enable kolab-setenv kolab-init
RUN sed -i -r -e 's/^SELINUX=.*$/SELINUX=permissive/g' /etc/selinux/config 2>/dev/null || :
-COPY /rootfs /
-
-COPY kolab-init.sh /usr/local/sbin/
-RUN chmod 750 /usr/local/sbin/kolab-init.sh
-
-COPY kolab.conf /etc/kolab/kolab.conf
-COPY cyrus.conf /etc/cyrus.conf
-COPY imapd.conf /etc/imapd.conf
-COPY imapd.annotations.conf /etc/imapd.annotations.conf
-COPY guam.conf /etc/guam/sys.config
-
-ARG DB_KOLAB_DATABASE
-ARG DB_KOLAB_USERNAME
-ARG DB_KOLAB_PASSWORD
-RUN sed -i -r \
- -e "s|DB_KOLAB_DATABASE|$DB_KOLAB_DATABASE|g" \
- -e "s|DB_KOLAB_USERNAME|$DB_KOLAB_USERNAME|g" \
- -e "s|DB_KOLAB_PASSWORD|$DB_KOLAB_PASSWORD|g" \
- /etc/kolab/kolab.conf
-
-RUN mkdir -p /imapdata/{spool,lib} && \
- rm -rf /var/spool/imap && ln -s /imapdata/spool /var/spool/imap && \
- mv /var/lib/imap /var/lib/imap-bak && ln -s /imapdata/lib /var/lib/imap && \
- chmod -R 777 /imapdata && \
- chown cyrus:mail /var/spool/imap /var/lib/imap
-
RUN mkdir -p /ldapdata/{config,ssca,run} /var/run/dirsrv && \
ln -s /ldapdata/config /etc/dirsrv/slapd-kolab && \
ln -s /ldapdata/ssca /etc/dirsrv/ssca && \
@@ -73,11 +42,10 @@
chmod -R 777 /ldapdata /etc/dirsrv
VOLUME [ "/sys/fs/cgroup" ]
-VOLUME [ "/imapdata" ]
VOLUME [ "/ldapdata" ]
WORKDIR /root/
CMD ["/lib/systemd/systemd"]
-EXPOSE 10143/tcp 10465/tcp 10587/tcp 11143/tcp 11993/tcp
+EXPOSE 389/tcp
diff --git a/docker/ldap/init.sh b/docker/ldap/init.sh
new file mode 100755
--- /dev/null
+++ b/docker/ldap/init.sh
@@ -0,0 +1,809 @@
+#!/bin/bash
+
+# Disable password checking
+cp -av /bin/true /usr/sbin/ds_systemd_ask_password_acl
+
+# Make sure all the relvant folders exist in /ldapdata
+mkdir -p /ldapdata/{config,ssca,run}
+chmod -R 777 /ldapdata
+
+mkdir -p /var/log/dirsrv/slapd-kolab/
+chmod 777 /var/log/dirsrv/slapd-kolab/
+
+mkdir -p /run/dirsrv
+chmod 777 /run/dirsrv
+
+mkdir -p /run/lock/dirsrv/slapd-kolab/
+chown dirsrv:dirsrv /run/lock/dirsrv/slapd-kolab/
+chmod 777 /run/lock/dirsrv/slapd-kolab/
+
+mkdir -p /var/lib/dirsrv/slapd-kolab
+chown dirsrv:dirsrv /var/lib/dirsrv/slapd-kolab
+
+
+if [ -f "/etc/dirsrv/slapd-kolab/dse.ldif" ]; then
+ echo "LDAP directory exists, nothing to do"
+
+ # mkdir -p /var/log/dirsrv/slapd-kolab/
+ # chmod 777 /var/log/dirsrv/slapd-kolab/
+ # systemctl start dirsrv@kolab
+ # mkdir /run/dirsrv
+ # chmod 777 /run/dirsrv
+ # mkdir -p /run/lock/dirsrv/slapd-kolab/
+ # chown dirsrv:dirsrv /run/lock/dirsrv/slapd-kolab/
+ # chmod 777 /run/lock/dirsrv/slapd-kolab/
+ # mkdir -p /var/lib/dirsrv/slapd-kolab
+ # chown dirsrv:dirsrv /var/lib/dirsrv/slapd-kolab
+
+ systemctl start dirsrv@kolab
+ exit 0
+fi
+
+# Used for the graphical console only.
+GRAPHICAL_ADMIN_PASSWORD="-22F_EjHut5JCcd"
+DS_INSTANCE_NAME="kolab"
+DOMAIN="mgmt.com"
+FQDN="ldap.mgmt.com"
+
+cat << EOF > /tmp/dscreateinput
+[general]
+FullMachineName = ldap.mgmt.com
+SuiteSpotUserID = dirsrv
+SuiteSpotGroup = dirsrv
+AdminDomain = mgmt.com
+ConfigDirectoryLdapURL = ldap://ldap.mgmt.com:389/o=NetscapeRoot
+ConfigDirectoryAdminID = admin
+ConfigDirectoryAdminPwd = $GRAPHICAL_ADMIN_PASSWORD
+full_machine_name = ldap.mgmt.com
+
+[slapd]
+SlapdConfigForMC = Yes
+UseExistingMC = 0
+ServerPort = 389
+ServerIdentifier = kolab
+Suffix = $LDAP_ADMIN_ROOT_DN
+RootDN = cn=Directory Manager
+RootDNPwd = $LDAP_ADMIN_BIND_PW
+ds_bename = mgmt_com
+AddSampleEntries = No
+instance_name = $DS_INSTANCE_NAME
+root_password = $LDAP_ADMIN_BIND_PW
+create_suffix_entry = True
+
+[backend-userroot]
+suffix = $LDAP_ADMIN_ROOT_DN
+create_suffix_entry = True
+
+[admin]
+Port = 9830
+ServerAdminID = admin
+ServerAdminPwd = $GRAPHICAL_ADMIN_PASSWORD
+
+EOF
+dscreate -v from-file /tmp/dscreateinput
+
+cp /usr/share/dirsrv/data/template.ldif /tmp/templatedata.ldif
+sed -i "s/%ds_suffix%/$LDAP_BASE_DN/" /tmp/templatedata.ldif
+sed -i "s/%rootdn%/cn=Directory Manager/" /tmp/templatedata.ldif
+ldapadd -x -H 'ldap://127.0.0.1:389/' -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/templatedata.ldif
+
+
+#FIXME in kolab container setup kolab.conf entries
+
+
+cp /usr/share/doc/kolab-schema/kolab3.ldif /etc/dirsrv/slapd-kolab/schema/99kolab3.ldif
+
+systemctl restart dirsrv.target
+systemctl restart dirsrv@kolab
+systemctl enable dirsrv.target
+systemctl enable dirsrv@kolab
+
+
+
+# I'm not sure why we need to create those manually
+cat << EOF > /tmp/ldapadd
+
+# Directory Administrators, mgmt.com
+dn: cn=Directory Administrators,dc=mgmt,dc=com
+objectClass: top
+objectClass: groupofuniquenames
+cn: Directory Administrators
+uniqueMember: cn=Directory Manager
+
+# Groups, mgmt.com
+dn: ou=Groups,dc=mgmt,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Groups
+
+# People, mgmt.com
+dn: ou=People,dc=mgmt,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: People
+
+# Special Users, mgmt.com
+dn: ou=Special Users,dc=mgmt,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: Special Users
+description: Special Administrative Accounts
+
+# Accounting Managers, Groups, mgmt.com
+dn: cn=Accounting Managers,ou=Groups,dc=mgmt,dc=com
+objectClass: top
+objectClass: groupOfUniqueNames
+cn: Accounting Managers
+ou: groups
+description: People who can manage accounting entries
+uniqueMember: cn=Directory Manager
+
+# HR Managers, Groups, mgmt.com
+dn: cn=HR Managers,ou=Groups,dc=mgmt,dc=com
+objectClass: top
+objectClass: groupOfUniqueNames
+cn: HR Managers
+ou: groups
+description: People who can manage HR entries
+uniqueMember: cn=Directory Manager
+
+# QA Managers, Groups, mgmt.com
+dn: cn=QA Managers,ou=Groups,dc=mgmt,dc=com
+objectClass: top
+objectClass: groupOfUniqueNames
+cn: QA Managers
+ou: groups
+description: People who can manage QA entries
+uniqueMember: cn=Directory Manager
+
+# PD Managers, Groups, mgmt.com
+dn: cn=PD Managers,ou=Groups,dc=mgmt,dc=com
+objectClass: top
+objectClass: groupOfUniqueNames
+cn: PD Managers
+ou: groups
+description: People who can manage engineer entries
+uniqueMember: cn=Directory Manager
+
+EOF
+ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+
+## =========== Start of pykolab changes
+# Work that pykolab used to do
+#
+cat << EOF > /tmp/ldapadd
+# cyrus-admin, Special Users, mgmt.com
+dn: uid=cyrus-admin,ou=Special Users,dc=mgmt,dc=com
+objectClass: top
+objectClass: person
+objectClass: inetorgperson
+objectClass: organizationalperson
+uid: cyrus-admin
+givenName: Cyrus
+sn: Administrator
+cn: Cyrus Administrator
+userPassword: ${IMAP_ADMIN_PW}
+
+# kolab-service, Special Users, mgmt.com
+dn: uid=kolab-service,ou=Special Users,dc=mgmt,dc=com
+objectClass: top
+objectClass: person
+objectClass: inetorgperson
+objectClass: organizationalperson
+uid: kolab-service
+givenName: Kolab
+sn: Service
+cn: Kolab Service
+userPassword: ${LDAP_SERVICE_BIND_PW}
+
+# Resources, mgmt.com
+dn: ou=Resources,dc=mgmt,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Resources
+
+# Shared Folders, mgmt.com
+dn: ou=Shared Folders,dc=mgmt,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Shared Folders
+
+EOF
+ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+
+cat << EOF > /tmp/ldapadd
+dn: cn=kolab,cn=config
+cn: kolab
+aci: (targetattr = "*") (version 3.0;acl "Kolab Services";allow (read,compare,search)(userdn = "ldap:///uid=kolab-service,ou=Special Users,$LDAP_ADMIN_ROOT_DN");)
+objectClass: top
+objectClass: extensibleobject
+EOF
+ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+echo "Adding domain $DOMAIN to list of domains for this deployment"
+cat << EOF > /tmp/ldapadd
+dn: associateddomain=$DOMAIN,cn=kolab,cn=config
+objectClass: top
+objectClass: domainrelatedobject
+associatedDomain: $DOMAIN, $FQDN, localhost.localdomain, localhost
+aci: (targetattr = "*") (version 3.0;acl "Read Access for $DOMAIN Users";allow (read,compare,search)(userdn = "ldap:///$LDAP_ADMIN_ROOT_DN??sub?(objectclass=*)");)
+EOF
+ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+##TODO
+ ## Add inetdomainbasedn in case the configured root dn is not the same as the
+ ## standard root dn for the domain name configured
+ #if not _input['rootdn'] == utils.standard_root_dn(_input['domain']):
+ # attrs['objectclass'].append('inetdomain')
+ # attrs['inetdomainbasedn'] = _input['rootdn']
+
+echo "Disabling anonymous binds"
+cat << EOF > /tmp/ldapadd
+dn: cn=config
+changetype: modify
+replace: nsslapd-allow-anonymous-access
+nsslapd-allow-anonymous-access: off
+EOF
+ldapmodify -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+
+## TODO: Ensure the uid attribute is unique
+## TODO^2: Consider renaming the general "attribute uniqueness to "uid attribute uniqueness"
+echo "Enabling attribute uniqueness plugin"
+cat << EOF > /tmp/ldapadd
+dn: cn=attribute uniqueness,cn=plugins,cn=config
+changetype: modify
+replace: nsslapd-pluginEnabled
+nsslapd-pluginEnabled: on
+EOF
+ldapmodify -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+echo "Enabling referential integrity plugin"
+cat << EOF > /tmp/ldapadd
+dn: cn=referential integrity postoperation,cn=plugins,cn=config
+changetype: modify
+replace: nsslapd-pluginEnabled
+nsslapd-pluginEnabled: on
+EOF
+ldapmodify -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+echo "Enabling referential integrity plugin"
+cat << EOF > /tmp/ldapadd
+dn: cn=referential integrity postoperation,cn=plugins,cn=config
+changetype: modify
+replace: nsslapd-pluginEnabled
+nsslapd-pluginEnabled: on
+EOF
+ldapmodify -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+echo "Enabling and configuring account policy plugin"
+cat << EOF > /tmp/ldapadd
+dn: cn=Account Policy Plugin,cn=plugins,cn=config
+changetype: modify
+replace: nsslapd-pluginEnabled
+nsslapd-pluginEnabled: on
+
+dn: cn=config,cn=Account Policy Plugin,cn=plugins,cn=config
+changetype: modify
+replace: alwaysrecordlogin
+alwaysrecordlogin: yes
+-
+add: stateattrname
+stateattrname: lastLoginTime
+-
+add: altstateattrname
+altstateattrname: createTimestamp
+EOF
+ldapmodify -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+echo "Adding the kolab-admin role"
+cat << EOF > /tmp/ldapadd
+dn: cn=kolab-admin,$LDAP_ADMIN_ROOT_DN
+description: Kolab Administrator
+objectClass: top
+objectClass: ldapsubentry
+objectClass: nsroledefinition
+objectClass: nssimpleroledefinition
+objectClass: nsmanagedroledefinition
+cn = kolab-admin
+EOF
+ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+echo "Setting access control to $LDAP_ADMIN_ROOT_DN"
+cat << EOF > /tmp/ldapadd
+dn: $LDAP_ADMIN_ROOT_DN
+changetype: modify
+replace: aci
+aci: (targetattr = "carLicense || description || displayName || facsimileTelephoneNumber || homePhone || homePostalAddress || initials || jpegPhoto || l || labeledURI || mobile || o || pager || photo || postOfficeBox || postalAddress || postalCode || preferredDeliveryMethod || preferredLanguage || registeredAddress || roomNumber || secretary || seeAlso || st || street || telephoneNumber || telexNumber || title || userCertificate || userPassword || userSMIMECertificate || x500UniqueIdentifier || kolabDelegate || kolabInvitationPolicy || kolabAllowSMTPSender")(version 3.0; acl "Enable self write for common attributes"; allow (read,compare,search,write)(userdn = "ldap:///self");)
+aci: (targetattr = "*")(version 3.0;acl "Directory Administrators Group";allow (all)(groupdn = "ldap:///cn=Directory Administrators,$LDAP_ADMIN_ROOT_DN" or roledn = "ldap:///cn=kolab-admin,$LDAP_ADMIN_ROOT_DN");)
+aci: (targetattr="*")(version 3.0; acl "Configuration Administrators Group"; allow (all) groupdn="ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot";)
+aci: (targetattr="*")(version 3.0; acl "Configuration Administrator"; allow (all) userdn="ldap:///uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot";)
+aci: (targetattr = "*")(version 3.0; acl "SIE Group"; allow (all) groupdn = "ldap:///cn=slapd-$DS_INSTANCE_NAME,cn=389 Directory Server,cn=Server Group,cn=$FQDN,ou=$DOMAIN,o=NetscapeRoot";)
+aci: (targetattr != "userPassword") (version 3.0;acl "Search Access";allow (read,compare,search)(userdn = "ldap:///all");)')
+EOF
+ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+## =========== End of pykolab code
+
+# Create hosted kolab service
+cat << EOF > /tmp/ldapadd
+dn: uid=hosted-kolab-service,ou=Special Users,${LDAP_ADMIN_ROOT_DN}
+objectclass: top
+objectclass: inetorgperson
+objectclass: person
+uid: hosted-kolab-service
+cn: Hosted Kolab Service Account
+sn: Service Account
+givenname: Hosted Kolab
+userpassword: ${LDAP_HOSTED_BIND_PW}
+
+EOF
+ldapadd -x -h 127.0.0.1 -D "cn=Directory Manager" -w "$LDAP_ADMIN_BIND_PW" -f /tmp/ldapadd
+
+export rootdn=$LDAP_ADMIN_ROOT_DN
+export domain=$DOMAIN
+export domain_db="mgmt_com"
+export ldap_host=127.0.0.1
+export ldap_binddn=${LDAP_ADMIN_BIND_DN}
+export ldap_bindpw=${LDAP_ADMIN_BIND_PW}
+
+export cyrus_admin=${IMAP_ADMIN_LOGIN}
+export cyrus_admin_pw=${IMAP_ADMIN_PASSWORD}
+
+export kolab_service_pw=${LDAP_SERVICE_BIND_PW}
+export hosted_kolab_service_pw=${LDAP_HOSTED_BIND_PW}
+
+export hosted_domain=${HOSTED_DOMAIN:-"hosted.com"}
+export hosted_domain_db=${HOSTED_DOMAIN_DB:-"hosted_com"}
+export hosted_domain_rootdn=${LDAP_HOSTED_ROOT_DN:-"dc=hosted,dc=com"}
+
+export domain_base_dn=${LDAP_DOMAIN_BASE_DN:-"ou=Domains,dc=mgmt,dc=com"}
+
+
+# Create ou domain
+(
+ echo "dn: ou=Domains,${rootdn}"
+ echo "ou: Domains"
+ echo "objectClass: top"
+ echo "objectClass: organizationalunit"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
+
+# Create management domain
+(
+ echo "dn: associateddomain=${domain},${domain_base_dn}"
+ echo "aci: (targetattr = \"*\")(version 3.0;acl \"Deny Rest\";deny (all)(userdn != \"ldap:///uid=kolab-service,ou=Special Users,${rootdn} || ldap:///${rootdn}??sub?(objectclass=*)\");)"
+ echo "aci: (targetattr = \"*\")(version 3.0;acl \"Deny Hosted Kolab\";deny (all)(userdn = \"ldap:///uid=hosted-kolab-service,ou=Special Users,${rootdn}\");)"
+ echo "inetDomainStatus: active"
+ echo "objectClass: top"
+ echo "objectClass: domainrelatedobject"
+ echo "objectClass: inetdomain"
+ echo "associatedDomain: ${domain}"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
+
+
+# Create hosted domains
+(
+ echo "dn: associateddomain=${hosted_domain},${domain_base_dn}"
+ echo "objectclass: top"
+ echo "objectclass: domainrelatedobject"
+ echo "objectclass: inetdomain"
+ echo "inetdomainstatus: active"
+ echo "associateddomain: ${hosted_domain}"
+ echo "inetdomainbasedn: ${hosted_domain_rootdn}"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
+
+(
+ echo "dn: cn=$(echo ${hosted_domain} | sed -e 's/\./_/g'),cn=ldbm database,cn=plugins,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: extensibleobject"
+ echo "objectClass: nsbackendinstance"
+ echo "cn: $(echo ${hosted_domain} | sed -e 's/\./_/g')"
+ echo "nsslapd-suffix: ${hosted_domain_rootdn}"
+ echo "nsslapd-cachesize: -1"
+ echo "nsslapd-cachememsize: 10485760"
+ echo "nsslapd-readonly: off"
+ echo "nsslapd-require-index: off"
+ echo "nsslapd-directory: /var/lib/dirsrv/slapd-${DS_INSTANCE_NAME}/db/$(echo ${hosted_domain} | sed -e 's/\./_/g')"
+ echo "nsslapd-dncachememsize: 10485760"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
+
+(
+ #On centos7
+ #echo "dn: cn=$(echo ${hosted_domain_rootdn} | sed -e 's/=/\\3D/g' -e 's/,/\\2D/g'),cn=mapping tree,cn=config"
+ #On centos8
+ echo "dn: cn=\"${hosted_domain_rootdn}\",cn=mapping tree,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: extensibleObject"
+ echo "objectClass: nsMappingTree"
+ echo "nsslapd-state: backend"
+ echo "cn: ${hosted_domain_rootdn}"
+ echo "nsslapd-backend: $(echo ${hosted_domain} | sed -e 's/\./_/g')"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
+
+(
+ echo "dn: ${hosted_domain_rootdn}"
+ echo "aci: (targetattr=\"carLicense || description || displayName || facsimileTelephoneNumber || homePhone || homePostalAddress || initials || jpegPhoto || labeledURI || mobile || pager || photo || postOfficeBox || postalAddress || postalCode || preferredDeliveryMethod || preferredLanguage || registeredAddress || roomNumber || secretary || seeAlso || st || street || telephoneNumber || telexNumber || title || userCertificate || userPassword || userSMIMECertificate || x500UniqueIdentifier\")(version 3.0; acl \"Enable self write for common attributes\"; allow (write) userdn=\"ldap:///self\";)"
+ echo "aci: (targetattr =\"*\")(version 3.0;acl \"Directory Administrators Group\";allow (all) (groupdn=\"ldap:///cn=Directory Administrators,${hosted_domain_rootdn}\" or roledn=\"ldap:///cn=kolab-admin,${hosted_domain_rootdn}\");)"
+ echo "aci: (targetattr=\"*\")(version 3.0; acl \"Configuration Administrators Group\"; allow (all) groupdn=\"ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot\";)"
+ echo "aci: (targetattr=\"*\")(version 3.0; acl \"Configuration Administrator\"; allow (all) userdn=\"ldap:///uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot\";)"
+ echo "aci: (targetattr = \"*\")(version 3.0; acl \"SIE Group\"; allow (all) groupdn = \"ldap:///cn=slapd-${DS_INSTANCE_NAME},cn=389 Directory Server,cn=Server Group,cn=$FQDN,ou=${domain},o=NetscapeRoot\";)"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Search Access\";allow (read,compare,search)(userdn = \"ldap:///${hosted_domain_rootdn}??sub?(objectclass=*)\");)"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Service Search Access\";allow (read,compare,search)(userdn = \"ldap:///uid=kolab-service,ou=Special Users,${rootdn}\");)"
+ echo "objectClass: top"
+ echo "objectClass: domain"
+ echo "dc: $(echo ${hosted_domain} | cut -d'.' -f 1)"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
+
+(
+ for role in "2fa-user" "activesync-user" "imap-user"; do
+ echo "dn: cn=${role},${hosted_domain_rootdn}"
+ echo "cn: ${role}"
+ echo "description: ${role} role"
+ echo "objectclass: top"
+ echo "objectclass: ldapsubentry"
+ echo "objectclass: nsmanagedroledefinition"
+ echo "objectclass: nsroledefinition"
+ echo "objectclass: nssimpleroledefinition"
+ echo ""
+ done
+
+ echo "dn: ou=Groups,${hosted_domain_rootdn}"
+ echo "ou: Groups"
+ echo "objectClass: top"
+ echo "objectClass: organizationalunit"
+ echo ""
+
+ echo "dn: ou=People,${hosted_domain_rootdn}"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Hosted Kolab Services\";allow (all)(userdn = \"ldap:///uid=hosted-kolab-service,ou=Special Users,${rootdn}\");)"
+ echo "ou: People"
+ echo "objectClass: top"
+ echo "objectClass: organizationalunit"
+ echo ""
+
+ echo "dn: ou=Special Users,${hosted_domain_rootdn}"
+ echo "ou: Special Users"
+ echo "objectClass: top"
+ echo "objectClass: organizationalunit"
+ echo ""
+
+ echo "dn: ou=Resources,${hosted_domain_rootdn}"
+ echo "ou: Resources"
+ echo "objectClass: top"
+ echo "objectClass: organizationalunit"
+ echo ""
+
+ echo "dn: ou=Shared Folders,${hosted_domain_rootdn}"
+ echo "ou: Shared Folders"
+ echo "objectClass: top"
+ echo "objectClass: organizationalunit"
+ echo ""
+
+ echo "dn: uid=cyrus-admin,ou=Special Users,${hosted_domain_rootdn}"
+ echo "sn: Administrator"
+ echo "uid: cyrus-admin"
+ echo "objectClass: top"
+ echo "objectClass: person"
+ echo "objectClass: inetorgperson"
+ echo "objectClass: organizationalperson"
+ echo "givenName: Cyrus"
+ echo "cn: Cyrus Administrator"
+ echo ""
+
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
+
+
+# Remove cn kolab cn config
+(
+ echo "associateddomain=${domain},cn=kolab,cn=config"
+ echo "cn=kolab,cn=config"
+) | ldapdelete -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+
+# Remove hosted service access from mgmt domain
+(
+ echo "dn: associateddomain=${domain},ou=Domains,${rootdn}"
+ echo "changetype: modify"
+ echo "replace: aci"
+ echo "aci: (targetattr = \"*\")(version 3.0;acl \"Deny Rest\";deny (all)(userdn != \"ldap:///uid=kolab-service,ou=Special Users,${rootdn} || ldap:///${rootdn}??sub?(objectclass=*)\");)"
+ echo "aci: (targetattr = \"*\")(version 3.0;acl \"Deny Hosted Kolab\";deny (all)(userdn = \"ldap:///uid=hosted-kolab-service,ou=Special Users,${rootdn}\");)"
+ echo ""
+) | ldapmodify -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}"
+
+
+# Add alias attribute index
+#
+export index_attr=alias
+
+(
+ echo "dn: cn=${index_attr},cn=index,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
+ echo "objectclass: top"
+ echo "objectclass: nsindex"
+ echo "cn: ${index_attr}"
+ echo "nsSystemIndex: false"
+ echo "nsindextype: pres"
+ echo "nsindextype: eq"
+ echo "nsindextype: sub"
+
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+
+(
+ echo "dn: cn=${hosted_domain_db} ${index_attr} index,cn=index,cn=tasks,cn=config"
+ echo "objectclass: top"
+ echo "objectclass: extensibleObject"
+ echo "cn: ${hosted_domain_db} ${index_attr} index"
+ echo "nsinstance: ${hosted_domain_db}"
+ echo "nsIndexAttribute: ${index_attr}:pres"
+ echo "nsIndexAttribute: ${index_attr}:eq"
+ echo "nsIndexAttribute: ${index_attr}:sub"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+ldap_complete=0
+
+while [ ${ldap_complete} -ne 1 ]; do
+ result=$(
+ ldapsearch \
+ -x \
+ -h "${ldap_host}" \
+ -D "${ldap_binddn}" \
+ -w "${ldap_bindpw}" \
+ -c \
+ -LLL \
+ -b "cn=${hosted_domain_db} ${index_attr} index,cn=index,cn=tasks,cn=config" \
+ '(!(nstaskexitcode=0))' \
+ -s base 2>/dev/null
+ )
+ if [ -z "$result" ]; then
+ ldap_complete=1
+ echo ""
+ else
+ echo -n "."
+ sleep 1
+ fi
+done
+
+
+# Add VLV searches
+(
+ echo "dn: cn=PVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: vlvSearch"
+ echo "cn: PVS"
+ echo "vlvBase: ${hosted_domain_rootdn}"
+ echo "vlvScope: 2"
+ echo "vlvFilter: (objectclass=inetorgperson)"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+(
+ echo "dn: cn=RVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: vlvSearch"
+ echo "cn: RVS"
+ echo "vlvBase: ${hosted_domain_rootdn}"
+ echo "vlvScope: 2"
+ echo "vlvFilter: (|(&(objectclass=kolabsharedfolder)(kolabfoldertype=event)(mail=*))(objectclass=groupofuniquenames)(objectclass=groupofurls))"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+(
+ echo "dn: cn=GVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: vlvSearch"
+ echo "cn: GVS"
+ echo "vlvBase: ${hosted_domain_rootdn}"
+ echo "vlvScope: 2"
+ echo "vlvFilter: (|(objectclass=groupofuniquenames)(objectclass=groupofurls))"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+if [ "${domain_base_dn}" != "cn=kolab,cn=config" ]; then
+ (
+ echo "dn: cn=DVS,cn=${domain_db},cn=ldbm database,cn=plugins,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: vlvSearch"
+ echo "cn: DVS"
+ echo "vlvBase: ${domain_base_dn}"
+ echo "vlvScope: 2"
+ echo "vlvFilter: (objectclass=domainrelatedobject)"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
+ echo ""
+ ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+fi
+
+
+
+# Add vlv indexes
+(
+ echo "dn: cn=PVI,cn=PVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: vlvIndex"
+ echo "cn: PVI"
+ echo "vlvSort: displayname sn givenname cn"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+(
+ echo "dn: cn=RVI,cn=RVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: vlvIndex"
+ echo "cn: RVI"
+ echo "vlvSort: cn"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+(
+ echo "dn: cn=GVI,cn=GVS,cn=${hosted_domain_db},cn=ldbm database,cn=plugins,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: vlvIndex"
+ echo "cn: GVI"
+ echo "vlvSort: cn"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
+ echo ""
+) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+if [ "${domain_base_dn}" != "cn=kolab,cn=config" ]; then
+ (
+ echo "dn: cn=DVI,cn=DVS,cn=${domain_db},cn=ldbm database,cn=plugins,cn=config"
+ echo "objectClass: top"
+ echo "objectClass: vlvIndex"
+ echo "cn: DVI"
+ echo "vlvSort: associatedDomain"
+ echo "aci: (targetattr = \"*\") (version 3.0;acl \"Read Access\";allow (read,compare,search)(userdn = \"ldap:///anyone\");)"
+ echo ""
+ ) | ldapadd -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+fi
+
+# Run vlv index tasks
+(
+ echo "dn: cn=PVI,cn=index,cn=tasks,cn=config"
+ echo "objectclass: top"
+ echo "objectclass: extensibleObject"
+ echo "cn: PVI"
+ echo "nsinstance: ${hosted_domain_db}"
+ echo "nsIndexVLVAttribute: PVI"
+ echo ""
+) | ldapmodify -a -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+ldap_complete=0
+
+while [ ${ldap_complete} -ne 1 ]; do
+ result=$(
+ ldapsearch \
+ -x \
+ -h ${ldap_host} \
+ -D "${ldap_binddn}" \
+ -w "${ldap_bindpw}" \
+ -c \
+ -LLL \
+ -b "cn=PVI,cn=index,cn=tasks,cn=config" \
+ '(!(nstaskexitcode=0))' \
+ -s base 2>/dev/null
+ )
+ if [ -z "$result" ]; then
+ ldap_complete=1
+ echo ""
+ else
+ echo -n "."
+ sleep 1
+ fi
+done
+
+(
+ echo "dn: cn=RVI,cn=index,cn=tasks,cn=config"
+ echo "objectclass: top"
+ echo "objectclass: extensibleObject"
+ echo "cn: RVI"
+ echo "nsinstance: ${hosted_domain_db}"
+ echo "nsIndexVLVAttribute: RVI"
+ echo ""
+) | ldapmodify -a -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+ldap_complete=0
+
+while [ ${ldap_complete} -ne 1 ]; do
+ result=$(
+ ldapsearch \
+ -x \
+ -h ${ldap_host} \
+ -D "${ldap_binddn}" \
+ -w "${ldap_bindpw}" \
+ -c \
+ -LLL \
+ -b "cn=RVI,cn=index,cn=tasks,cn=config" \
+ '(!(nstaskexitcode=0))' \
+ -s base 2>/dev/null
+ )
+ if [ -z "$result" ]; then
+ ldap_complete=1
+ echo ""
+ else
+ echo -n "."
+ sleep 1
+ fi
+done
+
+
+
+(
+ echo "dn: cn=GVI,cn=index,cn=tasks,cn=config"
+ echo "objectclass: top"
+ echo "objectclass: extensibleObject"
+ echo "cn: GVI"
+ echo "nsinstance: ${hosted_domain_db}"
+ echo "nsIndexVLVAttribute: GVI"
+ echo ""
+) | ldapmodify -a -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+ldap_complete=0
+
+while [ ${ldap_complete} -ne 1 ]; do
+ result=$(
+ ldapsearch \
+ -x \
+ -h ${ldap_host} \
+ -D "${ldap_binddn}" \
+ -w "${ldap_bindpw}" \
+ -c \
+ -LLL \
+ -b "cn=GVI,cn=index,cn=tasks,cn=config" \
+ '(!(nstaskexitcode=0))' \
+ -s base 2>/dev/null
+ )
+ if [ -z "$result" ]; then
+ ldap_complete=1
+ echo ""
+ else
+ echo -n "."
+ sleep 1
+ fi
+done
+
+if [ "${domain_base_dn}" != "cn=kolab,cn=config" ]; then
+ (
+ echo "dn: cn=DVI,cn=index,cn=tasks,cn=config"
+ echo "objectclass: top"
+ echo "objectclass: extensibleObject"
+ echo "cn: DVI"
+ echo "nsinstance: ${domain_db}"
+ echo "nsIndexVLVAttribute: DVI"
+ echo ""
+ ) | ldapmodify -a -x -h ${ldap_host} -D "${ldap_binddn}" -w "${ldap_bindpw}" -c
+
+ ldap_complete=0
+
+ while [ ${ldap_complete} -ne 1 ]; do
+ result=$(
+ ldapsearch \
+ -x \
+ -h ${ldap_host} \
+ -D "${ldap_binddn}" \
+ -w "${ldap_bindpw}" \
+ -c \
+ -LLL \
+ -b "cn=DVI,cn=index,cn=tasks,cn=config" \
+ '(!(nstaskexitcode=0))' \
+ -s base 2>/dev/null
+ )
+ if [ -z "$result" ]; then
+ ldap_complete=1
+ echo ""
+ else
+ echo -n "."
+ sleep 1
+ fi
+ done
+fi
diff --git a/docker/ldap/kolab-init.service b/docker/ldap/kolab-init.service
new file mode 100644
--- /dev/null
+++ b/docker/ldap/kolab-init.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Kolab Setup Service
+Requires=kolab-setenv.service
+After=kolab-setenv.service ldapdata.mount
+
+[Service]
+Type=oneshot
+EnvironmentFile=/etc/openshift-environment
+ExecStart=/init.sh
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/docker/ldap/kolab-setenv.service b/docker/ldap/kolab-setenv.service
new file mode 100644
--- /dev/null
+++ b/docker/ldap/kolab-setenv.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=Kolab Set Environment
+
+[Service]
+Type=oneshot
+ExecStart=/bin/bash -c "cat /proc/1/environ | tr '\0' '\n' > /etc/openshift-environment"
+
+[Install]
+WantedBy=multi-user.target
diff --git a/docker/meet/Dockerfile b/docker/meet/Dockerfile
--- a/docker/meet/Dockerfile
+++ b/docker/meet/Dockerfile
@@ -7,6 +7,7 @@
npm nodejs python3 python3-pip meson ninja-build make gcc g++ git && \
dnf clean all
+ARG GIT_REF=master
COPY build.sh /build.sh
RUN /build.sh
COPY init.sh /init.sh
diff --git a/docker/meet/build.sh b/docker/meet/build.sh
--- a/docker/meet/build.sh
+++ b/docker/meet/build.sh
@@ -2,7 +2,10 @@
set -e
mkdir /src/
cd /src/
-git clone https://git.kolab.org/source/kolab.git kolab
+git clone --branch $GIT_REF https://git.kolab.org/source/kolab.git kolab
+pushd kolab
+git reset --hard $GIT_REF
+popd
cp -R kolab/meet/server /src/meetsrc
rm -Rf /src/meetsrc/node_modules
cd /src/meetsrc
diff --git a/docker/swoole/Dockerfile b/docker/swoole/Dockerfile
--- a/docker/swoole/Dockerfile
+++ b/docker/swoole/Dockerfile
@@ -1,8 +1,8 @@
-FROM fedora:35
+FROM fedora:37
MAINTAINER Jeroen van Meeuwen <vanmeeuwen@apheleia-it.ch>
-ARG SWOOLE_VERSION=v4.11.1
+ARG SWOOLE_VERSION=v5.0.2
ENV HOME=/opt/app-root/src
LABEL io.k8s.description="Platform for serving PHP applications under Swoole" \
@@ -29,7 +29,7 @@
php-mysqlnd \
re2c \
wget && \
- git clone https://github.com/openswoole/swoole-src.git/ /swoole-src.git/ && \
+ git clone https://github.com/swoole/swoole-src.git/ /swoole-src.git/ && \
cd /swoole-src.git/ && \
git checkout -f ${SWOOLE_VERSION} && \
git clean -d -f -x && \
@@ -38,7 +38,6 @@
./configure \
--enable-sockets \
--disable-mysqlnd \
- --enable-http2 \
--enable-openssl && \
make -j4 && \
make install && \
@@ -52,8 +51,8 @@
php-devel \
re2c && \
dnf clean all && \
- echo "extension=openswoole.so" >> /etc/php.d/openswoole.ini && \
- php -m 2>&1 | grep -q openswoole
+ echo "extension=swoole.so" >> /etc/php.d/swoole.ini && \
+ php -m 2>&1 | grep -q swoole
RUN id default || (groupadd -g 1001 default && useradd -d /opt/app-root/ -u 1001 -g 1001 default)
diff --git a/docker/tests/Dockerfile b/docker/tests/Dockerfile
--- a/docker/tests/Dockerfile
+++ b/docker/tests/Dockerfile
@@ -1,4 +1,4 @@
-FROM apheleia/swoole:4.8.x
+FROM apheleia/swoole:latest
MAINTAINER Jeroen van Meeuwen <vanmeeuwen@apheleia-it.ch>
diff --git a/docker/webapp/Dockerfile b/docker/webapp/Dockerfile
--- a/docker/webapp/Dockerfile
+++ b/docker/webapp/Dockerfile
@@ -1,15 +1,17 @@
-FROM apheleia/swoole:4.8.x
+FROM apheleia/swoole:latest
MAINTAINER Jeroen van Meeuwen <vanmeeuwen@apheleia-it.ch>
USER root
-RUN dnf -y install findutils gnupg2 git rsync
+RUN dnf -y install findutils gnupg2 git rsync procps-ng
EXPOSE 8000
+ARG GIT_REF=master
COPY build.sh /build.sh
RUN /build.sh
COPY init.sh /init.sh
+COPY update.sh /update.sh
CMD [ "/init.sh" ]
diff --git a/docker/webapp/build.sh b/docker/webapp/build.sh
--- a/docker/webapp/build.sh
+++ b/docker/webapp/build.sh
@@ -5,7 +5,11 @@
mkdir /src
cd /src
-git clone https://git.kolab.org/source/kolab.git kolab
+git clone --branch $GIT_REF https://git.kolab.org/source/kolab.git kolab
+pushd kolab
+git reset --hard $GIT_REF
+popd
+
cp -a kolab/src /src/kolabsrc
cd /src/kolabsrc
diff --git a/docker/webapp/update.sh b/docker/webapp/update.sh
new file mode 100755
--- /dev/null
+++ b/docker/webapp/update.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+set -e
+set -x
+
+rsync -av \
+ --exclude=vendor \
+ --exclude=composer.lock \
+ --exclude=node_modules \
+ --exclude=package-lock.json \
+ --exclude=public \
+ --exclude=storage \
+ --exclude=resources/build \
+ --exclude=bootstrap \
+ --exclude=.gitignore \
+ /src/kolabsrc.orig/ /src/kolabsrc/ | tee /tmp/rsync.output
+
+cd /src/kolabsrc/
+# Only run npm if something relevant was updated
+if grep -e "package.json" -e "resources" /tmp/rsync.output; then
+ npm run dev
+fi
+./artisan octane:reload
diff --git a/src/app/Backends/DAV.php b/src/app/Backends/DAV.php
--- a/src/app/Backends/DAV.php
+++ b/src/app/Backends/DAV.php
@@ -184,7 +184,8 @@
$folder = DAV\Folder::fromDomElement($element);
// Note: Addressbooks don't have 'type' specified
- if (($component == self::TYPE_VCARD && in_array('addressbook', $folder->types))
+ if (
+ ($component == self::TYPE_VCARD && in_array('addressbook', $folder->types))
|| in_array($component, $folder->components)
) {
$folders[] = $folder;
@@ -303,7 +304,7 @@
// could fetch "an index" but also any of object's data.
$body = '<?xml version="1.0" encoding="utf-8"?>'
- .' <c:' . $queries[$component] . ' xmlns:d="DAV:" xmlns:c="' . self::NAMESPACES[$component] . '">'
+ . ' <c:' . $queries[$component] . ' xmlns:d="DAV:" xmlns:c="' . self::NAMESPACES[$component] . '">'
. '<d:prop>'
. '<d:getetag />'
. '</d:prop>'
@@ -359,7 +360,7 @@
];
$body = '<?xml version="1.0" encoding="utf-8"?>'
- .' <c:' . $queries[$component] . ' xmlns:d="DAV:" xmlns:c="' . self::NAMESPACES[$component] . '">'
+ . ' <c:' . $queries[$component] . ' xmlns:d="DAV:" xmlns:c="' . self::NAMESPACES[$component] . '">'
. '<d:prop>'
. '<d:getetag />'
. '<c:' . $types[$component] . ' />'
diff --git a/src/app/Backends/DAV/CommonObject.php b/src/app/Backends/DAV/CommonObject.php
--- a/src/app/Backends/DAV/CommonObject.php
+++ b/src/app/Backends/DAV/CommonObject.php
@@ -33,7 +33,7 @@
// Extract UID from the URL
$href_parts = explode('/', $object->href);
- $object->uid = preg_replace('/\.[a-z]+$/', '', $href_parts[count($href_parts)-1]);
+ $object->uid = preg_replace('/\.[a-z]+$/', '', $href_parts[count($href_parts) - 1]);
}
if ($etag = $element->getElementsByTagName('getetag')->item(0)) {
diff --git a/src/app/Backends/DAV/Vevent.php b/src/app/Backends/DAV/Vevent.php
--- a/src/app/Backends/DAV/Vevent.php
+++ b/src/app/Backends/DAV/Vevent.php
@@ -108,87 +108,85 @@
}
switch ($prop->name) {
- case 'DTSTART':
- case 'DTEND':
- case 'DUE':
- case 'CREATED':
- case 'LAST-MODIFIED':
- case 'DTSTAMP':
- $key = Str::camel(strtolower($prop->name));
- // These are of type Sabre\VObject\Property\ICalendar\DateTime
- $this->{$key} = $prop;
- break;
-
- case 'RRULE':
- $params = !empty($this->recurrence) ? $this->recurrence : [];
-
- foreach ($prop->getParts() as $k => $v) {
- $params[Str::camel(strtolower($k))] = is_array($v) ? implode(',', $v) : $v;
- }
+ case 'DTSTART':
+ case 'DTEND':
+ case 'DUE':
+ case 'CREATED':
+ case 'LAST-MODIFIED':
+ case 'DTSTAMP':
+ $key = Str::camel(strtolower($prop->name));
+ // These are of type Sabre\VObject\Property\ICalendar\DateTime
+ $this->{$key} = $prop;
+ break;
- if (!empty($params['until'])) {
- $params['until'] = new \DateTime($params['until']);
- }
+ case 'RRULE':
+ $params = !empty($this->recurrence) ? $this->recurrence : [];
- if (empty($params['interval'])) {
- $params['interval'] = 1;
- }
+ foreach ($prop->getParts() as $k => $v) {
+ $params[Str::camel(strtolower($k))] = is_array($v) ? implode(',', $v) : $v;
+ }
- $this->recurrence = array_filter($params);
- break;
+ if (!empty($params['until'])) {
+ $params['until'] = new \DateTime($params['until']);
+ }
- case 'EXDATE':
- case 'RDATE':
- $key = strtolower($prop->name);
- $dates = []; // TODO
+ if (empty($params['interval'])) {
+ $params['interval'] = 1;
+ }
- if (!empty($this->recurrence[$key])) {
- $this->recurrence[$key] = array_merge($this->recurrence[$key], $dates);
- }
- else {
- $this->recurrence[$key] = $dates;
- }
+ $this->recurrence = array_filter($params);
+ break;
- break;
-
- case 'ATTENDEE':
- case 'ORGANIZER':
- $attendee = [
- 'rsvp' => false,
- 'email' => preg_replace('!^mailto:!i', '', (string) $prop),
- ];
-
- $attendeeProps = ['CN', 'PARTSTAT', 'ROLE', 'CUTYPE', 'RSVP', 'DELEGATED-FROM', 'DELEGATED-TO',
- 'SCHEDULE-STATUS', 'SCHEDULE-AGENT', 'SENT-BY'];
-
- foreach ($prop->parameters() as $name => $value) {
- $key = Str::camel(strtolower($name));
- switch ($name) {
- case 'RSVP':
- $params[$key] = strtolower($value) == 'true';
- break;
- case 'CN':
- $params[$key] = str_replace('\,', ',', strval($value));
- break;
- default:
- if (in_array($name, $attendeeProps)) {
- $params[$key] = strval($value);
- }
- break;
+ case 'EXDATE':
+ case 'RDATE':
+ $key = strtolower($prop->name);
+ $dates = []; // TODO
+
+ if (!empty($this->recurrence[$key])) {
+ $this->recurrence[$key] = array_merge($this->recurrence[$key], $dates);
+ } else {
+ $this->recurrence[$key] = $dates;
}
- }
- if ($prop->name == 'ORGANIZER') {
- $attendee['role'] = 'ORGANIZER';
- $attendee['partstat'] = 'ACCEPTED';
+ break;
- $this->organizer = $attendee;
- }
- else if (empty($this->organizer) || $attendee['email'] != $this->organizer['email']) {
- $this->attendees[] = $attendee;
- }
+ case 'ATTENDEE':
+ case 'ORGANIZER':
+ $attendee = [
+ 'rsvp' => false,
+ 'email' => preg_replace('!^mailto:!i', '', (string) $prop),
+ ];
+
+ $attendeeProps = ['CN', 'PARTSTAT', 'ROLE', 'CUTYPE', 'RSVP', 'DELEGATED-FROM', 'DELEGATED-TO',
+ 'SCHEDULE-STATUS', 'SCHEDULE-AGENT', 'SENT-BY'];
+
+ foreach ($prop->parameters() as $name => $value) {
+ $key = Str::camel(strtolower($name));
+ switch ($name) {
+ case 'RSVP':
+ $params[$key] = strtolower($value) == 'true';
+ break;
+ case 'CN':
+ $params[$key] = str_replace('\,', ',', strval($value));
+ break;
+ default:
+ if (in_array($name, $attendeeProps)) {
+ $params[$key] = strval($value);
+ }
+ break;
+ }
+ }
+
+ if ($prop->name == 'ORGANIZER') {
+ $attendee['role'] = 'ORGANIZER';
+ $attendee['partstat'] = 'ACCEPTED';
+
+ $this->organizer = $attendee;
+ } elseif (empty($this->organizer) || $attendee['email'] != $this->organizer['email']) {
+ $this->attendees[] = $attendee;
+ }
- break;
+ break;
}
}
@@ -217,47 +215,46 @@
$value = strval($prop);
switch ($prop->name) {
- case 'TRIGGER':
- foreach ($prop->parameters as $param) {
- if ($param->name == 'VALUE' && $param->getValue() == 'DATE-TIME') {
- $trigger = '@' . $prop->getDateTime()->format('U');
- $alarm['trigger'] = $prop->getDateTime();
+ case 'TRIGGER':
+ foreach ($prop->parameters as $param) {
+ if ($param->name == 'VALUE' && $param->getValue() == 'DATE-TIME') {
+ $trigger = '@' . $prop->getDateTime()->format('U');
+ $alarm['trigger'] = $prop->getDateTime();
+ } elseif ($param->name == 'RELATED') {
+ $alarm['related'] = $param->getValue();
+ }
}
- else if ($param->name == 'RELATED') {
- $alarm['related'] = $param->getValue();
+ /*
+ if (!$trigger && ($values = libcalendaring::parse_alarm_value($value))) {
+ $trigger = $values[2];
}
- }
-/*
- if (!$trigger && ($values = libcalendaring::parse_alarm_value($value))) {
- $trigger = $values[2];
- }
-*/
- if (empty($alarm['trigger'])) {
- $alarm['trigger'] = rtrim(preg_replace('/([A-Z])0[WDHMS]/', '\\1', $value), 'T');
- // if all 0-values have been stripped, assume 'at time'
- if ($alarm['trigger'] == 'P') {
- $alarm['trigger'] = 'PT0S';
+ */
+ if (empty($alarm['trigger'])) {
+ $alarm['trigger'] = rtrim(preg_replace('/([A-Z])0[WDHMS]/', '\\1', $value), 'T');
+ // if all 0-values have been stripped, assume 'at time'
+ if ($alarm['trigger'] == 'P') {
+ $alarm['trigger'] = 'PT0S';
+ }
}
- }
- break;
+ break;
- case 'ACTION':
- $action = $alarm['action'] = strtoupper($value);
- break;
+ case 'ACTION':
+ $action = $alarm['action'] = strtoupper($value);
+ break;
- case 'SUMMARY':
- case 'DESCRIPTION':
- case 'DURATION':
- $alarm[strtolower($prop->name)] = $value;
- break;
+ case 'SUMMARY':
+ case 'DESCRIPTION':
+ case 'DURATION':
+ $alarm[strtolower($prop->name)] = $value;
+ break;
- case 'REPEAT':
- $alarm['repeat'] = (int) $value;
- break;
+ case 'REPEAT':
+ $alarm['repeat'] = (int) $value;
+ break;
- case 'ATTENDEE':
- $alarm['attendees'][] = preg_replace('!^mailto:!i', '', $value);
- break;
+ case 'ATTENDEE':
+ $alarm['attendees'][] = preg_replace('!^mailto:!i', '', $value);
+ break;
}
}
diff --git a/src/app/Backends/IMAP.php b/src/app/Backends/IMAP.php
--- a/src/app/Backends/IMAP.php
+++ b/src/app/Backends/IMAP.php
@@ -2,7 +2,6 @@
namespace App\Backends;
-use App\Domain;
use App\Group;
use App\Resource;
use App\SharedFolder;
@@ -76,6 +75,7 @@
// Mailbox already exists
if (self::folderExists($imap, $mailbox)) {
$imap->closeConnection();
+ self::createDefaultFolders($user);
return true;
}
@@ -107,11 +107,36 @@
$imap->setQuota($mailbox, ['storage' => $quota]);
}
+ self::createDefaultFolders($user);
+
$imap->closeConnection();
return true;
}
+ /**
+ * Create default folders for the user.
+ *
+ * @param \App\User $user User
+ */
+ public static function createDefaultFolders(User $user): void
+ {
+ if ($defaultFolders = \config('imap.default_folders')) {
+ $config = self::getConfig();
+ // Log in as user to set private annotations and subscription state
+ $imap = self::initIMAP($config, $user->email);
+ foreach ($defaultFolders as $name => $folderconfig) {
+ try {
+ $mailbox = self::toUTF7($name);
+ self::createFolder($imap, $mailbox, true, $folderconfig['metadata']);
+ } catch (\Exception $e) {
+ \Log::warning("Failed to create the default folder" . $e->getMessage());
+ }
+ }
+ $imap->closeConnection();
+ }
+ }
+
/**
* Delete a mailbox.
*
@@ -184,28 +209,13 @@
$settings = $resource->getSettings(['invitation_policy', 'folder']);
$mailbox = self::toUTF7($settings['folder']);
- // Mailbox already exists
- if (self::folderExists($imap, $mailbox)) {
- $imap->closeConnection();
- return true;
- }
-
- // Create the shared folder
- if (!$imap->createFolder($mailbox)) {
- \Log::error("Failed to create mailbox {$mailbox}");
- $imap->closeConnection();
- return false;
- }
-
- // Set folder type
- $imap->setMetadata($mailbox, ['/shared/vendor/kolab/folder-type' => 'event']);
-
- // Set ACL
+ $acl = null;
if (!empty($settings['invitation_policy'])) {
if (preg_match('/^manual:(\S+@\S+)$/', $settings['invitation_policy'], $m)) {
- self::aclUpdate($imap, $mailbox, ["{$m[1]}, full"]);
+ $acl = ["{$m[1]}, full"];
}
}
+ self::createFolder($imap, $mailbox, false, ['/shared/vendor/kolab/folder-type' => 'event'], $acl);
$imap->closeConnection();
@@ -299,24 +309,7 @@
$acl = !empty($settings['acl']) ? json_decode($settings['acl'], true) : null;
$mailbox = self::toUTF7($settings['folder']);
- // Mailbox already exists
- if (self::folderExists($imap, $mailbox)) {
- $imap->closeConnection();
- return true;
- }
-
- // Create the mailbox
- if (!$imap->createFolder($mailbox)) {
- \Log::error("Failed to create mailbox {$mailbox}");
- $imap->closeConnection();
- return false;
- }
-
- // Set folder type
- $imap->setMetadata($mailbox, ['/shared/vendor/kolab/folder-type' => $folder->type]);
-
- // Set ACL
- self::aclUpdate($imap, $mailbox, $acl);
+ self::createFolder($imap, $mailbox, false, ['/shared/vendor/kolab/folder-type' => $folder->type], $acl);
$imap->closeConnection();
@@ -459,6 +452,30 @@
return false;
}
+ /**
+ * Check if an account is set up
+ *
+ * @param string $username User login (email address)
+ *
+ * @return bool True if an account exists and is set up, False otherwise
+ */
+ public static function verifyDefaultFolders(string $username): bool
+ {
+ $config = self::getConfig();
+ $imap = self::initIMAP($config, $username);
+
+ foreach (\config('imap.default_folders') as $mb => $_metadata) {
+ $mailbox = self::toUTF7($mb);
+ if (!self::folderExists($imap, $mailbox)) {
+ $imap->closeConnection();
+ return false;
+ }
+ }
+
+ $imap->closeConnection();
+ return true;
+ }
+
/**
* Check if we can connect to the imap server
*
@@ -515,6 +532,43 @@
$imap->closeConnection();
}
+ /**
+ * Create a folder and set some default properties
+ *
+ * @param \rcube_imap_generic $imap The imap instance
+ * @param string $mailbox Mailbox name
+ * @param bool $subscribe Subscribe to the folder
+ * @param array $metadata Metadata to set on the folder
+ * @param array $acl Acl to set on the folder
+ *
+ * @return bool True when having a folder created, False if it already existed.
+ * @throws \Exception
+ */
+ private static function createFolder($imap, string $mailbox, $subscribe = false, $metadata = null, $acl = null)
+ {
+ if (self::folderExists($imap, $mailbox)) {
+ return false;
+ }
+
+ if (!$imap->createFolder($mailbox)) {
+ throw new \Exception("Failed to create mailbox {$mailbox}");
+ }
+
+ if ($acl) {
+ self::aclUpdate($imap, $mailbox, $acl, true);
+ }
+
+ if ($subscribe) {
+ $imap->subscribe($mailbox);
+ }
+
+ foreach ($metadata as $key => $value) {
+ $imap->setMetadata($mailbox, [$key => $value]);
+ }
+
+ return true;
+ }
+
/**
* Convert Kolab ACL into IMAP user->rights array
*/
diff --git a/src/app/Console/Commands/User/ResyncCommand.php b/src/app/Console/Commands/User/ResyncCommand.php
--- a/src/app/Console/Commands/User/ResyncCommand.php
+++ b/src/app/Console/Commands/User/ResyncCommand.php
@@ -45,8 +45,9 @@
if ($deleted_only) {
$users->whereNotNull('deleted_at')
- ->where(function($query) {
- $query->where('status', '&', User::STATUS_IMAP_READY)->orWhere('status', '&', User::STATUS_LDAP_READY);
+ ->where(function ($query) {
+ $query->where('status', '&', User::STATUS_IMAP_READY)
+ ->orWhere('status', '&', User::STATUS_LDAP_READY);
});
}
@@ -88,7 +89,7 @@
}
\App\Jobs\User\CreateJob::dispatch($user->id);
- } else if (!empty($req_user)) {
+ } elseif (!empty($req_user)) {
if ($dry_run) {
$this->info("{$user->email}: will be pushed");
continue;
diff --git a/src/app/Domain.php b/src/app/Domain.php
--- a/src/app/Domain.php
+++ b/src/app/Domain.php
@@ -44,6 +44,15 @@
// domain has been created in LDAP
public const STATUS_LDAP_READY = 1 << 6;
+ /** @var int The allowed states for this object used in StatusPropertyTrait */
+ private int $allowed_states = self::STATUS_NEW |
+ self::STATUS_ACTIVE |
+ self::STATUS_SUSPENDED |
+ self::STATUS_DELETED |
+ self::STATUS_CONFIRMED |
+ self::STATUS_VERIFIED |
+ self::STATUS_LDAP_READY;
+
// open for public registration
public const TYPE_PUBLIC = 1 << 0;
// zone hosted with us
@@ -170,29 +179,13 @@
*/
public function setStatusAttribute($status)
{
- $new_status = 0;
-
- $allowed_values = [
- self::STATUS_NEW,
- self::STATUS_ACTIVE,
- self::STATUS_SUSPENDED,
- self::STATUS_DELETED,
- self::STATUS_CONFIRMED,
- self::STATUS_VERIFIED,
- self::STATUS_LDAP_READY,
- ];
-
- foreach ($allowed_values as $value) {
- if ($status & $value) {
- $new_status |= $value;
- $status ^= $value;
- }
- }
-
- if ($status > 0) {
+ // Detect invalid flags
+ if ($status & ~$this->allowed_states) {
throw new \Exception("Invalid domain status: {$status}");
}
+ $new_status = $status;
+
if ($this->isPublic()) {
$this->attributes['status'] = $new_status;
return;
diff --git a/src/app/Group.php b/src/app/Group.php
--- a/src/app/Group.php
+++ b/src/app/Group.php
@@ -44,6 +44,13 @@
// group has been created in LDAP
public const STATUS_LDAP_READY = 1 << 4;
+ /** @var int The allowed states for this object used in StatusPropertyTrait */
+ private int $allowed_states = self::STATUS_NEW |
+ self::STATUS_ACTIVE |
+ self::STATUS_SUSPENDED |
+ self::STATUS_DELETED |
+ self::STATUS_LDAP_READY;
+
/** @var array<string, string> The attributes that should be cast */
protected $casts = [
'created_at' => 'datetime:Y-m-d H:i:s',
diff --git a/src/app/Http/Controllers/API/V4/PaymentsController.php b/src/app/Http/Controllers/API/V4/PaymentsController.php
--- a/src/app/Http/Controllers/API/V4/PaymentsController.php
+++ b/src/app/Http/Controllers/API/V4/PaymentsController.php
@@ -376,6 +376,7 @@
$mandate['amount'] = $mandate['minAmount'] = (int) ceil(Payment::MIN_AMOUNT / 100);
$mandate['balance'] = 0;
$mandate['isDisabled'] = !empty($mandate['id']) && $settings['mandate_disabled'];
+ $mandate['isValid'] = !empty($mandate['isValid']);
foreach (['amount', 'balance'] as $key) {
if (($value = $settings["mandate_{$key}"]) !== null) {
@@ -523,16 +524,16 @@
$request['vat_rate_id'] = $rate->id;
switch (\config('app.vat.mode')) {
- case 1:
- // In this mode tax is added on top of the payment. The amount
- // to pay grows, but we keep wallet balance without tax.
- $request['amount'] = $request['amount'] + round($request['amount'] * $rate->rate / 100);
- break;
-
- default:
- // In this mode tax is "swallowed" by the vendor. The payment
- // amount does not change
- break;
+ case 1:
+ // In this mode tax is added on top of the payment. The amount
+ // to pay grows, but we keep wallet balance without tax.
+ $request['amount'] = $request['amount'] + round($request['amount'] * $rate->rate / 100);
+ break;
+
+ default:
+ // In this mode tax is "swallowed" by the vendor. The payment
+ // amount does not change
+ break;
}
}
}
diff --git a/src/app/Http/Controllers/API/V4/UsersController.php b/src/app/Http/Controllers/API/V4/UsersController.php
--- a/src/app/Http/Controllers/API/V4/UsersController.php
+++ b/src/app/Http/Controllers/API/V4/UsersController.php
@@ -203,7 +203,7 @@
'enableUsers' => $isController,
'enableWallets' => $isController,
'enableWalletMandates' => $isController,
- 'enableWalletPayments' => !$plan || $plan->mode != 'mandate',
+ 'enableWalletPayments' => $isController && (!$plan || $plan->mode != 'mandate'),
'enableCompanionapps' => $hasBeta,
];
diff --git a/src/app/Http/Middleware/ContentSecurityPolicy.php b/src/app/Http/Middleware/ContentSecurityPolicy.php
--- a/src/app/Http/Middleware/ContentSecurityPolicy.php
+++ b/src/app/Http/Middleware/ContentSecurityPolicy.php
@@ -21,6 +21,11 @@
'xfo' => 'X-Frame-Options',
];
+ //Exclude horizon routes, per https://github.com/laravel/horizon/issues/576
+ if ($request->is('horizon*')) {
+ $headers = [];
+ }
+
$next = $next($request);
foreach ($headers as $opt => $header) {
diff --git a/src/app/Payment.php b/src/app/Payment.php
--- a/src/app/Payment.php
+++ b/src/app/Payment.php
@@ -106,7 +106,15 @@
*/
public function credit($method): void
{
- // TODO: Possibly we should sanity check that payment is paid, and not negative?
+ if (empty($this->wallet)) {
+ throw new \Exception("Cannot credit a payment not assigned to a wallet");
+ }
+
+ if ($this->credit_amount < 0) {
+ throw new \Exception("Cannot credit a payment with negative amount");
+ }
+
+ // TODO: Possibly we should sanity check that payment is paid?
// TODO: Localization?
$description = $this->type == self::TYPE_RECURRING ? 'Auto-payment' : 'Payment';
$description .= " transaction {$this->id} using {$method}";
diff --git a/src/app/Providers/AuthServiceProvider.php b/src/app/Providers/AuthServiceProvider.php
--- a/src/app/Providers/AuthServiceProvider.php
+++ b/src/app/Providers/AuthServiceProvider.php
@@ -2,10 +2,7 @@
namespace App\Providers;
-use Illuminate\Support\Facades\Auth;
-use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
-use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider
{
@@ -25,17 +22,5 @@
public function boot()
{
$this->registerPolicies();
-
- Passport::tokensCan([
- 'api' => 'Access API',
- 'mfa' => 'Access MFA API',
- ]);
-
- Passport::tokensExpireIn(now()->addMinutes(\config('auth.token_expiry_minutes')));
- Passport::refreshTokensExpireIn(now()->addMinutes(\config('auth.refresh_token_expiry_minutes')));
- Passport::personalAccessTokensExpireIn(now()->addMonths(6));
-
- Passport::useClientModel(\App\Auth\PassportClient::class);
- Passport::tokenModel()::observe(\App\Observers\Passport\TokenObserver::class);
}
}
diff --git a/src/app/Providers/PassportServiceProvider.php b/src/app/Providers/PassportServiceProvider.php
--- a/src/app/Providers/PassportServiceProvider.php
+++ b/src/app/Providers/PassportServiceProvider.php
@@ -10,6 +10,26 @@
class PassportServiceProvider extends \Laravel\Passport\PassportServiceProvider
{
+ /**
+ * Register any authentication / authorization services.
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ Passport::tokensCan([
+ 'api' => 'Access API',
+ 'mfa' => 'Access MFA API',
+ ]);
+
+ Passport::tokensExpireIn(now()->addMinutes(\config('auth.token_expiry_minutes')));
+ Passport::refreshTokensExpireIn(now()->addMinutes(\config('auth.refresh_token_expiry_minutes')));
+ Passport::personalAccessTokensExpireIn(now()->addMonths(6));
+
+ Passport::useClientModel(\App\Auth\PassportClient::class);
+ Passport::tokenModel()::observe(\App\Observers\Passport\TokenObserver::class);
+ }
+
/**
* Make the authorization service instance.
*
diff --git a/src/app/Resource.php b/src/app/Resource.php
--- a/src/app/Resource.php
+++ b/src/app/Resource.php
@@ -45,6 +45,13 @@
// resource has been created in IMAP
public const STATUS_IMAP_READY = 1 << 8;
+ /** @var int The allowed states for this object used in StatusPropertyTrait */
+ private int $allowed_states = self::STATUS_NEW |
+ self::STATUS_ACTIVE |
+ self::STATUS_DELETED |
+ self::STATUS_LDAP_READY |
+ self::STATUS_IMAP_READY;
+
// A template for the email attribute on a resource creation
public const EMAIL_TEMPLATE = 'resource-{id}@{domainName}';
diff --git a/src/app/SharedFolder.php b/src/app/SharedFolder.php
--- a/src/app/SharedFolder.php
+++ b/src/app/SharedFolder.php
@@ -48,6 +48,13 @@
// folder has been created in IMAP
public const STATUS_IMAP_READY = 1 << 8;
+ /** @var int The allowed states for this object used in StatusPropertyTrait */
+ private int $allowed_states = self::STATUS_NEW |
+ self::STATUS_ACTIVE |
+ self::STATUS_DELETED |
+ self::STATUS_LDAP_READY |
+ self::STATUS_IMAP_READY;
+
/** @const array Supported folder type labels */
public const SUPPORTED_TYPES = ['mail', 'event', 'contact', 'task', 'note', 'file'];
diff --git a/src/app/Traits/StatusPropertyTrait.php b/src/app/Traits/StatusPropertyTrait.php
--- a/src/app/Traits/StatusPropertyTrait.php
+++ b/src/app/Traits/StatusPropertyTrait.php
@@ -101,34 +101,10 @@
*/
public function setStatusAttribute($status)
{
- $new_status = 0;
-
- $allowed_states = [
- 'STATUS_NEW',
- 'STATUS_ACTIVE',
- 'STATUS_SUSPENDED',
- 'STATUS_DELETED',
- 'STATUS_LDAP_READY',
- 'STATUS_IMAP_READY',
- ];
-
- foreach ($allowed_states as $const) {
- if (!defined("static::$const")) {
- continue;
- }
-
- $value = constant("static::$const");
-
- if ($status & $value) {
- $new_status |= $value;
- $status ^= $value;
- }
- }
-
- if ($status > 0) {
+ if ($status & ~$this->allowed_states) {
throw new \Exception("Invalid status: {$status}");
}
- $this->attributes['status'] = $new_status;
+ $this->attributes['status'] = $status;
}
}
diff --git a/src/app/User.php b/src/app/User.php
--- a/src/app/User.php
+++ b/src/app/User.php
@@ -61,6 +61,16 @@
// a restricted user
public const STATUS_RESTRICTED = 1 << 7;
+ /** @var int The allowed states for this object used in StatusPropertyTrait */
+ private int $allowed_states = self::STATUS_NEW |
+ self::STATUS_ACTIVE |
+ self::STATUS_SUSPENDED |
+ self::STATUS_DELETED |
+ self::STATUS_LDAP_READY |
+ self::STATUS_IMAP_READY |
+ self::STATUS_DEGRADED |
+ self::STATUS_RESTRICTED;
+
/** @var array<int, string> The attributes that are mass assignable */
protected $fillable = [
'id',
@@ -547,7 +557,7 @@
/**
* Un-restrict this user.
*
- * @param bool $deep Unrestrinct also all users in the account
+ * @param bool $deep Unrestrict also all users in the account
*
* @return void
*/
@@ -632,40 +642,6 @@
$this->setPasswordAttribute($password);
}
- /**
- * User status mutator
- *
- * @throws \Exception
- */
- public function setStatusAttribute($status)
- {
- $new_status = 0;
-
- $allowed_values = [
- self::STATUS_NEW,
- self::STATUS_ACTIVE,
- self::STATUS_SUSPENDED,
- self::STATUS_DELETED,
- self::STATUS_LDAP_READY,
- self::STATUS_IMAP_READY,
- self::STATUS_DEGRADED,
- self::STATUS_RESTRICTED,
- ];
-
- foreach ($allowed_values as $value) {
- if ($status & $value) {
- $new_status |= $value;
- $status ^= $value;
- }
- }
-
- if ($status > 0) {
- throw new \Exception("Invalid user status: {$status}");
- }
-
- $this->attributes['status'] = $new_status;
- }
-
/**
* Validate the user credentials
*
diff --git a/src/config/imap.php b/src/config/imap.php
--- a/src/config/imap.php
+++ b/src/config/imap.php
@@ -9,4 +9,52 @@
'host' => env('IMAP_HOST', '172.18.0.5'),
'imap_port' => env('IMAP_PORT', 12143),
'guam_port' => env('IMAP_GUAM_PORT', 9143),
+ 'default_folders' => [
+ 'Drafts' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'mail.drafts',
+ ],
+ ],
+ 'Sent' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'mail.sentitems',
+ ],
+ ],
+ 'Trash' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'mail.wastebasket',
+ ],
+ ],
+ 'Spam' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'mail.junkemail',
+ ],
+ ],
+
+ 'Calendar' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'event.default'
+ ],
+ ],
+ 'Contacts' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'contact.default',
+ ],
+ ],
+ 'Tasks' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'task.default',
+ ],
+ ],
+ 'Notes' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'note.default',
+ ],
+ ],
+ 'Files' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'file.default',
+ ],
+ ],
+ ]
];
diff --git a/src/database/migrations/2023_02_17_100000_vat_rates_table.php b/src/database/migrations/2023_02_17_100000_vat_rates_table.php
--- a/src/database/migrations/2023_02_17_100000_vat_rates_table.php
+++ b/src/database/migrations/2023_02_17_100000_vat_rates_table.php
@@ -61,7 +61,7 @@
->from('user_settings')
->where('key', 'country')
->where('value', $country);
- });
+ });
})
->update(['vat_rate_id' => $vatRate->id]);
}
diff --git a/src/tests/Browser/Pages/PaymentMollie.php b/src/tests/Browser/Pages/PaymentMollie.php
--- a/src/tests/Browser/Pages/PaymentMollie.php
+++ b/src/tests/Browser/Pages/PaymentMollie.php
@@ -25,7 +25,7 @@
*/
public function assert($browser)
{
- $browser->waitFor('form#body table, form#body iframe');
+ $browser->waitFor('form#body table, form#body iframe', 10);
}
/**
@@ -64,7 +64,7 @@
$browser->type('#expiryDate', '12/' . (date('y') + 1));
})
->withinFrame('#cvv iframe', function ($browser) {
- $browser->type('#verificationCode', '123');
+ $browser->click('#verificationCode')->type('#verificationCode', '123');
})
->click('#submit-button');
}
diff --git a/src/tests/Browser/Pages/PaymentStripe.php b/src/tests/Browser/Pages/PaymentStripe.php
--- a/src/tests/Browser/Pages/PaymentStripe.php
+++ b/src/tests/Browser/Pages/PaymentStripe.php
@@ -40,7 +40,7 @@
'@title' => '.App-Overview .ProductSummary',
'@amount' => '#ProductSummary-totalAmount',
'@description' => '#ProductSummary-Description',
- '@email-input' => '.App-Payment #email',
+ '@email' => '.App-Payment .ReadOnlyFormField-email .ReadOnlyFormField-content',
'@cardnumber-input' => '.App-Payment #cardNumber',
'@cardexpiry-input' => '.App-Payment #cardExpiry',
'@cardcvc-input' => '.App-Payment #cardCvc',
diff --git a/src/tests/Browser/PaymentMollieTest.php b/src/tests/Browser/PaymentMollieTest.php
--- a/src/tests/Browser/PaymentMollieTest.php
+++ b/src/tests/Browser/PaymentMollieTest.php
@@ -108,6 +108,7 @@
->on(new WalletPage())
->assertMissing('@body #mandate-form .alert')
->click('@main #mandate-form button')
+/*
->with(new Dialog('@payment-dialog'), function (Browser $browser) {
$browser->assertSeeIn('@title', 'Set up auto-payment')
->waitFor('#payment-method-selection .link-creditcard svg')
@@ -115,8 +116,10 @@
->assertMissing('#payment-method-selection .link-banktransfer')
->click('#payment-method-selection .link-creditcard');
})
+*/
->with(new Dialog('@payment-dialog'), function (Browser $browser) {
$browser->assertSeeIn('@title', 'Set up auto-payment')
+ ->waitFor('@body #mandate_amount')
->assertSeeIn('@body label[for="mandate_amount"]', 'Fill up by')
->assertValue('@body #mandate_amount', Payment::MIN_AMOUNT / 100)
->assertSeeIn('@body label[for="mandate_balance"]', 'when account balance is below') // phpcs:ignore
@@ -229,13 +232,16 @@
$browser->on(new WalletPage())
->assertMissing('@body #mandate-form .alert')
->click('@main #mandate-form button')
+/*
->with(new Dialog('@payment-dialog'), function (Browser $browser) {
$browser->assertSeeIn('@title', 'Set up auto-payment')
->waitFor('#payment-method-selection .link-creditcard')
->click('#payment-method-selection .link-creditcard');
})
+*/
->with(new Dialog('@payment-dialog'), function (Browser $browser) {
$browser->assertSeeIn('@title', 'Set up auto-payment')
+ ->waitFor('@body #mandate_amount')
->assertSeeIn('@button-cancel', 'Cancel')
->assertSeeIn('@button-action', 'Continue')
// Submit valid data
@@ -259,13 +265,16 @@
// Create a new mandate
->click('@main #mandate-form button')
+/*
->with(new Dialog('@payment-dialog'), function (Browser $browser) {
$browser->assertSeeIn('@title', 'Set up auto-payment')
->waitFor('#payment-method-selection .link-creditcard')
->click('#payment-method-selection .link-creditcard');
})
+*/
->with(new Dialog('@payment-dialog'), function (Browser $browser) {
$browser->assertSeeIn('@title', 'Set up auto-payment')
+ ->waitFor('@body #mandate_amount')
->assertSeeIn('@button-cancel', 'Cancel')
->assertSeeIn('@button-action', 'Continue')
// Submit valid data
diff --git a/src/tests/Browser/PaymentStripeTest.php b/src/tests/Browser/PaymentStripeTest.php
--- a/src/tests/Browser/PaymentStripeTest.php
+++ b/src/tests/Browser/PaymentStripeTest.php
@@ -80,7 +80,7 @@
->on(new PaymentStripe())
->assertSeeIn('@title', $user->tenant->title . ' Payment')
->assertSeeIn('@amount', 'CHF 12.34')
- ->assertValue('@email-input', $user->email)
+ ->assertSeeIn('@email', $user->email)
->submitValidCreditCard();
// Now it should redirect back to wallet page and in background
@@ -115,6 +115,7 @@
->on(new WalletPage())
->assertMissing('@body #mandate-form .alert')
->click('@main #mandate-form button')
+/*
->with(new Dialog('@payment-dialog'), function (Browser $browser) {
$browser->assertSeeIn('@title', 'Set up auto-payment')
->waitFor('#payment-method-selection .link-creditcard')
@@ -122,8 +123,10 @@
->assertMissing('#payment-method-selection .link-banktransfer')
->click('#payment-method-selection .link-creditcard');
})
+*/
->with(new Dialog('@payment-dialog'), function (Browser $browser) {
$browser->assertSeeIn('@title', 'Set up auto-payment')
+ ->waitFor('@body #mandate_amount')
->assertSeeIn('@body label[for="mandate_amount"]', 'Fill up by')
->assertValue('@body #mandate_amount', Payment::MIN_AMOUNT / 100)
->assertSeeIn('@body label[for="mandate_balance"]', 'when account balance is below') // phpcs:ignore
@@ -157,7 +160,7 @@
->on(new PaymentStripe())
->assertMissing('@title')
->assertMissing('@amount')
- ->assertValue('@email-input', $user->email)
+ ->assertSeeIn('@email', $user->email)
->submitValidCreditCard()
->waitForLocation('/wallet', 30) // need more time than default 5 sec.
->visit('/wallet?paymentProvider=stripe')
diff --git a/src/tests/Browser/SignupTest.php b/src/tests/Browser/SignupTest.php
--- a/src/tests/Browser/SignupTest.php
+++ b/src/tests/Browser/SignupTest.php
@@ -12,7 +12,9 @@
use Tests\Browser\Components\Menu;
use Tests\Browser\Components\Toast;
use Tests\Browser\Pages\Dashboard;
+use Tests\Browser\Pages\Home;
use Tests\Browser\Pages\Signup;
+use Tests\Browser\Pages\Wallet;
use Tests\TestCaseDusk;
use Illuminate\Foundation\Testing\DatabaseMigrations;
@@ -29,7 +31,7 @@
$this->deleteTestUser('admin@user-domain-signup.com');
$this->deleteTestDomain('user-domain-signup.com');
- Plan::where('mode', 'token')->update(['mode' => 'email']);
+ Plan::whereIn('mode', ['token', 'mandate'])->update(['mode' => 'email']);
}
/**
@@ -42,7 +44,7 @@
$this->deleteTestDomain('user-domain-signup.com');
SignupInvitation::truncate();
- Plan::where('mode', 'token')->update(['mode' => 'email']);
+ Plan::whereIn('mode', ['token', 'mandate'])->update(['mode' => 'email']);
@unlink(storage_path('signup-tokens.txt'));
@@ -518,6 +520,67 @@
});
}
+ /**
+ * Test signup with a mandate plan, also the wallet lock
+ */
+ public function testSignupMandate(): void
+ {
+ // Test the individual plan
+ $plan = Plan::withEnvTenantContext()->where('title', 'individual')->first();
+ $plan->mode = 'mandate';
+ $plan->save();
+
+ $this->browse(function (Browser $browser) {
+ $browser->visit(new Signup())
+ ->waitFor('@step0 .plan-individual button')
+ ->click('@step0 .plan-individual button')
+ // Test Back button
+ ->whenAvailable('@step3', function ($browser) {
+ $browser->click('button[type=button]');
+ })
+ ->whenAvailable('@step0', function ($browser) {
+ $browser->click('.plan-individual button');
+ })
+ // Test submit
+ ->whenAvailable('@step3', function ($browser) {
+ $domains = Domain::getPublicDomains();
+ $domains_count = count($domains);
+
+ $browser->assertMissing('.card-title')
+ ->assertElementsCount('select#signup_domain option', $domains_count, false)
+ ->assertText('select#signup_domain option:nth-child(1)', $domains[0])
+ ->assertValue('select#signup_domain option:nth-child(1)', $domains[0])
+ ->type('#signup_login', 'signuptestdusk')
+ ->type('#signup_password', '12345678')
+ ->type('#signup_password_confirmation', '12345678')
+ ->click('[type=submit]');
+ })
+ ->waitUntilMissing('@step3')
+ ->on(new Wallet())
+ ->assertSeeIn('#lock-alert', "The account is locked")
+ ->within(new Menu(), function ($browser) {
+ $browser->clickMenuItem('logout');
+ });
+ });
+
+ $user = User::where('email', 'signuptestdusk@' . \config('app.domain'))->first();
+ $this->assertSame($plan->id, $user->getSetting('plan_id'));
+
+ // Login again and see that the account is still locked
+ $this->browse(function (Browser $browser) use ($user) {
+ $browser->on(new Home())
+ ->submitLogon($user->email, '12345678', false)
+ ->waitForLocation('/wallet')
+ ->on(new Wallet())
+ ->assertSeeIn('#lock-alert', "The account is locked")
+ ->within(new Menu(), function ($browser) {
+ $browser->clickMenuItem('logout');
+ });
+
+ // TODO: Test automatic UI unlock after creating a valid auto-payment mandate
+ });
+ }
+
/**
* Test signup with a token plan
*/
diff --git a/src/tests/Feature/Backends/IMAPTest.php b/src/tests/Feature/Backends/IMAPTest.php
--- a/src/tests/Feature/Backends/IMAPTest.php
+++ b/src/tests/Feature/Backends/IMAPTest.php
@@ -107,6 +107,7 @@
$result = IMAP::createUser($user);
$this->assertTrue($result);
$this->assertTrue(IMAP::verifyAccount($user->email));
+ $this->assertTrue(IMAP::verifyDefaultFolders($user->email));
$imap = $this->getImap();
$quota = $imap->getQuota('user/' . $user->email);
@@ -128,6 +129,7 @@
$result = IMAP::verifyAccount($user->email);
$this->assertFalse($result);
+ $this->assertFalse(IMAP::verifyDefaultFolders($user->email));
}
/**
@@ -150,7 +152,9 @@
$imap = $this->getImap();
$expectedAcl = ['john@kolab.org' => str_split('lrswipkxtecdn')];
- $this->assertSame($expectedAcl, $imap->getACL(IMAP::toUTF7($imapFolder)));
+ $acl = $imap->getACL(IMAP::toUTF7($imapFolder));
+ $this->assertTrue(is_array($acl) && isset($acl['john@kolab.org']));
+ $this->assertSame($expectedAcl['john@kolab.org'], $acl['john@kolab.org']);
// Update the resource (rename)
$resource->name = 'Resource1 ©' . time();
@@ -160,7 +164,9 @@
$this->assertTrue(IMAP::updateResource($resource, ['folder' => $imapFolder]));
$this->assertTrue($imapFolder != $newImapFolder);
$this->assertTrue(IMAP::verifySharedFolder($newImapFolder));
- $this->assertSame($expectedAcl, $imap->getACL(IMAP::toUTF7($newImapFolder)));
+ $acl = $imap->getACL(IMAP::toUTF7($newImapFolder));
+ $this->assertTrue(is_array($acl) && isset($acl['john@kolab.org']));
+ $this->assertSame($expectedAcl['john@kolab.org'], $acl['john@kolab.org']);
// Update the resource (acl change)
$resource->setSetting('invitation_policy', 'accept');
@@ -196,7 +202,11 @@
'jack@kolab.org' => str_split('lrs')
];
- $this->assertSame($expectedAcl, $imap->getACL(IMAP::toUTF7($imapFolder)));
+ $acl = $imap->getACL(IMAP::toUTF7($imapFolder));
+ $this->assertTrue(is_array($acl) && isset($acl['john@kolab.org']));
+ $this->assertSame($expectedAcl['john@kolab.org'], $acl['john@kolab.org']);
+ $this->assertTrue(is_array($acl) && isset($acl['jack@kolab.org']));
+ $this->assertSame($expectedAcl['jack@kolab.org'], $acl['jack@kolab.org']);
// Update shared folder (acl)
$folder->setSetting('acl', json_encode(['jack@kolab.org, read-only']));
@@ -205,7 +215,10 @@
$expectedAcl = ['jack@kolab.org' => str_split('lrs')];
- $this->assertSame($expectedAcl, $imap->getACL(IMAP::toUTF7($imapFolder)));
+ $acl = $imap->getACL(IMAP::toUTF7($imapFolder));
+ $this->assertTrue(is_array($acl) && isset($acl['jack@kolab.org']));
+ $this->assertSame($expectedAcl['jack@kolab.org'], $acl['jack@kolab.org']);
+ $this->assertTrue(!isset($acl['john@kolab.org']));
// Update the shared folder (rename)
$folder->name = 'SharedFolder1 ©' . time();
@@ -215,7 +228,10 @@
$this->assertTrue(IMAP::updateSharedFolder($folder, ['folder' => $imapFolder]));
$this->assertTrue($imapFolder != $newImapFolder);
$this->assertTrue(IMAP::verifySharedFolder($newImapFolder));
- $this->assertSame($expectedAcl, $imap->getACL(IMAP::toUTF7($newImapFolder)));
+
+ $acl = $imap->getACL(IMAP::toUTF7($newImapFolder));
+ $this->assertTrue(is_array($acl) && isset($acl['jack@kolab.org']));
+ $this->assertSame($expectedAcl['jack@kolab.org'], $acl['jack@kolab.org']);
// Delete the shared folder
$this->assertTrue(IMAP::deleteSharedFolder($folder));
diff --git a/src/tests/Feature/Controller/PaymentsMollieTest.php b/src/tests/Feature/Controller/PaymentsMollieTest.php
--- a/src/tests/Feature/Controller/PaymentsMollieTest.php
+++ b/src/tests/Feature/Controller/PaymentsMollieTest.php
@@ -4,6 +4,7 @@
use App\Http\Controllers\API\V4\PaymentsController;
use App\Payment;
+use App\Plan;
use App\Providers\PaymentProvider;
use App\Transaction;
use App\Wallet;
@@ -47,6 +48,7 @@
Transaction::WALLET_CHARGEBACK,
];
Transaction::where('object_id', $wallet->id)->whereIn('type', $types)->delete();
+ Plan::withEnvTenantContext()->where('title', 'individual')->update(['mode' => 'email', 'months' => 1]);
}
/**
@@ -68,6 +70,7 @@
Transaction::WALLET_CHARGEBACK,
];
Transaction::where('object_id', $wallet->id)->whereIn('type', $types)->delete();
+ Plan::withEnvTenantContext()->where('title', 'individual')->update(['mode' => 'email', 'months' => 1]);
Utils::setTestExchangeRates([]);
parent::tearDown();
@@ -337,6 +340,44 @@
$this->assertNull($wallet->fresh()->getSetting('mollie_mandate_id'));
}
+ /**
+ * Test fetching an outo-payment mandate parameters
+ *
+ * @group mollie
+ */
+ public function testMandateParams(): void
+ {
+ $plan = Plan::withEnvTenantContext()->where('title', 'individual')->first();
+ $user = $this->getTestUser('payment-test@' . \config('app.domain'));
+ $wallet = $user->wallets()->first();
+
+ $response = $this->actingAs($user)->get("api/v4/payments/mandate");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame((int) ceil(Payment::MIN_AMOUNT / 100), $json['amount']);
+ $this->assertSame($json['amount'], $json['minAmount']);
+ $this->assertSame(0, $json['balance']);
+ $this->assertFalse($json['isValid']);
+ $this->assertFalse($json['isDisabled']);
+
+ $plan->months = 12;
+ $plan->save();
+ $user->setSetting('plan_id', $plan->id);
+
+ $response = $this->actingAs($user)->get("api/v4/payments/mandate");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame((int) ceil(Payment::MIN_AMOUNT / 100), $json['amount']);
+ $this->assertSame((int) ceil(($plan->cost() * $plan->months) / 100), $json['minAmount']);
+
+ // TODO: Test more cases
+ // TODO: Test user unrestricting if mandate is valid
+ }
+
/**
* Test creating a payment and receiving a status via webhook
*
diff --git a/src/tests/Feature/Controller/SignupTest.php b/src/tests/Feature/Controller/SignupTest.php
--- a/src/tests/Feature/Controller/SignupTest.php
+++ b/src/tests/Feature/Controller/SignupTest.php
@@ -84,11 +84,29 @@
return $this->domain;
}
+ /**
+ * Test fetching public domains for signup
+ */
+ public function testSignupDomains(): void
+ {
+ $response = $this->get('/api/auth/signup/domains');
+ $json = $response->json();
+
+ $response->assertStatus(200);
+
+ $this->assertCount(2, $json);
+ $this->assertSame('success', $json['status']);
+ $this->assertSame(Domain::getPublicDomains(), $json['domains']);
+ }
+
/**
* Test fetching plans for signup
*/
public function testSignupPlans(): void
{
+ $individual = Plan::withEnvTenantContext()->where('title', 'individual')->first();
+ $group = Plan::withEnvTenantContext()->where('title', 'group')->first();
+
$response = $this->get('/api/auth/signup/plans');
$json = $response->json();
@@ -96,10 +114,16 @@
$this->assertSame('success', $json['status']);
$this->assertCount(2, $json['plans']);
- $this->assertArrayHasKey('title', $json['plans'][0]);
- $this->assertArrayHasKey('name', $json['plans'][0]);
- $this->assertArrayHasKey('description', $json['plans'][0]);
+ $this->assertSame($individual->title, $json['plans'][0]['title']);
+ $this->assertSame($individual->name, $json['plans'][0]['name']);
+ $this->assertSame($individual->description, $json['plans'][0]['description']);
+ $this->assertFalse($json['plans'][0]['isDomain']);
$this->assertArrayHasKey('button', $json['plans'][0]);
+ $this->assertSame($group->title, $json['plans'][1]['title']);
+ $this->assertSame($group->name, $json['plans'][1]['name']);
+ $this->assertSame($group->description, $json['plans'][1]['description']);
+ $this->assertTrue($json['plans'][1]['isDomain']);
+ $this->assertArrayHasKey('button', $json['plans'][1]);
}
/**
diff --git a/src/tests/Feature/Controller/UsersTest.php b/src/tests/Feature/Controller/UsersTest.php
--- a/src/tests/Feature/Controller/UsersTest.php
+++ b/src/tests/Feature/Controller/UsersTest.php
@@ -6,6 +6,7 @@
use App\Domain;
use App\Http\Controllers\API\V4\UsersController;
use App\Package;
+use App\Plan;
use App\Sku;
use App\Tenant;
use App\User;
@@ -47,7 +48,10 @@
$wallet->save();
$user->settings()->whereIn('key', ['greylist_enabled', 'guam_enabled'])->delete();
$user->status |= User::STATUS_IMAP_READY | User::STATUS_LDAP_READY;
+ $user->status &= ~User::STATUS_RESTRICTED;
$user->save();
+ Plan::withEnvTenantContext()->where('title', 'individual')->update(['mode' => 'email']);
+ $user->setSettings(['plan_id' => null]);
}
/**
@@ -78,7 +82,10 @@
$wallet->save();
$user->settings()->whereIn('key', ['greylist_enabled', 'guam_enabled'])->delete();
$user->status |= User::STATUS_IMAP_READY | User::STATUS_LDAP_READY;
+ $user->status &= ~User::STATUS_RESTRICTED;
$user->save();
+ Plan::withEnvTenantContext()->where('title', 'individual')->update(['mode' => 'email']);
+ $user->setSettings(['plan_id' => null]);
parent::tearDown();
}
@@ -1372,14 +1379,15 @@
public function testUserResponse(): void
{
$provider = \config('services.payment_provider') ?: 'mollie';
- $user = $this->getTestUser('john@kolab.org');
- $wallet = $user->wallets()->first();
+ $john = $this->getTestUser('john@kolab.org');
+ $wallet = $john->wallets()->first();
$wallet->setSettings(['mollie_id' => null, 'stripe_id' => null]);
- $result = $this->invokeMethod(new UsersController(), 'userResponse', [$user]);
+ $wallet->owner->setSettings(['plan_id' => null]);
+ $result = $this->invokeMethod(new UsersController(), 'userResponse', [$john]);
- $this->assertEquals($user->id, $result['id']);
- $this->assertEquals($user->email, $result['email']);
- $this->assertEquals($user->status, $result['status']);
+ $this->assertEquals($john->id, $result['id']);
+ $this->assertEquals($john->email, $result['email']);
+ $this->assertEquals($john->status, $result['status']);
$this->assertTrue(is_array($result['statusInfo']));
$this->assertTrue(is_array($result['settings']));
@@ -1392,13 +1400,20 @@
$this->assertCount(1, $result['wallets']);
$this->assertSame($wallet->id, $result['wallet']['id']);
$this->assertArrayNotHasKey('discount', $result['wallet']);
+ $this->assertFalse($result['isLocked']);
$this->assertTrue($result['statusInfo']['enableDomains']);
$this->assertTrue($result['statusInfo']['enableWallets']);
+ $this->assertTrue($result['statusInfo']['enableWalletMandates']);
+ $this->assertTrue($result['statusInfo']['enableWalletPayments']);
$this->assertTrue($result['statusInfo']['enableUsers']);
$this->assertTrue($result['statusInfo']['enableSettings']);
// Ned is John's wallet controller
+ $plan = Plan::withEnvTenantContext()->where('title', 'individual')->first();
+ $plan->mode = 'mandate';
+ $plan->save();
+ $wallet->owner->setSettings(['plan_id' => $plan->id]);
$ned = $this->getTestUser('ned@kolab.org');
$ned_wallet = $ned->wallets()->first();
$result = $this->invokeMethod(new UsersController(), 'userResponse', [$ned]);
@@ -1414,9 +1429,12 @@
$this->assertSame($ned_wallet->id, $result['wallets'][0]['id']);
$this->assertSame($provider, $result['wallet']['provider']);
$this->assertSame($provider, $result['wallets'][0]['provider']);
+ $this->assertFalse($result['isLocked']);
$this->assertTrue($result['statusInfo']['enableDomains']);
$this->assertTrue($result['statusInfo']['enableWallets']);
+ $this->assertTrue($result['statusInfo']['enableWalletMandates']);
+ $this->assertFalse($result['statusInfo']['enableWalletPayments']);
$this->assertTrue($result['statusInfo']['enableUsers']);
$this->assertTrue($result['statusInfo']['enableSettings']);
@@ -1426,11 +1444,11 @@
$wallet->save();
$mod_provider = $provider == 'mollie' ? 'stripe' : 'mollie';
$wallet->setSetting($mod_provider . '_id', 123);
- $user->refresh();
+ $john->refresh();
- $result = $this->invokeMethod(new UsersController(), 'userResponse', [$user]);
+ $result = $this->invokeMethod(new UsersController(), 'userResponse', [$john]);
- $this->assertEquals($user->id, $result['id']);
+ $this->assertEquals($john->id, $result['id']);
$this->assertSame($discount->id, $result['wallet']['discount_id']);
$this->assertSame($discount->discount, $result['wallet']['discount']);
$this->assertSame($discount->description, $result['wallet']['discount_description']);
@@ -1439,6 +1457,7 @@
$this->assertSame($discount->discount, $result['wallets'][0]['discount']);
$this->assertSame($discount->description, $result['wallets'][0]['discount_description']);
$this->assertSame($mod_provider, $result['wallets'][0]['provider']);
+ $this->assertFalse($result['isLocked']);
// Jack is not a John's wallet controller
$jack = $this->getTestUser('jack@kolab.org');
@@ -1446,8 +1465,17 @@
$this->assertFalse($result['statusInfo']['enableDomains']);
$this->assertFalse($result['statusInfo']['enableWallets']);
+ $this->assertFalse($result['statusInfo']['enableWalletMandates']);
+ $this->assertFalse($result['statusInfo']['enableWalletPayments']);
$this->assertFalse($result['statusInfo']['enableUsers']);
$this->assertFalse($result['statusInfo']['enableSettings']);
+ $this->assertFalse($result['isLocked']);
+
+ // Test locked user
+ $john->restrict();
+ $result = $this->invokeMethod(new UsersController(), 'userResponse', [$john]);
+
+ $this->assertTrue($result['isLocked']);
}
/**
diff --git a/src/tests/Feature/PaymentTest.php b/src/tests/Feature/PaymentTest.php
--- a/src/tests/Feature/PaymentTest.php
+++ b/src/tests/Feature/PaymentTest.php
@@ -82,7 +82,8 @@
$wallet->balance = -5000;
$wallet->save();
- // Credit the 2nd payment
+ // Credit the 2nd payment (restricted user)
+ $user->restrict();
$payment2->credit('Test2');
$wallet->refresh();
$transaction = $wallet->transactions()->first();
@@ -91,6 +92,7 @@
$this->assertSame('1', $wallet->getSetting('mandate_disabled'));
$this->assertSame($payment2->credit_amount, $transaction->amount);
$this->assertSame("Auto-payment transaction {$payment2->id} using Test2", $transaction->description);
+ $this->assertFalse($user->refresh()->isRestricted());
}
/**
diff --git a/src/tests/Feature/UserTest.php b/src/tests/Feature/UserTest.php
--- a/src/tests/Feature/UserTest.php
+++ b/src/tests/Feature/UserTest.php
@@ -1183,7 +1183,8 @@
\App\Jobs\User\UpdateJob::class,
function ($job) use ($user) {
return TestCase::getObjectProperty($job, 'userId') == $user->id;
- });
+ }
+ );
$userB->restrict();
$this->assertTrue($userB->fresh()->isRestricted());
@@ -1200,7 +1201,8 @@
\App\Jobs\User\UpdateJob::class,
function ($job) use ($user) {
return TestCase::getObjectProperty($job, 'userId') == $user->id;
- });
+ }
+ );
Queue::fake(); // reset queue state
@@ -1213,7 +1215,8 @@
\App\Jobs\User\UpdateJob::class,
function ($job) use ($userB) {
return TestCase::getObjectProperty($job, 'userId') == $userB->id;
- });
+ }
+ );
}
/**
diff --git a/src/tests/Infrastructure/ActivesyncTest.php b/src/tests/Infrastructure/ActivesyncTest.php
--- a/src/tests/Infrastructure/ActivesyncTest.php
+++ b/src/tests/Infrastructure/ActivesyncTest.php
@@ -38,15 +38,38 @@
{
parent::setUp();
- if (!self::$user) {
- self::$user = $this->getTestUser('activesynctest@kolab.org', ['password' => 'simple123'], true);
- }
if (!self::$deviceId) {
// By always creating a new device we force syncroton to initialize.
// Otherwise we work against uninitialized metadata (subscription states),
// because the account has been removed, but syncroton doesn't reinitalize the metadata for known devices.
self::$deviceId = (string) Str::uuid();
}
+
+ $deviceId = self::$deviceId;
+ \config(['imap.default_folders' => [
+ 'Drafts' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'mail.drafts',
+ '/private/vendor/kolab/activesync' => "{\"FOLDER\":{\"{$deviceId}\":{\"S\":1}}}"
+ ],
+ ],
+ 'Calendar' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'event.default',
+ '/private/vendor/kolab/activesync' => "{\"FOLDER\":{\"{$deviceId}\":{\"S\":1}}}"
+ ],
+ ],
+ 'Contacts' => [
+ 'metadata' => [
+ '/private/vendor/kolab/folder-type' => 'contact.default',
+ '/private/vendor/kolab/activesync' => "{\"FOLDER\":{\"{$deviceId}\":{\"S\":1}}}"
+ ],
+ ],
+ ]]);
+
+ if (!self::$user) {
+ self::$user = $this->getTestUser('activesynctest@kolab.org', ['password' => 'simple123'], true);
+ }
if (!self::$client) {
self::$client = new \GuzzleHttp\Client([
'http_errors' => false, // No exceptions
@@ -103,12 +126,9 @@
// The hash is based on the name, so it's always the same
$inboxId = '38b950ebd62cd9a66929c89615d0fc04';
$this->assertStringContainsString($inboxId, $xml);
- //TODO for this to work we need to create the default folders in IMAP::createUser
- // $this->assertStringContainsString('Drafts', $result);
- // $this->assertStringContainsString('Sent', $result);
- // $this->assertStringContainsString('Trash', $result);
- // $this->assertStringContainsString('Calendar', $result);
- // $this->assertStringContainsString('Contacts', $result);
+ $this->assertStringContainsString('Drafts', $xml);
+ $this->assertStringContainsString('Calendar', $xml);
+ $this->assertStringContainsString('Contacts', $xml);
// Find the inbox for the next step
// $collectionIds = $dom->getElementsByTagName('ServerId');
diff --git a/src/tests/Unit/Backends/DAV/FolderTest.php b/src/tests/Unit/Backends/DAV/FolderTest.php
--- a/src/tests/Unit/Backends/DAV/FolderTest.php
+++ b/src/tests/Unit/Backends/DAV/FolderTest.php
@@ -14,7 +14,11 @@
{
$xml = <<<XML
<?xml version="1.0" encoding="utf-8"?>
-<d:multistatus xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:a="http://apple.com/ns/ical/" xmlns:k="Kolab:">
+ <d:multistatus xmlns:d="DAV:"
+ xmlns:cs="http://calendarserver.org/ns/"
+ xmlns:c="urn:ietf:params:xml:ns:caldav"
+ xmlns:a="http://apple.com/ns/ical/"
+ xmlns:k="Kolab:">
<d:response>
<d:href>/dav/calendars/user/alec@aphy.io/Default/</d:href>
<d:propstat>
diff --git a/utils/kolabendpointtester.py b/utils/kolabendpointtester.py
--- a/utils/kolabendpointtester.py
+++ b/utils/kolabendpointtester.py
@@ -352,10 +352,10 @@
print(' autodiscover CNAME', rdata.target)
except dns.resolver.NXDOMAIN:
success = False
- print(" ERROR on autodiscover. CNAME entry")
+ print(f" ERROR on autodiscover.{host} CNAME entry")
except dns.resolver.NoAnswer:
success = False
- print(" ERROR on autodiscover. CNAME entry")
+ print(f" ERROR on autodiscover.{host} CNAME entry")
srv_records = [
f"_autodiscover._tcp.{host}",

File Metadata

Mime Type
text/plain
Expires
Mon, Apr 6, 4:39 AM (8 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18835640
Default Alt Text
D4130.1775450386.diff (132 KB)

Event Timeline