Compare commits
47
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8904350b71 | ||
|
|
c3234882eb | ||
|
|
458d39d48a | ||
|
|
5cba10ccff | ||
|
|
b48704e7ff | ||
|
|
69116d8eb7 | ||
|
|
d15f0ee804 | ||
|
|
a1e8a5c4af | ||
|
|
02dd5c3501 | ||
|
|
7a7f85d1a7 | ||
|
|
6d4016dfdb | ||
|
|
e06a0d2035 | ||
|
|
f8ff98b657 | ||
|
|
512f14f6d0 | ||
|
|
9f4f48a44f | ||
|
|
092e7694e0 | ||
|
|
42c227beca | ||
|
|
c400f45ef1 | ||
|
|
78655ae127 | ||
|
|
29e00f3e9e | ||
|
|
beb0d9cb1b | ||
|
|
5c1278a895 | ||
|
|
616c9291e0 | ||
|
|
9941e2a04b | ||
|
|
b40d777a86 | ||
|
|
70f72eb92f | ||
|
|
0a828a739e | ||
|
|
395a01ed52 | ||
|
|
c05b331ac0 | ||
|
|
da32b3f86d | ||
|
|
a09720cf22 | ||
|
|
0364f7f2a4 | ||
|
|
00082eb0bc | ||
|
|
178b1a3a0e | ||
|
|
9ffa72ee9b | ||
|
|
169a0c6125 | ||
|
|
c7b0133000 | ||
|
|
2f9c2cf4da | ||
|
|
57db6b5f91 | ||
|
|
f65ab9b43a | ||
|
|
2258d5cd60 | ||
|
|
ea427ea41a | ||
|
|
ce86d14826 | ||
|
|
7f7c743976 | ||
|
|
6f86506b28 | ||
|
|
e44bc1fc91 | ||
|
|
c90e2b0501 |
No files matched your search
@@ -0,0 +1,163 @@
|
||||
FROM ubuntu:noble
|
||||
|
||||
# The standard user with which vscode-server will be executed
|
||||
ARG USERNAME=sogo
|
||||
ARG USER_ID=1000 GROUP_ID=1000
|
||||
|
||||
ENV CONTAINER_USERNAME=${USERNAME}
|
||||
|
||||
# rename user ubuntu to ${USERNAME}
|
||||
RUN usermod -l ${CONTAINER_USERNAME} ubuntu && groupmod -n ${CONTAINER_USERNAME} ubuntu
|
||||
|
||||
RUN apt-get update && apt-get install -y sudo \
|
||||
&& usermod -aG sudo ${CONTAINER_USERNAME} \
|
||||
&& echo ${CONTAINER_USERNAME} ALL=\(ALL\) NOPASSWD:ALL > /etc/sudoers.d/${CONTAINER_USERNAME} \
|
||||
&& chmod 0440 /etc/sudoers.d/${CONTAINER_USERNAME}
|
||||
|
||||
# Ensure sudo group users are not asked for a password
|
||||
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/${CONTAINER_USERNAME} && chmod 0440 /etc/sudoers.d/${CONTAINER_USERNAME}
|
||||
|
||||
ENV WORKSPACE=/workspace
|
||||
RUN mkdir -p ${WORKSPACE} && chown -R ${CONTAINER_USERNAME}:${CONTAINER_USERNAME} ${WORKSPACE}
|
||||
|
||||
RUN apt-get clean && apt-get update --allow-unauthenticated --allow-insecure-repositories \
|
||||
&& apt-get install -y --fix-missing git cmake libexpat-dev curl telnet nano
|
||||
|
||||
|
||||
WORKDIR /tmp/build
|
||||
|
||||
RUN mkdir -p /src/libwbxml && mkdir -p /tmp/build/libwbxml
|
||||
RUN git clone https://github.com/libwbxml/libwbxml.git /src/libwbxml/
|
||||
RUN cmake -B/tmp/build/libwbxml -DCMAKE_INSTALL_PREFIX=$prefix /src/libwbxml && cd /tmp/build/libwbxml && make && make install
|
||||
# For backward compatibility
|
||||
|
||||
WORKDIR ${WORKSPACE}
|
||||
|
||||
RUN ln -s /include/libwbxml-1.1 /usr/include/libwbxml-1.0
|
||||
RUN ln -s /include/libwbxml-1.1 /usr/include/libwbxml
|
||||
RUN ln -s /include/libwbxml-1.1 /usr/include/libwbxml-1.1
|
||||
|
||||
# download, prepare & compile
|
||||
RUN echo "Download SOPE sources" \
|
||||
&& git clone https://github.com/Alinto/sope.git /src/SOPE \
|
||||
&& echo "install required packages" \
|
||||
&& apt-get update \
|
||||
&& apt-get upgrade --allow-unauthenticated -qy \
|
||||
&& DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install --allow-unauthenticated -qy --no-install-recommends \
|
||||
gnustep-make \
|
||||
gnustep-base-runtime \
|
||||
libgnustep-base-dev \
|
||||
pkg-config \
|
||||
make \
|
||||
gobjc \
|
||||
g++ \
|
||||
gdb gdbserver \
|
||||
vim \
|
||||
liblasso3-dev \
|
||||
python-is-python3 \
|
||||
libz-dev \
|
||||
zlib1g-dev \
|
||||
libpq-dev \
|
||||
libmysqlclient-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libsodium-dev \
|
||||
libxml2-dev \
|
||||
libssl-dev \
|
||||
libldap2-dev \
|
||||
libzip-dev \
|
||||
mariadb-client \
|
||||
postgresql-client \
|
||||
tmpreaper \
|
||||
python3-m2crypto \
|
||||
python3-simplejson \
|
||||
python3-vobject \
|
||||
python3-dateutil \
|
||||
postgresql-server-dev-all \
|
||||
libmemcached-dev \
|
||||
libcurl4-openssl-dev \
|
||||
tzdata \
|
||||
libytnef0 \
|
||||
libytnef0-dev \
|
||||
liboath-dev \
|
||||
sudo \
|
||||
lsof \
|
||||
systemd \
|
||||
tcpdump \
|
||||
jq libxml2-utils
|
||||
|
||||
RUN echo "compiling sope" \
|
||||
&& cd /src/SOPE \
|
||||
&& ./configure --with-gnustep --enable-debug --disable-strip \
|
||||
&& make \
|
||||
&& make install
|
||||
|
||||
# copy sogo local sources
|
||||
COPY / /src/SOGo
|
||||
RUN chown -R ${CONTAINER_USERNAME}:${CONTAINER_USERNAME} /src/SOGo
|
||||
|
||||
# install sogo
|
||||
RUN echo "compiling sogo"
|
||||
|
||||
# RUN chmod +x /src/SOGo/configure
|
||||
WORKDIR /src/SOGo
|
||||
RUN ./configure --enable-debug --disable-strip --enable-mfa
|
||||
RUN make
|
||||
RUN make install
|
||||
RUN echo "register sogo library"
|
||||
RUN echo "/usr/local/lib/sogo" > /etc/ld.so.conf.d/sogo.conf
|
||||
RUN ldconfig
|
||||
RUN echo "create directories and enforce permissions"
|
||||
RUN install -o sogo -g sogo -m 755 -d /var/run/sogo
|
||||
RUN install -o sogo -g sogo -m 750 -d /var/spool/sogo
|
||||
RUN install -o sogo -g sogo -m 750 -d /var/log/sogo
|
||||
|
||||
WORKDIR ${WORKSPACE}
|
||||
|
||||
# Copy the binary file into /usr/local/bin
|
||||
COPY .devcontainer/conf/sogo/compile_sogo.sh /usr/local/bin/compile_sogo.sh
|
||||
# Set the appropriate permissions
|
||||
RUN chmod +x /usr/local/bin/compile_sogo.sh
|
||||
|
||||
# Install nodejs
|
||||
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
|
||||
RUN apt install -qy nodejs \
|
||||
&& npm i -g grunt
|
||||
|
||||
# Users
|
||||
RUN useradd sogo-tests1 && \
|
||||
useradd sogo-tests2 && \
|
||||
useradd sogo-tests3 && \
|
||||
useradd sogo-tests-super
|
||||
|
||||
# Tests
|
||||
RUN npm i -g xunit-viewer
|
||||
|
||||
|
||||
# SOGo
|
||||
RUN apt-get clean && apt-get install -y --no-install-recommends wget && cd /tmp && \
|
||||
wget https://raw.githubusercontent.com/Alinto/sogo/master/packaging/debian/sogo.init && \
|
||||
ln -s /usr/local/sbin/sogod /usr/sbin/sogod && \
|
||||
mv sogo.init /etc/init.d/sogod && chmod +x /etc/init.d/sogod && \
|
||||
mkdir /etc/sogo
|
||||
COPY .devcontainer/conf/sogo/sogo.conf /etc/sogo/
|
||||
ADD .devcontainer/conf/sogo/sogo.conf /etc/sogo/sogo-base.conf
|
||||
|
||||
# Clean
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Dev env script
|
||||
ADD .devcontainer/conf/sogo/devenv /usr/sbin/
|
||||
ADD .devcontainer/conf/sogo/common.sh /include/
|
||||
RUN chmod +x /usr/sbin/devenv
|
||||
RUN chmod +x /include/common.sh
|
||||
|
||||
# Interface the environment
|
||||
EXPOSE 50000 50001
|
||||
|
||||
# Entrypoint
|
||||
ADD .devcontainer/conf/sogo/entrypoint.sh /
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
USER ${CONTAINER_USERNAME}
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -0,0 +1,67 @@
|
||||
CREATE USER IF NOT EXISTS 'sogobuild'@'%' IDENTIFIED BY 'sogo123';
|
||||
GRANT ALL PRIVILEGES ON * . * TO 'sogobuild'@'%';
|
||||
|
||||
FLUSH PRIVILEGES;
|
||||
|
||||
CREATE DATABASE IF NOT EXISTS sogo;
|
||||
USE sogo;
|
||||
-- INSERT INTO
|
||||
CREATE DATABASE IF NOT EXISTS sogo_integration_tests_auth;
|
||||
USE sogo_integration_tests_auth;
|
||||
-- MySQL dump 10.16 Distrib 10.1.48-MariaDB, for debian-linux-gnu (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: sogo_integration_tests_auth
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 10.1.48-MariaDB-0+deb9u2
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `sogoauth`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `sogoauth`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `sogoauth` (
|
||||
`c_uid` varchar(255) NOT NULL,
|
||||
`c_name` varchar(255) NOT NULL,
|
||||
`c_password` varchar(255) DEFAULT NULL,
|
||||
`c_cn` varchar(255) DEFAULT NULL,
|
||||
`mail` varchar(255) DEFAULT NULL,
|
||||
`kind` varchar(255) DEFAULT NULL,
|
||||
`multiplebookings` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`c_uid`),
|
||||
UNIQUE KEY `c_name` (`c_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `sogoauth`
|
||||
--
|
||||
|
||||
LOCK TABLES `sogoauth` WRITE;
|
||||
/*!40000 ALTER TABLE `sogoauth` DISABLE KEYS */;
|
||||
INSERT INTO `sogoauth` VALUES ('res','res','sogo','Resource no overbook','res@example.org','location',1),('res-nolimit','res-nolimit','sogo','Resource can overbook','res-nolimit@example.org','location',0),('sogo-tests-super','sogo-tests-super','sogo','sogo test super','sogo-tests-super@example.org',NULL,NULL),('sogo-tests1','sogo-tests1','sogo','sogo One','sogo-tests1@example.org',NULL,NULL),('sogo-tests2','sogo-tests2','sogo','sogo Two','sogo-tests2@example.org',NULL,NULL),('sogo-tests3','sogo-tests3','sogo','sogo Three','sogo-tests3@example.org',NULL,NULL);
|
||||
/*!40000 ALTER TABLE `sogoauth` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2023-02-22 4:15:31
|
||||
@@ -0,0 +1,72 @@
|
||||
# From https://github.com/dovecot/docker/blob/main/2.3.21.1/
|
||||
FROM debian:11-slim
|
||||
|
||||
LABEL org.opencontainers.image.authors="dovecot@dovecot.org"
|
||||
|
||||
ENV container=docker \
|
||||
LC_ALL=C
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ADD dovecot.gpg /etc/apt/keyrings/dovecot.gpg
|
||||
ADD dovecot.list /etc/apt/sources.list.d
|
||||
|
||||
RUN apt-get -y update && apt-get -y install \
|
||||
tini \
|
||||
dovecot-core \
|
||||
dovecot-gssapi \
|
||||
dovecot-imapd \
|
||||
dovecot-ldap \
|
||||
dovecot-lmtpd \
|
||||
dovecot-managesieved \
|
||||
dovecot-mysql \
|
||||
dovecot-pgsql \
|
||||
dovecot-pop3d \
|
||||
dovecot-sieve \
|
||||
dovecot-solr \
|
||||
dovecot-sqlite \
|
||||
dovecot-submissiond \
|
||||
ca-certificates \
|
||||
ssl-cert && \
|
||||
rm -rf /var/lib/apt/lists && \
|
||||
groupadd -g 1000 vmail && \
|
||||
useradd -u 1000 -g 1000 vmail -d /srv/vmail && \
|
||||
passwd -l vmail && \
|
||||
rm -rf /etc/dovecot && \
|
||||
mkdir /srv/mail && \
|
||||
chown vmail:vmail /srv/mail && \
|
||||
make-ssl-cert generate-default-snakeoil && \
|
||||
mkdir /etc/dovecot && \
|
||||
ln -s /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/dovecot/cert.pem && \
|
||||
ln -s /etc/ssl/private/ssl-cert-snakeoil.key /etc/dovecot/key.pem
|
||||
|
||||
#ADD dovecot.conf /etc/dovecot/dovecot.conf
|
||||
|
||||
# Add for SOGo env
|
||||
RUN apt-get update --allow-insecure-repositories && \
|
||||
apt-get install --allow-unauthenticated -y dovecot-ldap dovecot-sieve dovecot-managesieved dovecot-lmtpd
|
||||
|
||||
RUN chown -R dovecot:dovecot /var/mail
|
||||
RUN mkdir /var/sieve && \
|
||||
chmod -R 755 /var/sieve && \
|
||||
chown -R vmail:vmail /var/sieve
|
||||
|
||||
ADD dovecot.conf /etc/dovecot/dovecot.conf
|
||||
ADD conf.d /etc/dovecot/conf.d
|
||||
ADD dovecot-ldap.conf.ext /etc/dovecot/dovecot-ldap.conf.ext
|
||||
# Clean
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
# End add for SOGo env
|
||||
|
||||
ADD wait-for-it.sh /wait-for-it.sh
|
||||
|
||||
EXPOSE 24
|
||||
EXPOSE 110
|
||||
EXPOSE 143
|
||||
EXPOSE 587
|
||||
EXPOSE 990
|
||||
EXPOSE 993
|
||||
EXPOSE 4190
|
||||
|
||||
VOLUME ["/etc/dovecot", "/srv/mail"]
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
CMD ["./wait-for-it.sh", "openldap:389", "--", "/usr/sbin/dovecot", "-F"]
|
||||
@@ -0,0 +1,3 @@
|
||||
disable_plaintext_auth = no
|
||||
auth_mechanisms = plain login
|
||||
!include auth-ldap.conf.ext
|
||||
@@ -0,0 +1,11 @@
|
||||
mail_location = maildir:/var/mail/%n
|
||||
namespace inbox {
|
||||
inbox = yes
|
||||
}
|
||||
mail_privileged_group = mail
|
||||
protocol !indexer-worker {
|
||||
}
|
||||
|
||||
protocol imap {
|
||||
mail_plugins = $mail_plugins acl
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
service imap-login {
|
||||
inet_listener imap {
|
||||
#port = 143
|
||||
}
|
||||
inet_listener imaps {
|
||||
#port = 993
|
||||
#ssl = yes
|
||||
}
|
||||
}
|
||||
|
||||
service pop3-login {
|
||||
inet_listener pop3 {
|
||||
#port = 110
|
||||
}
|
||||
inet_listener pop3s {
|
||||
#port = 995
|
||||
#ssl = yes
|
||||
}
|
||||
}
|
||||
|
||||
service submission-login {
|
||||
inet_listener submission {
|
||||
#port = 587
|
||||
}
|
||||
}
|
||||
|
||||
service lmtp {
|
||||
unix_listener /var/spool/dovecot-lmtp {
|
||||
mode = 0660
|
||||
user = root
|
||||
group = root
|
||||
}
|
||||
inet_listener lmtp {
|
||||
port = 24
|
||||
}
|
||||
}
|
||||
|
||||
service imap {
|
||||
# Most of the memory goes to mmap()ing files. You may need to increase this
|
||||
# limit if you have huge mailboxes.
|
||||
#vsz_limit = $default_vsz_limit
|
||||
|
||||
# Max. number of IMAP processes (connections)
|
||||
#process_limit = 1024
|
||||
}
|
||||
|
||||
service pop3 {
|
||||
# Max. number of POP3 processes (connections)
|
||||
#process_limit = 1024
|
||||
}
|
||||
|
||||
service submission {
|
||||
# Max. number of SMTP Submission processes (connections)
|
||||
#process_limit = 1024
|
||||
}
|
||||
|
||||
service auth {
|
||||
unix_listener auth-userdb {
|
||||
#mode = 0666
|
||||
#user =
|
||||
#group =
|
||||
}
|
||||
|
||||
# Postfix smtp-auth
|
||||
#unix_listener /var/spool/postfix/private/auth {
|
||||
# mode = 0666
|
||||
#}
|
||||
|
||||
# Auth process is run as this user.
|
||||
#user = $default_internal_user
|
||||
}
|
||||
|
||||
service auth-worker {
|
||||
# Auth worker process is run as root by default, so that it can access
|
||||
# /etc/shadow. If this isn't necessary, the user should be changed to
|
||||
# $default_internal_user.
|
||||
#user = root
|
||||
}
|
||||
|
||||
service dict {
|
||||
# If dict proxy is used, mail processes should have access to its socket.
|
||||
# For example: mode=0660, group=vmail and global mail_access_groups=vmail
|
||||
unix_listener dict {
|
||||
#mode = 0600
|
||||
#user =
|
||||
#group =
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
protocol lmtp {
|
||||
postmaster_address = postmaster@example.org
|
||||
# Space separated list of plugins to load (default is global mail_plugins).
|
||||
mail_plugins = $mail_plugins sieve
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
plugin {
|
||||
acl = vfile
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
plugin {
|
||||
sieve = file:/var/sieve/%n@example.org;active=/var/sieve/%n@example.org/.dovecot.sieve
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
passdb {
|
||||
driver = ldap
|
||||
|
||||
# Path for LDAP configuration file, see example-config/dovecot-ldap.conf.ext
|
||||
args = /etc/dovecot/dovecot-ldap.conf.ext
|
||||
}
|
||||
|
||||
userdb {
|
||||
driver = ldap
|
||||
args = /etc/dovecot/dovecot-ldap.conf.ext
|
||||
|
||||
# Default fields can be used to specify defaults that LDAP may override
|
||||
#default_fields = home=/home/virtual/%u
|
||||
}
|
||||
|
||||
mail_gid = 8
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
hosts = openldap:389
|
||||
dn = cn=admin,dc=example,dc=org
|
||||
dnpass = password
|
||||
debug_level = -1
|
||||
auth_bind = yes
|
||||
ldap_version = 3
|
||||
base = ou=users,dc=example,dc=org
|
||||
user_filter = (&(objectClass=posixAccount)(uid=%u))
|
||||
user_filter = (|(uid=%u)(mail=%u))
|
||||
@@ -0,0 +1,13 @@
|
||||
## Dovecot configuration file
|
||||
|
||||
!include_try /usr/share/dovecot/protocols.d/*.protocol
|
||||
!include conf.d/*.conf
|
||||
protocols = imap lmtp sieve
|
||||
!include_try local.conf
|
||||
|
||||
log_path = /dev/stderr
|
||||
auth_verbose = yes
|
||||
auth_debug = yes
|
||||
mail_debug = yes
|
||||
|
||||
mail_plugins = acl
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
deb [signed-by=/etc/apt/keyrings/dovecot.gpg] http://repo.dovecot.org/ce-2.3.21.1/debian/bullseye bullseye main
|
||||
Executable
+182
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env bash
|
||||
# Use this script to test if a given TCP host/port are available
|
||||
|
||||
WAITFORIT_cmdname=${0##*/}
|
||||
|
||||
echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
|
||||
|
||||
usage()
|
||||
{
|
||||
cat << USAGE >&2
|
||||
Usage:
|
||||
$WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
|
||||
-h HOST | --host=HOST Host or IP under test
|
||||
-p PORT | --port=PORT TCP port under test
|
||||
Alternatively, you specify the host and port as host:port
|
||||
-s | --strict Only execute subcommand if the test succeeds
|
||||
-q | --quiet Don't output any status messages
|
||||
-t TIMEOUT | --timeout=TIMEOUT
|
||||
Timeout in seconds, zero for no timeout
|
||||
-- COMMAND ARGS Execute command with args after the test finishes
|
||||
USAGE
|
||||
exit 1
|
||||
}
|
||||
|
||||
wait_for()
|
||||
{
|
||||
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
|
||||
echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
|
||||
else
|
||||
echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
|
||||
fi
|
||||
WAITFORIT_start_ts=$(date +%s)
|
||||
while :
|
||||
do
|
||||
if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
|
||||
nc -z $WAITFORIT_HOST $WAITFORIT_PORT
|
||||
WAITFORIT_result=$?
|
||||
else
|
||||
(echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
|
||||
WAITFORIT_result=$?
|
||||
fi
|
||||
if [[ $WAITFORIT_result -eq 0 ]]; then
|
||||
WAITFORIT_end_ts=$(date +%s)
|
||||
echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
return $WAITFORIT_result
|
||||
}
|
||||
|
||||
wait_for_wrapper()
|
||||
{
|
||||
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
|
||||
if [[ $WAITFORIT_QUIET -eq 1 ]]; then
|
||||
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
|
||||
else
|
||||
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
|
||||
fi
|
||||
WAITFORIT_PID=$!
|
||||
trap "kill -INT -$WAITFORIT_PID" INT
|
||||
wait $WAITFORIT_PID
|
||||
WAITFORIT_RESULT=$?
|
||||
if [[ $WAITFORIT_RESULT -ne 0 ]]; then
|
||||
echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
|
||||
fi
|
||||
return $WAITFORIT_RESULT
|
||||
}
|
||||
|
||||
# process arguments
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
case "$1" in
|
||||
*:* )
|
||||
WAITFORIT_hostport=(${1//:/ })
|
||||
WAITFORIT_HOST=${WAITFORIT_hostport[0]}
|
||||
WAITFORIT_PORT=${WAITFORIT_hostport[1]}
|
||||
shift 1
|
||||
;;
|
||||
--child)
|
||||
WAITFORIT_CHILD=1
|
||||
shift 1
|
||||
;;
|
||||
-q | --quiet)
|
||||
WAITFORIT_QUIET=1
|
||||
shift 1
|
||||
;;
|
||||
-s | --strict)
|
||||
WAITFORIT_STRICT=1
|
||||
shift 1
|
||||
;;
|
||||
-h)
|
||||
WAITFORIT_HOST="$2"
|
||||
if [[ $WAITFORIT_HOST == "" ]]; then break; fi
|
||||
shift 2
|
||||
;;
|
||||
--host=*)
|
||||
WAITFORIT_HOST="${1#*=}"
|
||||
shift 1
|
||||
;;
|
||||
-p)
|
||||
WAITFORIT_PORT="$2"
|
||||
if [[ $WAITFORIT_PORT == "" ]]; then break; fi
|
||||
shift 2
|
||||
;;
|
||||
--port=*)
|
||||
WAITFORIT_PORT="${1#*=}"
|
||||
shift 1
|
||||
;;
|
||||
-t)
|
||||
WAITFORIT_TIMEOUT="$2"
|
||||
if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
|
||||
shift 2
|
||||
;;
|
||||
--timeout=*)
|
||||
WAITFORIT_TIMEOUT="${1#*=}"
|
||||
shift 1
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
WAITFORIT_CLI=("$@")
|
||||
break
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echoerr "Unknown argument: $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
|
||||
echoerr "Error: you need to provide a host and port to test."
|
||||
usage
|
||||
fi
|
||||
|
||||
WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
|
||||
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
|
||||
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
|
||||
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
|
||||
|
||||
# Check to see if timeout is from busybox?
|
||||
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
|
||||
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
|
||||
|
||||
WAITFORIT_BUSYTIMEFLAG=""
|
||||
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
|
||||
WAITFORIT_ISBUSY=1
|
||||
# Check if busybox timeout uses -t flag
|
||||
# (recent Alpine versions don't support -t anymore)
|
||||
if timeout &>/dev/stdout | grep -q -e '-t '; then
|
||||
WAITFORIT_BUSYTIMEFLAG="-t"
|
||||
fi
|
||||
else
|
||||
WAITFORIT_ISBUSY=0
|
||||
fi
|
||||
|
||||
if [[ $WAITFORIT_CHILD -gt 0 ]]; then
|
||||
wait_for
|
||||
WAITFORIT_RESULT=$?
|
||||
exit $WAITFORIT_RESULT
|
||||
else
|
||||
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
|
||||
wait_for_wrapper
|
||||
WAITFORIT_RESULT=$?
|
||||
else
|
||||
wait_for
|
||||
WAITFORIT_RESULT=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $WAITFORIT_CLI != "" ]]; then
|
||||
if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
|
||||
echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
|
||||
exit $WAITFORIT_RESULT
|
||||
fi
|
||||
exec "${WAITFORIT_CLI[@]}"
|
||||
else
|
||||
exit $WAITFORIT_RESULT
|
||||
fi
|
||||
@@ -0,0 +1,277 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:America/Denver
|
||||
X-LIC-LOCATION:America/Denver
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:-0700
|
||||
TZOFFSETTO:-0600
|
||||
TZNAME:MDT
|
||||
DTSTART:19700308T020000
|
||||
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:-0600
|
||||
TZOFFSETTO:-0700
|
||||
TZNAME:MST
|
||||
DTSTART:19701101T020000
|
||||
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T215905Z
|
||||
LAST-MODIFIED:20120117T215919Z
|
||||
DTSTAMP:20120117T215919Z
|
||||
UID:58f2f5bb-3be9-43d5-998b-088c593dbc85
|
||||
SUMMARY:New Year's Day
|
||||
DTSTART;TZID=America/Denver:20120101T081500
|
||||
DTEND;TZID=America/Denver:20120101T090500
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T215933Z
|
||||
LAST-MODIFIED:20120117T215941Z
|
||||
DTSTAMP:20120117T215941Z
|
||||
UID:7fdfe99e-3485-4af4-8949-2b0c8c829d17
|
||||
SUMMARY:Epiphany
|
||||
DTSTART;VALUE=DATE:20120106
|
||||
DTEND;VALUE=DATE:20120107
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T215959Z
|
||||
LAST-MODIFIED:20120117T220007Z
|
||||
DTSTAMP:20120117T220007Z
|
||||
UID:112cd226-2600-48d3-b5e8-55ab497c7a10
|
||||
SUMMARY:Groundhog Day
|
||||
DTSTART;VALUE=DATE:20120202
|
||||
DTEND;VALUE=DATE:20120203
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220032Z
|
||||
LAST-MODIFIED:20120117T220040Z
|
||||
DTSTAMP:20120117T220040Z
|
||||
UID:42daa2c5-42e0-4cbc-a614-1284b2f3378b
|
||||
SUMMARY:Valentine's Day
|
||||
DTSTART;VALUE=DATE:20120214
|
||||
DTEND;VALUE=DATE:20120215
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220145Z
|
||||
LAST-MODIFIED:20120117T220156Z
|
||||
DTSTAMP:20120117T220156Z
|
||||
UID:d35c66b7-cc8f-4fa8-b980-eae06732a679
|
||||
SUMMARY:St. Patrick's Day
|
||||
DTSTART;VALUE=DATE:20120317
|
||||
DTEND;VALUE=DATE:20120318
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220208Z
|
||||
LAST-MODIFIED:20120117T220215Z
|
||||
DTSTAMP:20120117T220215Z
|
||||
UID:15440c4b-f459-4599-9d92-5906555eb6d1
|
||||
SUMMARY:Good Friday
|
||||
DTSTART;VALUE=DATE:20120406
|
||||
DTEND;VALUE=DATE:20120407
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220225Z
|
||||
LAST-MODIFIED:20120117T220232Z
|
||||
DTSTAMP:20120117T220232Z
|
||||
UID:00c93d5e-4c1a-45b0-a4f5-3a60b4ba865b
|
||||
SUMMARY:Easter Sunday
|
||||
DTSTART;VALUE=DATE:20120408
|
||||
DTEND;VALUE=DATE:20120409
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220244Z
|
||||
LAST-MODIFIED:20120117T220251Z
|
||||
DTSTAMP:20120117T220251Z
|
||||
UID:8443f87e-ccc7-4bbc-bdc2-8244f459638f
|
||||
SUMMARY:Vimy Ridge Day
|
||||
DTSTART;VALUE=DATE:20120409
|
||||
DTEND;VALUE=DATE:20120410
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220301Z
|
||||
LAST-MODIFIED:20120117T220307Z
|
||||
DTSTAMP:20120117T220307Z
|
||||
UID:3b2035b3-1b4c-4f08-8a38-5ab3d57ef4bb
|
||||
SUMMARY:Easter Monday
|
||||
DTSTART;VALUE=DATE:20120409
|
||||
DTEND;VALUE=DATE:20120410
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220407Z
|
||||
LAST-MODIFIED:20120117T220411Z
|
||||
DTSTAMP:20120117T220411Z
|
||||
UID:dc725c0c-587a-4f7d-997f-521478528973
|
||||
SUMMARY:Canada Day
|
||||
DTSTART;VALUE=DATE:20120701
|
||||
DTEND;VALUE=DATE:20120702
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220615Z
|
||||
LAST-MODIFIED:20120117T220622Z
|
||||
DTSTAMP:20120117T220622Z
|
||||
UID:5663c847-3a29-492b-aeab-1d009852ad67
|
||||
SUMMARY:Halloween
|
||||
DTSTART;VALUE=DATE:20121031
|
||||
DTEND;VALUE=DATE:20121101
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220859Z
|
||||
LAST-MODIFIED:20120117T220905Z
|
||||
DTSTAMP:20120117T220905Z
|
||||
UID:ab6e8de1-e4cd-46c2-a031-5e030d7d56d2
|
||||
SUMMARY:Christmas Eve
|
||||
DTSTART;VALUE=DATE:20121224
|
||||
DTEND;VALUE=DATE:20121225
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220911Z
|
||||
LAST-MODIFIED:20120117T220917Z
|
||||
DTSTAMP:20120117T220917Z
|
||||
UID:6a7de4c3-bb70-477c-91b8-5d780c19d4ca
|
||||
SUMMARY:Christmas
|
||||
DTSTART;VALUE=DATE:20121225
|
||||
DTEND;VALUE=DATE:20121226
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220920Z
|
||||
LAST-MODIFIED:20120117T220943Z
|
||||
DTSTAMP:20120117T220943Z
|
||||
UID:4913c6ab-5332-4094-811a-f6060616fda3
|
||||
SUMMARY:Boxing Day (Most regions)
|
||||
DTSTART;VALUE=DATE:20121226
|
||||
DTEND;VALUE=DATE:20121227
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220956Z
|
||||
LAST-MODIFIED:20120117T221003Z
|
||||
DTSTAMP:20120117T221003Z
|
||||
UID:43bc10df-bd29-4f2b-aa0f-c6cc8cf3839f
|
||||
SUMMARY:New Year's Eve
|
||||
DTSTART;VALUE=DATE:20121231
|
||||
DTEND;VALUE=DATE:20130101
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T221726Z
|
||||
LAST-MODIFIED:20120117T221734Z
|
||||
DTSTAMP:20120117T221734Z
|
||||
UID:a51d1d54-72c6-4fe7-8080-bb78b3ab0c09
|
||||
SUMMARY:New Year's Day
|
||||
DTSTART;VALUE=DATE:20120101
|
||||
DTEND;VALUE=DATE:20120102
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220055Z
|
||||
LAST-MODIFIED:20120117T221850Z
|
||||
DTSTAMP:20120117T221850Z
|
||||
UID:31cc39a6-9b41-4a5f-9fd2-f5c43ad686fb
|
||||
SUMMARY:Family Day (AB\, ON\, SK)
|
||||
DTSTART;VALUE=DATE:20120220
|
||||
DTEND;VALUE=DATE:20120221
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220125Z
|
||||
LAST-MODIFIED:20120117T221906Z
|
||||
DTSTAMP:20120117T221906Z
|
||||
UID:4370e2c8-0863-456c-b1d0-e64ec8a06ef1
|
||||
SUMMARY:Commonwealth Day
|
||||
DTSTART;VALUE=DATE:20120312
|
||||
DTEND;VALUE=DATE:20120313
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220321Z
|
||||
LAST-MODIFIED:20120117T221951Z
|
||||
DTSTAMP:20120117T221951Z
|
||||
UID:722dc2f8-c0c8-457b-902c-30e94aee23bb
|
||||
SUMMARY:Victoria Day (Most regions)
|
||||
DTSTART;VALUE=DATE:20120521
|
||||
DTEND;VALUE=DATE:20120522
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220346Z
|
||||
LAST-MODIFIED:20120117T222004Z
|
||||
DTSTAMP:20120117T222004Z
|
||||
UID:f782fb4f-7d96-425c-ae58-839d0f8a9283
|
||||
SUMMARY:National Aboriginal Day
|
||||
DTSTART;VALUE=DATE:20120621
|
||||
DTEND;VALUE=DATE:20120622
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220419Z
|
||||
LAST-MODIFIED:20120117T222100Z
|
||||
DTSTAMP:20120117T222100Z
|
||||
UID:e9a8bda0-2fa1-4cdf-94e1-5eb5467869ff
|
||||
SUMMARY:Canada Day - statutory holiday
|
||||
DTSTART;VALUE=DATE:20120702
|
||||
DTEND;VALUE=DATE:20120703
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220450Z
|
||||
LAST-MODIFIED:20120117T222123Z
|
||||
DTSTAMP:20120117T222123Z
|
||||
UID:074e1958-329b-45b8-b918-3d6807c9deb4
|
||||
SUMMARY:Civic/Provincial Day (Many regions)
|
||||
DTSTART;VALUE=DATE:20120806
|
||||
DTEND;VALUE=DATE:20120807
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220527Z
|
||||
LAST-MODIFIED:20120117T222135Z
|
||||
DTSTAMP:20120117T222135Z
|
||||
UID:e2bc628c-79da-4f09-bfbb-b1d1ea9ca79f
|
||||
SUMMARY:Labour Day (All)
|
||||
DTSTART;VALUE=DATE:20120903
|
||||
DTEND;VALUE=DATE:20120904
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220553Z
|
||||
LAST-MODIFIED:20120117T222145Z
|
||||
DTSTAMP:20120117T222145Z
|
||||
UID:ed33f08c-fcb1-44ee-9173-1bd7051093fb
|
||||
SUMMARY:Thanksgiving Day (All)
|
||||
DTSTART;VALUE=DATE:20121008
|
||||
DTEND;VALUE=DATE:20121009
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220635Z
|
||||
LAST-MODIFIED:20120117T222159Z
|
||||
DTSTAMP:20120117T222159Z
|
||||
UID:27bff647-69a4-4be8-9647-6dd1ad3b4d60
|
||||
SUMMARY:Remembrance Day (Most regions)
|
||||
DTSTART;VALUE=DATE:20121111
|
||||
DTEND;VALUE=DATE:20121112
|
||||
X-MOZ-GENERATION:2
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220727Z
|
||||
LAST-MODIFIED:20120117T222205Z
|
||||
DTSTAMP:20120117T222205Z
|
||||
UID:487d87a7-618f-4f07-bba2-9f472be8c836
|
||||
SUMMARY:Remembrance Day observed (Many regions)
|
||||
DTSTART;VALUE=DATE:20121112
|
||||
DTEND;VALUE=DATE:20121113
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20120117T220816Z
|
||||
LAST-MODIFIED:20120117T222220Z
|
||||
DTSTAMP:20120117T222220Z
|
||||
UID:696de927-4e80-4d07-a283-550f51ec324e
|
||||
SUMMARY:Anniversary of the Statute of Westminster
|
||||
DTSTART;VALUE=DATE:20121211
|
||||
DTEND;VALUE=DATE:20121212
|
||||
X-MOZ-GENERATION:1
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM httpd:2.4
|
||||
|
||||
ADD httpd.conf /usr/local/apache2/conf/httpd.conf
|
||||
@@ -0,0 +1,772 @@
|
||||
#
|
||||
# This is the main Apache HTTP server configuration file. It contains the
|
||||
# configuration directives that give the server its instructions.
|
||||
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
|
||||
# In particular, see
|
||||
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
|
||||
# for a discussion of each configuration directive.
|
||||
#
|
||||
# Do NOT simply read the instructions in here without understanding
|
||||
# what they do. They're here only as hints or reminders. If you are unsure
|
||||
# consult the online docs. You have been warned.
|
||||
#
|
||||
# Configuration and logfile names: If the filenames you specify for many
|
||||
# of the server's control files begin with "/" (or "drive:/" for Win32), the
|
||||
# server will use that explicit path. If the filenames do *not* begin
|
||||
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
|
||||
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
|
||||
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
|
||||
# will be interpreted as '/logs/access_log'.
|
||||
|
||||
#
|
||||
# ServerRoot: The top of the directory tree under which the server's
|
||||
# configuration, error, and log files are kept.
|
||||
#
|
||||
# Do not add a slash at the end of the directory path. If you point
|
||||
# ServerRoot at a non-local disk, be sure to specify a local disk on the
|
||||
# Mutex directive, if file-based mutexes are used. If you wish to share the
|
||||
# same ServerRoot for multiple httpd daemons, you will need to change at
|
||||
# least PidFile.
|
||||
#
|
||||
ServerRoot "/usr/local/apache2"
|
||||
|
||||
#
|
||||
# Mutex: Allows you to set the mutex mechanism and mutex file directory
|
||||
# for individual mutexes, or change the global defaults
|
||||
#
|
||||
# Uncomment and change the directory if mutexes are file-based and the default
|
||||
# mutex file directory is not on a local disk or is not appropriate for some
|
||||
# other reason.
|
||||
#
|
||||
# Mutex default:logs
|
||||
|
||||
#
|
||||
# Listen: Allows you to bind Apache to specific IP addresses and/or
|
||||
# ports, instead of the default. See also the <VirtualHost>
|
||||
# directive.
|
||||
#
|
||||
# Change this to Listen on specific IP addresses as shown below to
|
||||
# prevent Apache from glomming onto all bound IP addresses.
|
||||
#
|
||||
#Listen 12.34.56.78:80
|
||||
Listen 80
|
||||
Listen 81
|
||||
Listen 443
|
||||
|
||||
#
|
||||
# Dynamic Shared Object (DSO) Support
|
||||
#
|
||||
# To be able to use the functionality of a module which was built as a DSO you
|
||||
# have to place corresponding `LoadModule' lines at this location so the
|
||||
# directives contained in it are actually available _before_ they are used.
|
||||
# Statically compiled modules (those listed by `httpd -l') do not need
|
||||
# to be loaded here.
|
||||
#
|
||||
# Example:
|
||||
# LoadModule foo_module modules/mod_foo.so
|
||||
#
|
||||
LoadModule mpm_event_module modules/mod_mpm_event.so
|
||||
#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
|
||||
#LoadModule mpm_worker_module modules/mod_mpm_worker.so
|
||||
LoadModule authn_file_module modules/mod_authn_file.so
|
||||
#LoadModule authn_dbm_module modules/mod_authn_dbm.so
|
||||
#LoadModule authn_anon_module modules/mod_authn_anon.so
|
||||
#LoadModule authn_dbd_module modules/mod_authn_dbd.so
|
||||
#LoadModule authn_socache_module modules/mod_authn_socache.so
|
||||
LoadModule authn_core_module modules/mod_authn_core.so
|
||||
LoadModule authz_host_module modules/mod_authz_host.so
|
||||
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
|
||||
LoadModule authz_user_module modules/mod_authz_user.so
|
||||
#LoadModule authz_dbm_module modules/mod_authz_dbm.so
|
||||
#LoadModule authz_owner_module modules/mod_authz_owner.so
|
||||
#LoadModule authz_dbd_module modules/mod_authz_dbd.so
|
||||
LoadModule authz_core_module modules/mod_authz_core.so
|
||||
#LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
|
||||
#LoadModule authnz_fcgi_module modules/mod_authnz_fcgi.so
|
||||
LoadModule access_compat_module modules/mod_access_compat.so
|
||||
LoadModule auth_basic_module modules/mod_auth_basic.so
|
||||
#LoadModule auth_form_module modules/mod_auth_form.so
|
||||
#LoadModule auth_digest_module modules/mod_auth_digest.so
|
||||
#LoadModule allowmethods_module modules/mod_allowmethods.so
|
||||
#LoadModule isapi_module modules/mod_isapi.so
|
||||
#LoadModule file_cache_module modules/mod_file_cache.so
|
||||
#LoadModule cache_module modules/mod_cache.so
|
||||
#LoadModule cache_disk_module modules/mod_cache_disk.so
|
||||
#LoadModule cache_socache_module modules/mod_cache_socache.so
|
||||
#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
|
||||
#LoadModule socache_dbm_module modules/mod_socache_dbm.so
|
||||
#LoadModule socache_memcache_module modules/mod_socache_memcache.so
|
||||
#LoadModule socache_redis_module modules/mod_socache_redis.so
|
||||
#LoadModule watchdog_module modules/mod_watchdog.so
|
||||
#LoadModule macro_module modules/mod_macro.so
|
||||
#LoadModule dbd_module modules/mod_dbd.so
|
||||
#LoadModule bucketeer_module modules/mod_bucketeer.so
|
||||
#LoadModule dumpio_module modules/mod_dumpio.so
|
||||
#LoadModule echo_module modules/mod_echo.so
|
||||
#LoadModule example_hooks_module modules/mod_example_hooks.so
|
||||
#LoadModule case_filter_module modules/mod_case_filter.so
|
||||
#LoadModule case_filter_in_module modules/mod_case_filter_in.so
|
||||
#LoadModule example_ipc_module modules/mod_example_ipc.so
|
||||
#LoadModule buffer_module modules/mod_buffer.so
|
||||
#LoadModule data_module modules/mod_data.so
|
||||
#LoadModule ratelimit_module modules/mod_ratelimit.so
|
||||
LoadModule reqtimeout_module modules/mod_reqtimeout.so
|
||||
#LoadModule ext_filter_module modules/mod_ext_filter.so
|
||||
#LoadModule request_module modules/mod_request.so
|
||||
#LoadModule include_module modules/mod_include.so
|
||||
LoadModule filter_module modules/mod_filter.so
|
||||
#LoadModule reflector_module modules/mod_reflector.so
|
||||
#LoadModule substitute_module modules/mod_substitute.so
|
||||
#LoadModule sed_module modules/mod_sed.so
|
||||
#LoadModule charset_lite_module modules/mod_charset_lite.so
|
||||
#LoadModule deflate_module modules/mod_deflate.so
|
||||
#LoadModule xml2enc_module modules/mod_xml2enc.so
|
||||
#LoadModule proxy_html_module modules/mod_proxy_html.so
|
||||
#LoadModule brotli_module modules/mod_brotli.so
|
||||
LoadModule mime_module modules/mod_mime.so
|
||||
#LoadModule ldap_module modules/mod_ldap.so
|
||||
LoadModule log_config_module modules/mod_log_config.so
|
||||
#LoadModule log_debug_module modules/mod_log_debug.so
|
||||
#LoadModule log_forensic_module modules/mod_log_forensic.so
|
||||
#LoadModule logio_module modules/mod_logio.so
|
||||
#LoadModule lua_module modules/mod_lua.so
|
||||
LoadModule env_module modules/mod_env.so
|
||||
#LoadModule mime_magic_module modules/mod_mime_magic.so
|
||||
#LoadModule cern_meta_module modules/mod_cern_meta.so
|
||||
#LoadModule expires_module modules/mod_expires.so
|
||||
LoadModule headers_module modules/mod_headers.so
|
||||
#LoadModule ident_module modules/mod_ident.so
|
||||
#LoadModule usertrack_module modules/mod_usertrack.so
|
||||
#LoadModule unique_id_module modules/mod_unique_id.so
|
||||
LoadModule setenvif_module modules/mod_setenvif.so
|
||||
LoadModule version_module modules/mod_version.so
|
||||
#LoadModule remoteip_module modules/mod_remoteip.so
|
||||
LoadModule proxy_module modules/mod_proxy.so
|
||||
#LoadModule proxy_connect_module modules/mod_proxy_connect.so
|
||||
#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
|
||||
LoadModule proxy_http_module modules/mod_proxy_http.so
|
||||
#LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
|
||||
#LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
|
||||
#LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
|
||||
#LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so
|
||||
#LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
|
||||
#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
|
||||
#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
|
||||
#LoadModule proxy_express_module modules/mod_proxy_express.so
|
||||
#LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so
|
||||
#LoadModule session_module modules/mod_session.so
|
||||
#LoadModule session_cookie_module modules/mod_session_cookie.so
|
||||
#LoadModule session_crypto_module modules/mod_session_crypto.so
|
||||
#LoadModule session_dbd_module modules/mod_session_dbd.so
|
||||
#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
|
||||
#LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
|
||||
LoadModule ssl_module modules/mod_ssl.so
|
||||
#LoadModule optional_hook_export_module modules/mod_optional_hook_export.so
|
||||
#LoadModule optional_hook_import_module modules/mod_optional_hook_import.so
|
||||
#LoadModule optional_fn_import_module modules/mod_optional_fn_import.so
|
||||
#LoadModule optional_fn_export_module modules/mod_optional_fn_export.so
|
||||
#LoadModule dialup_module modules/mod_dialup.so
|
||||
#LoadModule http2_module modules/mod_http2.so
|
||||
#LoadModule proxy_http2_module modules/mod_proxy_http2.so
|
||||
#LoadModule md_module modules/mod_md.so
|
||||
#LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
|
||||
#LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
|
||||
#LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
|
||||
#LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
|
||||
LoadModule unixd_module modules/mod_unixd.so
|
||||
#LoadModule heartbeat_module modules/mod_heartbeat.so
|
||||
#LoadModule heartmonitor_module modules/mod_heartmonitor.so
|
||||
#LoadModule dav_module modules/mod_dav.so
|
||||
LoadModule status_module modules/mod_status.so
|
||||
LoadModule autoindex_module modules/mod_autoindex.so
|
||||
#LoadModule asis_module modules/mod_asis.so
|
||||
#LoadModule info_module modules/mod_info.so
|
||||
#LoadModule suexec_module modules/mod_suexec.so
|
||||
<IfModule !mpm_prefork_module>
|
||||
#LoadModule cgid_module modules/mod_cgid.so
|
||||
</IfModule>
|
||||
<IfModule mpm_prefork_module>
|
||||
#LoadModule cgi_module modules/mod_cgi.so
|
||||
</IfModule>
|
||||
#LoadModule dav_fs_module modules/mod_dav_fs.so
|
||||
#LoadModule dav_lock_module modules/mod_dav_lock.so
|
||||
#LoadModule vhost_alias_module modules/mod_vhost_alias.so
|
||||
#LoadModule negotiation_module modules/mod_negotiation.so
|
||||
LoadModule dir_module modules/mod_dir.so
|
||||
#LoadModule imagemap_module modules/mod_imagemap.so
|
||||
#LoadModule actions_module modules/mod_actions.so
|
||||
#LoadModule speling_module modules/mod_speling.so
|
||||
#LoadModule userdir_module modules/mod_userdir.so
|
||||
LoadModule alias_module modules/mod_alias.so
|
||||
LoadModule rewrite_module modules/mod_rewrite.so
|
||||
|
||||
<IfModule unixd_module>
|
||||
#
|
||||
# If you wish httpd to run as a different user or group, you must run
|
||||
# httpd as root initially and it will switch.
|
||||
#
|
||||
# User/Group: The name (or #number) of the user/group to run httpd as.
|
||||
# It is usually good practice to create a dedicated user and group for
|
||||
# running httpd, as with most system services.
|
||||
#
|
||||
User www-data
|
||||
Group www-data
|
||||
|
||||
</IfModule>
|
||||
|
||||
# 'Main' server configuration
|
||||
#
|
||||
# The directives in this section set up the values used by the 'main'
|
||||
# server, which responds to any requests that aren't handled by a
|
||||
# <VirtualHost> definition. These values also provide defaults for
|
||||
# any <VirtualHost> containers you may define later in the file.
|
||||
#
|
||||
# All of these directives may appear inside <VirtualHost> containers,
|
||||
# in which case these default settings will be overridden for the
|
||||
# virtual host being defined.
|
||||
#
|
||||
|
||||
#
|
||||
# ServerAdmin: Your address, where problems with the server should be
|
||||
# e-mailed. This address appears on some server-generated pages, such
|
||||
# as error documents. e.g. admin@your-domain.com
|
||||
#
|
||||
ServerAdmin you@example.com
|
||||
|
||||
#
|
||||
# ServerName gives the name and port that the server uses to identify itself.
|
||||
# This can often be determined automatically, but we recommend you specify
|
||||
# it explicitly to prevent problems during startup.
|
||||
#
|
||||
# If your host doesn't have a registered DNS name, enter its IP address here.
|
||||
#
|
||||
#ServerName www.example.com:80
|
||||
|
||||
#
|
||||
# Deny access to the entirety of your server's filesystem. You must
|
||||
# explicitly permit access to web content directories in other
|
||||
# <Directory> blocks below.
|
||||
#
|
||||
<Directory />
|
||||
AllowOverride none
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
#
|
||||
# Note that from this point forward you must specifically allow
|
||||
# particular features to be enabled - so if something's not working as
|
||||
# you might expect, make sure that you have specifically enabled it
|
||||
# below.
|
||||
#
|
||||
|
||||
#
|
||||
# DocumentRoot: The directory out of which you will serve your
|
||||
# documents. By default, all requests are taken from this directory, but
|
||||
# symbolic links and aliases may be used to point to other locations.
|
||||
#
|
||||
DocumentRoot "/usr/local/apache2/htdocs"
|
||||
<Directory "/usr/local/apache2/htdocs">
|
||||
#
|
||||
# Possible values for the Options directive are "None", "All",
|
||||
# or any combination of:
|
||||
# Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
|
||||
#
|
||||
# Note that "MultiViews" must be named *explicitly* --- "Options All"
|
||||
# doesn't give it to you.
|
||||
#
|
||||
# The Options directive is both complicated and important. Please see
|
||||
# http://httpd.apache.org/docs/2.4/mod/core.html#options
|
||||
# for more information.
|
||||
#
|
||||
Options Indexes FollowSymLinks
|
||||
|
||||
#
|
||||
# AllowOverride controls what directives may be placed in .htaccess files.
|
||||
# It can be "All", "None", or any combination of the keywords:
|
||||
# AllowOverride FileInfo AuthConfig Limit
|
||||
#
|
||||
AllowOverride None
|
||||
|
||||
#
|
||||
# Controls who can get stuff from this server.
|
||||
#
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
#
|
||||
# DirectoryIndex: sets the file that Apache will serve if a directory
|
||||
# is requested.
|
||||
#
|
||||
<IfModule dir_module>
|
||||
DirectoryIndex index.html
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# The following lines prevent .htaccess and .htpasswd files from being
|
||||
# viewed by Web clients.
|
||||
#
|
||||
<Files ".ht*">
|
||||
Require all denied
|
||||
</Files>
|
||||
|
||||
#
|
||||
# ErrorLog: The location of the error log file.
|
||||
# If you do not specify an ErrorLog directive within a <VirtualHost>
|
||||
# container, error messages relating to that virtual host will be
|
||||
# logged here. If you *do* define an error logfile for a <VirtualHost>
|
||||
# container, that host's errors will be logged there and not here.
|
||||
#
|
||||
ErrorLog /proc/self/fd/2
|
||||
|
||||
#
|
||||
# LogLevel: Control the number of messages logged to the error_log.
|
||||
# Possible values include: debug, info, notice, warn, error, crit,
|
||||
# alert, emerg.
|
||||
#
|
||||
LogLevel warn
|
||||
|
||||
<IfModule log_config_module>
|
||||
#
|
||||
# The following directives define some format nicknames for use with
|
||||
# a CustomLog directive (see below).
|
||||
#
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
||||
|
||||
<IfModule logio_module>
|
||||
# You need to enable mod_logio.c to use %I and %O
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# The location and format of the access logfile (Common Logfile Format).
|
||||
# If you do not define any access logfiles within a <VirtualHost>
|
||||
# container, they will be logged here. Contrariwise, if you *do*
|
||||
# define per-<VirtualHost> access logfiles, transactions will be
|
||||
# logged therein and *not* in this file.
|
||||
#
|
||||
CustomLog /proc/self/fd/1 common
|
||||
|
||||
#
|
||||
# If you prefer a logfile with access, agent, and referer information
|
||||
# (Combined Logfile Format) you can use the following directive.
|
||||
#
|
||||
#CustomLog "logs/access_log" combined
|
||||
</IfModule>
|
||||
|
||||
<IfModule alias_module>
|
||||
#
|
||||
# Redirect: Allows you to tell clients about documents that used to
|
||||
# exist in your server's namespace, but do not anymore. The client
|
||||
# will make a new request for the document at its new location.
|
||||
# Example:
|
||||
# Redirect permanent /foo http://www.example.com/bar
|
||||
|
||||
#
|
||||
# Alias: Maps web paths into filesystem paths and is used to
|
||||
# access content that does not live under the DocumentRoot.
|
||||
# Example:
|
||||
# Alias /webpath /full/filesystem/path
|
||||
#
|
||||
# If you include a trailing / on /webpath then the server will
|
||||
# require it to be present in the URL. You will also likely
|
||||
# need to provide a <Directory> section to allow access to
|
||||
# the filesystem path.
|
||||
|
||||
#
|
||||
# ScriptAlias: This controls which directories contain server scripts.
|
||||
# ScriptAliases are essentially the same as Aliases, except that
|
||||
# documents in the target directory are treated as applications and
|
||||
# run by the server when requested rather than as documents sent to the
|
||||
# client. The same rules about trailing "/" apply to ScriptAlias
|
||||
# directives as to Alias.
|
||||
#
|
||||
ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule cgid_module>
|
||||
#
|
||||
# ScriptSock: On threaded servers, designate the path to the UNIX
|
||||
# socket used to communicate with the CGI daemon of mod_cgid.
|
||||
#
|
||||
#Scriptsock cgisock
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# "/usr/local/apache2/cgi-bin" should be changed to whatever your ScriptAliased
|
||||
# CGI directory exists, if you have that configured.
|
||||
#
|
||||
<Directory "/usr/local/apache2/cgi-bin">
|
||||
AllowOverride None
|
||||
Options None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule headers_module>
|
||||
#
|
||||
# Avoid passing HTTP_PROXY environment to CGI's on this or any proxied
|
||||
# backend servers which have lingering "httpoxy" defects.
|
||||
# 'Proxy' request header is undefined by the IETF, not listed by IANA
|
||||
#
|
||||
RequestHeader unset Proxy early
|
||||
</IfModule>
|
||||
|
||||
<IfModule mime_module>
|
||||
#
|
||||
# TypesConfig points to the file containing the list of mappings from
|
||||
# filename extension to MIME-type.
|
||||
#
|
||||
TypesConfig conf/mime.types
|
||||
|
||||
#
|
||||
# AddType allows you to add to or override the MIME configuration
|
||||
# file specified in TypesConfig for specific file types.
|
||||
#
|
||||
#AddType application/x-gzip .tgz
|
||||
#
|
||||
# AddEncoding allows you to have certain browsers uncompress
|
||||
# information on the fly. Note: Not all browsers support this.
|
||||
#
|
||||
#AddEncoding x-compress .Z
|
||||
#AddEncoding x-gzip .gz .tgz
|
||||
#
|
||||
# If the AddEncoding directives above are commented-out, then you
|
||||
# probably should define those extensions to indicate media types:
|
||||
#
|
||||
AddType application/x-compress .Z
|
||||
AddType application/x-gzip .gz .tgz
|
||||
|
||||
#
|
||||
# AddHandler allows you to map certain file extensions to "handlers":
|
||||
# actions unrelated to filetype. These can be either built into the server
|
||||
# or added with the Action directive (see below)
|
||||
#
|
||||
# To use CGI scripts outside of ScriptAliased directories:
|
||||
# (You will also need to add "ExecCGI" to the "Options" directive.)
|
||||
#
|
||||
#AddHandler cgi-script .cgi
|
||||
|
||||
# For type maps (negotiated resources):
|
||||
#AddHandler type-map var
|
||||
|
||||
#
|
||||
# Filters allow you to process content before it is sent to the client.
|
||||
#
|
||||
# To parse .shtml files for server-side includes (SSI):
|
||||
# (You will also need to add "Includes" to the "Options" directive.)
|
||||
#
|
||||
#AddType text/html .shtml
|
||||
#AddOutputFilter INCLUDES .shtml
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# The mod_mime_magic module allows the server to use various hints from the
|
||||
# contents of the file itself to determine its type. The MIMEMagicFile
|
||||
# directive tells the module where the hint definitions are located.
|
||||
#
|
||||
#MIMEMagicFile conf/magic
|
||||
|
||||
#
|
||||
# Customizable error responses come in three flavors:
|
||||
# 1) plain text 2) local redirects 3) external redirects
|
||||
#
|
||||
# Some examples:
|
||||
#ErrorDocument 500 "The server made a boo boo."
|
||||
#ErrorDocument 404 /missing.html
|
||||
#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
|
||||
#ErrorDocument 402 http://www.example.com/subscription_info.html
|
||||
#
|
||||
|
||||
#
|
||||
# MaxRanges: Maximum number of Ranges in a request before
|
||||
# returning the entire resource, or one of the special
|
||||
# values 'default', 'none' or 'unlimited'.
|
||||
# Default setting is to accept 200 Ranges.
|
||||
#MaxRanges unlimited
|
||||
|
||||
#
|
||||
# EnableMMAP and EnableSendfile: On systems that support it,
|
||||
# memory-mapping or the sendfile syscall may be used to deliver
|
||||
# files. This usually improves server performance, but must
|
||||
# be turned off when serving from networked-mounted
|
||||
# filesystems or if support for these functions is otherwise
|
||||
# broken on your system.
|
||||
# Defaults: EnableMMAP On, EnableSendfile Off
|
||||
#
|
||||
#EnableMMAP off
|
||||
#EnableSendfile on
|
||||
|
||||
# Supplemental configuration
|
||||
#
|
||||
# The configuration files in the conf/extra/ directory can be
|
||||
# included to add extra features or to modify the default configuration of
|
||||
# the server, or you may simply copy their contents here and change as
|
||||
# necessary.
|
||||
|
||||
# Server-pool management (MPM specific)
|
||||
#Include conf/extra/httpd-mpm.conf
|
||||
|
||||
# Multi-language error messages
|
||||
#Include conf/extra/httpd-multilang-errordoc.conf
|
||||
|
||||
# Fancy directory listings
|
||||
#Include conf/extra/httpd-autoindex.conf
|
||||
|
||||
# Language settings
|
||||
#Include conf/extra/httpd-languages.conf
|
||||
|
||||
# User home directories
|
||||
#Include conf/extra/httpd-userdir.conf
|
||||
|
||||
# Real-time info on requests and configuration
|
||||
#Include conf/extra/httpd-info.conf
|
||||
|
||||
# Virtual hosts
|
||||
#Include conf/extra/httpd-vhosts.conf
|
||||
|
||||
# Local access to the Apache HTTP Server Manual
|
||||
#Include conf/extra/httpd-manual.conf
|
||||
|
||||
# Distributed authoring and versioning (WebDAV)
|
||||
#Include conf/extra/httpd-dav.conf
|
||||
|
||||
# Various default settings
|
||||
#Include conf/extra/httpd-default.conf
|
||||
|
||||
# Configure mod_proxy_html to understand HTML4/XHTML1
|
||||
<IfModule proxy_html_module>
|
||||
Include conf/extra/proxy-html.conf
|
||||
</IfModule>
|
||||
|
||||
# Secure (SSL/TLS) connections
|
||||
#Include conf/extra/httpd-ssl.conf
|
||||
#
|
||||
# Note: The following must must be present to support
|
||||
# starting without SSL on platforms with no /dev/random equivalent
|
||||
# but a statically compiled-in mod_ssl.
|
||||
#
|
||||
<IfModule ssl_module>
|
||||
SSLRandomSeed startup builtin
|
||||
SSLRandomSeed connect builtin
|
||||
</IfModule>
|
||||
|
||||
|
||||
###########################
|
||||
#
|
||||
# SOGo HTTP
|
||||
#
|
||||
###########################
|
||||
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin support@sogo.nu
|
||||
ServerName dev.sogo
|
||||
|
||||
Alias /SOGo.woa/WebServerResources/ /usr/local/lib/GNUstep/SOGo/WebServerResources/
|
||||
Alias /SOGo/WebServerResources/ /usr/local/lib/GNUstep/SOGo/WebServerResources/
|
||||
|
||||
<Directory /usr/lib/GNUstep/SOGo/>
|
||||
<IfVersion < 2.4>
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</IfVersion>
|
||||
<IfVersion >= 2.4>
|
||||
Require all granted
|
||||
</IfVersion>
|
||||
|
||||
# Explicitly allow caching of static content to avoid browser specific behavior.
|
||||
# A resource's URL MUST change in order to have the client load the new version.
|
||||
<IfModule expires_module>
|
||||
ExpiresActive On
|
||||
ExpiresDefault "access plus 1 year"
|
||||
</IfModule>
|
||||
</Directory>
|
||||
|
||||
CustomLog /tmp/sogo.log combined
|
||||
|
||||
ProxyRequests Off
|
||||
SetEnv proxy-nokeepalive 1
|
||||
ProxyPreserveHost On
|
||||
ProxyPass /SOGo http://sogo:50000/SOGo retry=0
|
||||
ProxyPass /Microsoft-Server-ActiveSync http://sogo:50000/SOGo/Microsoft-Server-ActiveSync retry=0 connectiontimeout=5 timeout=3600
|
||||
ProxyPass /keycloak http://keycloak:8080/keycloak retry=0
|
||||
ProxyPassReverse /keycloak http://keycloak:8080/keycloak
|
||||
|
||||
<Proxy http://sogo:50000/SOGo>
|
||||
## adjust the following to your configuration
|
||||
RequestHeader set "x-webobjects-server-port" "80"
|
||||
RequestHeader set "x-webobjects-server-name" "127.0.0.1"
|
||||
RequestHeader set "x-webobjects-server-url" "http://127.0.0.1"
|
||||
RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0"
|
||||
RequestHeader set "x-webobjects-remote-host" "127.0.0.1"
|
||||
|
||||
AddDefaultCharset UTF-8
|
||||
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Proxy>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteRule ^/SOGo/(.*)$ /SOGo/$1 [env=REMOTE_HOST:%{REMOTE_ADDR},PT]
|
||||
|
||||
# For Apple autoconfiguration
|
||||
<IfModule rewrite_module>
|
||||
RewriteEngine On
|
||||
RewriteRule ^/.well-known/caldav/?$ /SOGo/dav [R=301]
|
||||
RewriteRule ^/.well-known/carddav/?$ /SOGo/dav [R=301]
|
||||
</IfModule>
|
||||
|
||||
<Directory /src/SOGo/Tests/results>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
Alias /tests /src/SOGo/Tests/results
|
||||
</VirtualHost>
|
||||
|
||||
###########################
|
||||
#
|
||||
# SOGo HTTP (for tests)
|
||||
#
|
||||
###########################
|
||||
|
||||
<VirtualHost *:81>
|
||||
ServerAdmin support@sogo.nu
|
||||
ServerName dev.sogo
|
||||
|
||||
Alias /SOGo.woa/WebServerResources/ /usr/local/lib/GNUstep/SOGo/WebServerResources/
|
||||
Alias /SOGo/WebServerResources/ /usr/local/lib/GNUstep/SOGo/WebServerResources/
|
||||
|
||||
<Directory /usr/lib/GNUstep/SOGo/>
|
||||
<IfVersion < 2.4>
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</IfVersion>
|
||||
<IfVersion >= 2.4>
|
||||
Require all granted
|
||||
</IfVersion>
|
||||
|
||||
# Explicitly allow caching of static content to avoid browser specific behavior.
|
||||
# A resource's URL MUST change in order to have the client load the new version.
|
||||
<IfModule expires_module>
|
||||
ExpiresActive On
|
||||
ExpiresDefault "access plus 1 year"
|
||||
</IfModule>
|
||||
</Directory>
|
||||
|
||||
CustomLog /tmp/sogo.log combined
|
||||
|
||||
ProxyRequests Off
|
||||
SetEnv proxy-nokeepalive 1
|
||||
ProxyPreserveHost On
|
||||
ProxyPass /SOGo http://sogo:50000/SOGo retry=0
|
||||
ProxyPass /Microsoft-Server-ActiveSync http://sogo:50000/SOGo/Microsoft-Server-ActiveSync retry=0 connectiontimeout=5 timeout=3600
|
||||
|
||||
<Proxy http://sogo:50000/SOGo>
|
||||
## adjust the following to your configuration
|
||||
RequestHeader set "x-webobjects-server-port" "81"
|
||||
RequestHeader set "x-webobjects-server-name" "httpd"
|
||||
RequestHeader set "x-webobjects-server-url" "http://httpd"
|
||||
|
||||
RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0"
|
||||
RequestHeader set "x-webobjects-remote-host" "httpd"
|
||||
|
||||
AddDefaultCharset UTF-8
|
||||
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Proxy>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteRule ^/SOGo/(.*)$ /SOGo/$1 [env=REMOTE_HOST:%{REMOTE_ADDR},PT]
|
||||
|
||||
# For Apple autoconfiguration
|
||||
<IfModule rewrite_module>
|
||||
RewriteEngine On
|
||||
RewriteRule ^/.well-known/caldav/?$ /SOGo/dav [R=301]
|
||||
RewriteRule ^/.well-known/carddav/?$ /SOGo/dav [R=301]
|
||||
</IfModule>
|
||||
</VirtualHost>
|
||||
|
||||
|
||||
###########################
|
||||
#
|
||||
# SOGo HTTPS
|
||||
#
|
||||
###########################
|
||||
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerAdmin support@sogo.nu
|
||||
ServerName dev.sogo
|
||||
|
||||
Alias /SOGo.woa/WebServerResources/ /usr/local/lib/GNUstep/SOGo/WebServerResources/
|
||||
Alias /SOGo/WebServerResources/ /usr/local/lib/GNUstep/SOGo/WebServerResources/
|
||||
|
||||
SSLEngine on
|
||||
SSLCertificateFile /ssl/apache-selfsigned.crt
|
||||
SSLCertificateKeyFile /ssl/apache-selfsigned.key
|
||||
|
||||
<Directory /usr/lib/GNUstep/SOGo/>
|
||||
<IfVersion < 2.4>
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</IfVersion>
|
||||
<IfVersion >= 2.4>
|
||||
Require all granted
|
||||
</IfVersion>
|
||||
|
||||
# Explicitly allow caching of static content to avoid browser specific behavior.
|
||||
# A resource's URL MUST change in order to have the client load the new version.
|
||||
<IfModule expires_module>
|
||||
ExpiresActive On
|
||||
ExpiresDefault "access plus 1 year"
|
||||
</IfModule>
|
||||
</Directory>
|
||||
|
||||
CustomLog /tmp/sogo.log combined
|
||||
|
||||
ProxyRequests Off
|
||||
SetEnv proxy-nokeepalive 1
|
||||
ProxyPreserveHost On
|
||||
ProxyPass /SOGo http://sogo:50000/SOGo retry=0
|
||||
ProxyPass /Microsoft-Server-ActiveSync http://sogo:50000/SOGo/Microsoft-Server-ActiveSync retry=0 connectiontimeout=5 timeout=3600
|
||||
ProxyPass /keycloak http://keycloak:8080/keycloak retry=0
|
||||
ProxyPassReverse /keycloak http://keycloak:8080/keycloak
|
||||
|
||||
<Proxy http://sogo:50000/SOGo>
|
||||
## adjust the following to your configuration
|
||||
RequestHeader set "x-webobjects-server-port" "443"
|
||||
RequestHeader set "x-webobjects-server-name" "127.0.0.1"
|
||||
RequestHeader set "x-webobjects-server-url" "https://127.0.0.1"
|
||||
|
||||
RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0"
|
||||
RequestHeader set "x-webobjects-remote-host" "127.0.0.1"
|
||||
|
||||
AddDefaultCharset UTF-8
|
||||
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Proxy>
|
||||
|
||||
|
||||
RewriteEngine On
|
||||
RewriteRule ^/SOGo/(.*)$ /SOGo/$1 [env=REMOTE_HOST:%{REMOTE_ADDR},PT]
|
||||
|
||||
# For Apple autoconfiguration
|
||||
<IfModule rewrite_module>
|
||||
RewriteEngine On
|
||||
RewriteRule ^/.well-known/caldav/?$ /SOGo/dav [R=301]
|
||||
RewriteRule ^/.well-known/carddav/?$ /SOGo/dav [R=301]
|
||||
</IfModule>
|
||||
|
||||
<Directory /src/SOGo/Tests/results>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
Alias /tests /src/SOGo/Tests/results
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDOTCCAiGgAwIBAgIUCMtp7zGBSm27mjkmvqMww9BtkBowDQYJKoZIhvcNAQEL
|
||||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA3MjcyMjM4MTdaFw0zNDA3
|
||||
MjUyMjM4MTdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQCtpzPeBvnCJJD36iaTClW1dprzqs9yMqEyLHaczOIm
|
||||
VEsUG6ykWx1XpH4adAfVuVkyKgIuVWK57F4wmDq6mpyycfIbhT2r93fWkW5k10vU
|
||||
c1QG6CPiQs01n0H3BqMXfbkYmJ5Re63cDawRT5aofpc2ClSk7odrL2cF0keb84qB
|
||||
4b6M4D2fGEL9dkwzXeKysMqmcur6jsfAv1ojB4lie+q2UR3VIvU2OP8NQckJJ4le
|
||||
O5i20gR5iX71KykgCy4J2OelU18RH3yY9AZd+m6rWokIqqvS3Izz1hTRC7TQUDql
|
||||
RCWCCyLZolWs9T5K2i23lpswWX+bjtkR1NAqPy+W0WJfAgMBAAGjITAfMB0GA1Ud
|
||||
DgQWBBTUzyYg3BCKqzFWJSu/iTNxknyphjANBgkqhkiG9w0BAQsFAAOCAQEAouJq
|
||||
l6vdg5x/nOBRO+opqIetyc0iux/aJb4QxcMgMgH0c756+EhIFtkx6oxMf7PfNqXF
|
||||
xykkjE5qRMWdtYRsR9unYX/aXJG22iHce9rbK7NgQe0UvqfTZcHCLuvjJA+Iyxnk
|
||||
KPUboPoBqE9ByOhX9NnMBkmhJPe/l4pE0h8hXPEFYvTDfAYYFh6+4JAtEqF+E8ZW
|
||||
UBPZLG+nM48TuPte8iWRHZyn70h2dGQ05Wzlov58ftV1AY5BAo7z3b/ZCEz3gTNe
|
||||
+TcN413F1drctxjSXAd5OuqTEUOutaUwdwAbRQpOXuMDabXGQvTXMIRnHG0QLmQo
|
||||
YzVEGaUAWP4MeXrvIQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
|
||||
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAK2nM94G+cIkkPfqJpMKVbV2mvOqz3IyoTIsdpzM
|
||||
4iZUSxQbrKRbHVekfhp0B9W5WTIqAi5VYrnsXjCYOrqanLJx8huFPav3d9aRbmTX
|
||||
S9RzVAboI+JCzTWfQfcGoxd9uRiYnlF7rdwNrBFPlqh+lzYKVKTuh2svZwXSR5vz
|
||||
ioHhvozgPZ8YQv12TDNd4rKwyqZy6vqOx8C/WiMHiWJ76rZRHdUi9TY4/w1ByQkn
|
||||
iV47mLbSBHmJfvUrKSALLgnY56VTXxEffJj0Bl36bqtaiQiqq9LcjPPWFNELtNBQ
|
||||
OqVEJYILItmiVaz1PkraLbeWmzBZf5uO2RHU0Co/L5bRYl8CAwEAAaAAMA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQBgCABl1ViXYbRIMtZR4UA2Cd0jY5kqMq0CePWHDYOI+eUP
|
||||
qcnx+sVNZN5ixDdm3Dv6g7dwu3g8FZ3oC/jS/JhTJbbVubPq21T3nv0ZIOL3rmMp
|
||||
hJsWBBanVgLi3MD+UsF/2IrJYf7mOR2OEVmb4dglUJNgkcbuaTA4nF5nFS/gVZxg
|
||||
id6wvGygNuoGXhWVOC1amr+xYqt6Wv/U2vA8fQ9LWL7L7/eU2ol2VurC8bEAQYRR
|
||||
7O8WOk9j2njDRzE8HHKg7iSSDleMiYtAcs2HIljlTAB7mQolkYpcfyIOE1wFWG14
|
||||
IhS/p2SxHfvUKvJIgauG7DkFeURRtnqEioB2JwyE
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtpzPeBvnCJJD3
|
||||
6iaTClW1dprzqs9yMqEyLHaczOImVEsUG6ykWx1XpH4adAfVuVkyKgIuVWK57F4w
|
||||
mDq6mpyycfIbhT2r93fWkW5k10vUc1QG6CPiQs01n0H3BqMXfbkYmJ5Re63cDawR
|
||||
T5aofpc2ClSk7odrL2cF0keb84qB4b6M4D2fGEL9dkwzXeKysMqmcur6jsfAv1oj
|
||||
B4lie+q2UR3VIvU2OP8NQckJJ4leO5i20gR5iX71KykgCy4J2OelU18RH3yY9AZd
|
||||
+m6rWokIqqvS3Izz1hTRC7TQUDqlRCWCCyLZolWs9T5K2i23lpswWX+bjtkR1NAq
|
||||
Py+W0WJfAgMBAAECggEAETSSfFp56d/FnUqjLR5xBNxjgzur93HyFrk2Z1gaxWwZ
|
||||
+ZFkYZnFe9GId7EstY2lEMX+FqkkcazBsv7iPWrkQP9D2wYwk2v0K7DtHICturb9
|
||||
NFH70YcyMzCRpfAPvgbEOB5RprEiYL3aC+e0KFhNPcV4QtR/W6yuqxz9NgPDTWru
|
||||
g061RKNlqkeNUp5cnOWhj2Z98SfyZEmN8eIYNW0/neGHiYzXgrpWNvlu5Y1yuZxF
|
||||
ztapr4Oigl6CK7uJ0fOMRsGDdpjfAmOaXGJn2Y848VjUDvlj4HuyW2InPvDwd5t2
|
||||
Do/1zqasMMPl+xa7729Sr5oWYu4IaG/sTTLGQNC2gQKBgQDoeXAQDtwxche3Bddw
|
||||
WEn6ZtpbgDyeoDroZ4P9Fzyq5Rc3HJxv4WFILlYR16buJlPn7QARUD8r2/n+oTiA
|
||||
CN75z5YDKoRmKbCflp2OHmSWWCd7lIzpxaOoOwtPYhOWyaR4BEfaGPjowUz0XBfr
|
||||
Zp80APMOhNZKeeLUOR+Skk09VwKBgQC/Oevluv9rvELX2h09BvE63ZwHjdqW0xaz
|
||||
udSGATpssOeu+9k4E0WKqbyzIhYzR0tGYDHqgdAvU5sr++q/ImhIrz4qRgBSBZs1
|
||||
/1DvbarjogoSZK4lKB20vme+b329sv4TE1c+tusFmLQc+beS67MWfCtvoRzT79Lj
|
||||
WVjhUWjWOQKBgQDParIykZ+iukIweRTMVunCcYw3gYYUna+RdfKMmPP3IimBcZSe
|
||||
Of/bMAR5LOjyqN3KG2opCGkkj8NQjbXkfoW7a5xTvNaedGnzKaXvjGyAu6yWlWbY
|
||||
mQ845V1k+YOy0LxenKsjC47h1L39dEGfNVOsSYCTHrA3iOJ6KXFjDAfARwKBgAqK
|
||||
yet/VKd+2HyH5MbBhDkHkJuGAYABISZFX2cH2E23rv4hmkcSIOpVEzcY8PpgutCl
|
||||
avsPrwd853gUSkvBPNiM+LNh6pCbCISAj9izRhGIlF+RN1yLCqA1Qwjnopn7rTAm
|
||||
B9BIS0qisYNLbQhngBcEP2zHCAzTEuA/w7ugPlRZAoGBAK2wW8ByG3DagT2S4BPB
|
||||
kR/199rdPlNDr6x/BUdceYD76+ZvUsE+mJpzyluU4ECJwNlWLQZkw4uAaRWUNGP6
|
||||
wPYQH2LwXLoj9kxckHEdrC9S0uyEs7A8j60SrGGEtylLG7xS9LyGWj3ZwrycmPoA
|
||||
DOKRLBQCahiob4BAfDfQjXDg
|
||||
-----END PRIVATE KEY-----
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -f apache-selfsigned.key apache-selfsigned.csr apache-selfsigned.crt
|
||||
openssl genpkey -algorithm RSA -out apache-selfsigned.key
|
||||
openssl req -new -key apache-selfsigned.key -out apache-selfsigned.csr
|
||||
openssl x509 -req -days 3650 -in apache-selfsigned.csr -signkey apache-selfsigned.key -out apache-selfsigned.crt
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
# extended LDIF
|
||||
#
|
||||
# LDAPv3
|
||||
# base <dc=example,dc=org> with scope subtree
|
||||
# filter: (objectclass=*)
|
||||
# requesting: ALL
|
||||
#
|
||||
|
||||
# example.org
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
dc: example
|
||||
o: example
|
||||
|
||||
# users, example.org
|
||||
dn: ou=users,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
|
||||
dn: ou=groups,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: groups
|
||||
|
||||
dn: uid=sogo,ou=users,dc=example,dc=org
|
||||
uid: sogo
|
||||
cn: Sogo
|
||||
sn: Sogo
|
||||
mail: sogo@example.org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
userPassword:: c29nbw==
|
||||
uidNumber: 1000
|
||||
gidNumber: 1000
|
||||
homeDirectory: /home/sogo
|
||||
|
||||
dn: uid=cyrus,ou=users,dc=example,dc=org
|
||||
uid: cyrus
|
||||
cn: cyrus
|
||||
sn: cyrus
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
userPassword:: c29nbw==
|
||||
uidNumber: 1000
|
||||
gidNumber: 1000
|
||||
homeDirectory: /home/cyrus
|
||||
|
||||
dn: uid=sogo-tests1,ou=users,dc=example,dc=org
|
||||
uid: sogo-tests1
|
||||
cn: Dude
|
||||
sn: Love
|
||||
givenName: Dude
|
||||
homePhone: +1 (123) 456-7890
|
||||
l: Vladivostok
|
||||
telephoneNumber: +1 (120) 987-6543
|
||||
mail: sogo-tests1@example.org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
userPassword:: c29nbw==
|
||||
uidNumber: 1000
|
||||
gidNumber: 1000
|
||||
homeDirectory: /home/sogo-tests1
|
||||
|
||||
dn: uid=sogo-tests2,ou=users,dc=example,dc=org
|
||||
uid: sogo-tests2
|
||||
cn: Hewill
|
||||
sn: Crashit
|
||||
givenName: Hewill
|
||||
homePhone: +1 (123) 456-7890
|
||||
l: Vladivostok
|
||||
telephoneNumber: +1 (120) 987-6543
|
||||
mail: sogo-tests2@example.org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
userPassword:: c29nbw==
|
||||
uidNumber: 1000
|
||||
gidNumber: 1000
|
||||
homeDirectory: /home/sogo-tests2
|
||||
|
||||
dn: uid=sogo-tests3,ou=users,dc=example,dc=org
|
||||
uid: sogo-tests3
|
||||
cn: Ithas
|
||||
sn: Exploded
|
||||
givenName: Ithas
|
||||
homePhone: +1 (123) 456-7890
|
||||
l: Vladivostok
|
||||
telephoneNumber: +1 (120) 987-6543
|
||||
mail: sogo-tests3@example.org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
userPassword:: c29nbw==
|
||||
uidNumber: 1000
|
||||
gidNumber: 1000
|
||||
homeDirectory: /home/sogo-tests3
|
||||
|
||||
dn: uid=sogo-tests-super,ou=users,dc=example,dc=org
|
||||
uid: sogo-tests-super
|
||||
cn: John Doe
|
||||
sn: Doe
|
||||
givenName: John
|
||||
homePhone: +1 (123) 456-7890
|
||||
l: Vladivostok
|
||||
telephoneNumber: +1 (120) 987-6543
|
||||
mail: sogo-tests-super@example.org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
userPassword:: c29nbw==
|
||||
uidNumber: 1000
|
||||
gidNumber: 1000
|
||||
homeDirectory: /home/sogo-tests-super
|
||||
|
||||
dn: uid=res,ou=users,dc=example,dc=org
|
||||
uid: res
|
||||
cn: resource No Overbook
|
||||
sn: source
|
||||
departmentNumber: 1
|
||||
description: location
|
||||
givenName: res
|
||||
mail: res@example.org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
userPassword:: c29nbw==
|
||||
uidNumber: 1000
|
||||
gidNumber: 1000
|
||||
homeDirectory: /home/res
|
||||
|
||||
dn: uid=res-nolimit,ou=users,dc=example,dc=org
|
||||
uid: res-nolimit
|
||||
cn: resource Overbook
|
||||
sn: source
|
||||
departmentNumber: 0
|
||||
description: location
|
||||
givenName: res-nolimit
|
||||
mail: res-nolimit@example.org
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
userPassword:: c29nbw==
|
||||
uidNumber: 1000
|
||||
gidNumber: 1000
|
||||
homeDirectory: /home/res-nolimit
|
||||
|
||||
# readers, users, example.org
|
||||
dn: uid=readers,ou=users,dc=example,dc=org
|
||||
objectClass: extensibleObject
|
||||
objectClass: groupOfNames
|
||||
cn: readers
|
||||
mail: readers@example.org
|
||||
member: uid=sogo-tests1,ou=users,dc=example,dc=org
|
||||
member: uid=sogo-tests2,ou=users,dc=example,dc=org
|
||||
member: uid=sogo-tests3,ou=users,dc=example,dc=org
|
||||
|
||||
|
||||
# search result
|
||||
search: 2
|
||||
result: 0 Success
|
||||
|
||||
# numResponses: 14
|
||||
# numEntries: 13
|
||||
@@ -0,0 +1,8 @@
|
||||
# original catatnight/postfix
|
||||
FROM agiledigital/postfix:latest
|
||||
|
||||
ADD main.cf /etc/postfix/main.cf
|
||||
ADD mailname /etc/mailname
|
||||
ADD virtual /etc/postfix/virtual
|
||||
ADD entrypoint.sh /entrypoint.sh
|
||||
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
cp /etc/postfix/main.cf /etc/postfix/main.cf.bak
|
||||
chown root:root /etc/postfix/virtual
|
||||
chown root:root /etc/postfix/virtual.db
|
||||
postmap /etc/postfix/virtual
|
||||
|
||||
/opt/install.sh
|
||||
# /opt/install.sh make changes /etc/postfix/main.cf, so restore bak file
|
||||
rm -f /etc/postfix/main.cf
|
||||
cp /etc/postfix/main.cf.bak /etc/postfix/main.cf
|
||||
/usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||
@@ -0,0 +1 @@
|
||||
example.org
|
||||
@@ -0,0 +1,52 @@
|
||||
# Debian specific: Specifying a file name will cause the first
|
||||
# line of that file to be used as the name. The Debian default
|
||||
# is /etc/mailname.
|
||||
#myorigin = /etc/mailname
|
||||
|
||||
smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
|
||||
biff = no
|
||||
|
||||
# appending .domain is the MUA's job.
|
||||
append_dot_mydomain = no
|
||||
|
||||
# Uncomment the next line to generate "delayed mail" warnings
|
||||
#delay_warning_time = 4h
|
||||
|
||||
readme_directory = no
|
||||
|
||||
# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 3.6 on
|
||||
# fresh installs.
|
||||
compatibility_level = 3.6
|
||||
|
||||
# TLS parameters
|
||||
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
|
||||
smtpd_tls_security_level=may
|
||||
|
||||
smtp_tls_CApath=/etc/ssl/certs
|
||||
smtp_tls_security_level=may
|
||||
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
||||
|
||||
smtp_sender_dependent_authentication = yes
|
||||
smtp_sasl_auth_enable = yes
|
||||
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
|
||||
|
||||
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
|
||||
myhostname = localhost
|
||||
alias_maps = hash:/etc/aliases
|
||||
alias_database = hash:/etc/aliases
|
||||
mydestination = example.org
|
||||
mydomain = example.org
|
||||
relayhost =
|
||||
mailbox_size_limit = 51200000
|
||||
recipient_delimiter =
|
||||
inet_interfaces = all
|
||||
inet_protocols = all
|
||||
maillog_file = /var/log/mail.log
|
||||
myorigin = /etc/mailname
|
||||
default_transport = error
|
||||
relay_transport = error
|
||||
|
||||
mailbox_transport = lmtp:inet:imapd:24
|
||||
|
||||
local_recipient_maps = hash:/etc/postfix/virtual
|
||||
@@ -0,0 +1 @@
|
||||
@example.org root
|
||||
Executable
+518
@@ -0,0 +1,518 @@
|
||||
#!/bin/bash
|
||||
|
||||
TESTS_RET=0
|
||||
SRC_SOPE=$1
|
||||
SRC_SOGO=$2
|
||||
TESTS_FAILED=false
|
||||
TESTS_RESULTS=()
|
||||
JSON_TEST_RESULTS_PATH="/tmp/tests_results.json"
|
||||
|
||||
export TERM=xterm
|
||||
|
||||
# formatting
|
||||
bold=$(tput bold)
|
||||
normal=$(tput sgr0)
|
||||
underline=$(tput smul)
|
||||
no_underline=$(tput rmul)
|
||||
red=$(tput setaf 1)
|
||||
green=$(tput setaf 2)
|
||||
yellow=$(tput setaf 3)
|
||||
blue=$(tput setaf 4)
|
||||
magenta=$(tput setaf 5)
|
||||
cyan=$(tput setaf 6)
|
||||
white=$(tput setaf 7)
|
||||
|
||||
title() {
|
||||
echo "${bold}${cyan}$1${normal}"
|
||||
}
|
||||
|
||||
subtitle() {
|
||||
echo "${magenta}$1${normal}"
|
||||
}
|
||||
|
||||
subsubtitle() {
|
||||
echo "${cyan}$1${normal}"
|
||||
}
|
||||
|
||||
texterror() {
|
||||
echo "${bold}${red}$1${normal}"
|
||||
}
|
||||
|
||||
textwarning() {
|
||||
echo "${bold}${yellow}$1${normal}"
|
||||
}
|
||||
|
||||
textsuccess() {
|
||||
echo "${bold}${green}$1${normal}"
|
||||
}
|
||||
|
||||
stepsuccess() {
|
||||
echo -e "$1 ${bold}${green}✔ ${normal}${bold}$2${normal}"
|
||||
}
|
||||
|
||||
steperror() {
|
||||
echo -e "$1 ${bold}${red}𐄂 ${normal}${bold}$2${normal}"
|
||||
}
|
||||
|
||||
in_array() {
|
||||
local SEARCH_ELEMENT
|
||||
SEARCH_ELEMENT="$1"
|
||||
shift
|
||||
local ELEMENT
|
||||
for ELEMENT in "$@"; do
|
||||
if [[ "$ELEMENT" == "$SEARCH_ELEMENT" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
is_in_docker() {
|
||||
if [ -f /.dockerenv ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
get_latest_github_commit_hash() {
|
||||
REPO="$1"
|
||||
BRANCH="${2:-master}"
|
||||
TOKEN="$3"
|
||||
GITHUB_API_URL="https://api.github.com/repos/$REPO/commits/$BRANCH"
|
||||
LATEST_COMMIT_HASH=$(curl -s -k -H "Authorization: token $TOKEN" $GITHUB_API_URL | grep '"sha"' | head -n 1 | awk '{print $2}' | tr -d '",')
|
||||
|
||||
echo "$LATEST_COMMIT_HASH"
|
||||
}
|
||||
|
||||
build_system_header() {
|
||||
echo "${white} "
|
||||
echo "${white} "
|
||||
echo "${white} "
|
||||
echo "${white} ${green}▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒▒▒▒${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒▒▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒▒▒▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒${white} ${green}▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒▒▒▒${white} "
|
||||
echo "${white} "
|
||||
echo "${white} ${bold}${green}BUILD SYSTEM${normal} "
|
||||
echo "${white} "
|
||||
echo ""
|
||||
}
|
||||
|
||||
|
||||
check_exit_code() {
|
||||
local EXIT_CODE=$?
|
||||
if [ $EXIT_CODE -ne 0 ]; then
|
||||
return $EXIT_CODE
|
||||
fi
|
||||
}
|
||||
|
||||
function prep_tests {
|
||||
# install node modules
|
||||
cd "$SRC_SOGO/Tests" && \
|
||||
npm config set loglevel=error && \
|
||||
npm install > /dev/null
|
||||
RC=$?
|
||||
GRC=$(($GRC+$RC))
|
||||
if [ $RC -ne 0 ]; then
|
||||
MSG="$MSG\nError installing node modules"
|
||||
error_out
|
||||
fi
|
||||
|
||||
# fixup the tests configuration
|
||||
cd "$SRC_SOGO/Tests/lib" && \
|
||||
cat >config.js <<EOF
|
||||
export default {
|
||||
hostname: "httpd",
|
||||
port: "81",
|
||||
username: "sogo-tests1",
|
||||
password: "sogo",
|
||||
superuser: "sogo-tests-super",
|
||||
superuser_password: "sogo",
|
||||
subscriber_username: "sogo-tests2",
|
||||
subscriber_password: "sogo",
|
||||
attendee1: "sogo-tests2@example.org",
|
||||
attendee1_username: "sogo-tests2",
|
||||
attendee1_password: "sogo",
|
||||
attendee1_delegate: "sogo-tests3@example.org",
|
||||
attendee1_delegate_username: "sogo-tests3",
|
||||
attendee1_delegate_password: "sogo",
|
||||
resource_no_overbook: "res",
|
||||
resource_can_overbook: "res-nolimit",
|
||||
white_listed_attendee: {
|
||||
"sogo-tests1": "John Doe <sogo-tests1@example.org>"
|
||||
},
|
||||
mailserver: "dovecot",
|
||||
testput_nbrdays: 30,
|
||||
sieve_server: "dovecot",
|
||||
sieve_port: 4190,
|
||||
sogo_user: "sogo",
|
||||
sogo_tool_path: "/usr/sbin/sogo-tool",
|
||||
webCalendarURL: "http://httpd/CanadaHolidays.ics",
|
||||
timeout: 600000
|
||||
}
|
||||
EOF
|
||||
RC=$?
|
||||
GRC=$(($GRC+$RC))
|
||||
if [ $RC -ne 0 ]; then
|
||||
MSG="$MSG\nError creating config.js"
|
||||
error_out
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
}
|
||||
|
||||
function prep_tests_mysql {
|
||||
rm /etc/sogo/sogo.conf
|
||||
cp /etc/sogo/sogo-tests-mysql-ldap.conf /etc/sogo/sogo.conf
|
||||
|
||||
TMP_FILE=$(mktemp)
|
||||
chmod 600 $TMP_FILE
|
||||
cat <<EOF > $TMP_FILE
|
||||
[client]
|
||||
user = sogobuild
|
||||
password = sogo123
|
||||
host = mariadb
|
||||
EOF
|
||||
|
||||
# drop the mysql database and recreate it
|
||||
echo "drop database sogo_integration_tests;" | mysql --defaults-extra-file=$TMP_FILE -h mariadb
|
||||
echo "create database sogo_integration_tests charset=utf8;" | mysql --defaults-extra-file=$TMP_FILE -h mariadb
|
||||
|
||||
rm $TMP_FILE
|
||||
|
||||
RC=$?
|
||||
GRC=$(($GRC+$RC))
|
||||
if [ $RC -ne 0 ]; then
|
||||
MSG="$MSG\nError recreating MySQL database"
|
||||
error_out
|
||||
fi
|
||||
}
|
||||
|
||||
function prep_tests_postgresql {
|
||||
rm /etc/sogo/sogo.conf
|
||||
cp /etc/sogo/sogo-tests-postgresql-ldap.conf /etc/sogo/sogo.conf
|
||||
|
||||
# drop the postgresql database and recreate it
|
||||
# the env var is unsafe, i know... just easier and non sensitive anyway
|
||||
PGPASSWORD=sogo123 dropdb -U sogobuild -h postgres sogo_integration_tests
|
||||
PGPASSWORD=sogo123 createdb -O sogobuild -U sogobuild -h postgres sogo_integration_tests
|
||||
RC=$?
|
||||
GRC=$(($GRC+$RC))
|
||||
if [ $RC -ne 0 ]; then
|
||||
MSG="$MSG\nError recreating postgresql database"
|
||||
error_out
|
||||
fi
|
||||
}
|
||||
|
||||
function prep_tests_mysql_auth {
|
||||
rm /etc/sogo/sogo.conf
|
||||
cp /etc/sogo/sogo-tests-mysql.conf /etc/sogo/sogo.conf
|
||||
}
|
||||
|
||||
function prep_tests_postgresql_auth {
|
||||
rm /etc/sogo/sogo.conf
|
||||
cp /etc/sogo/sogo-tests-postgresql.conf /etc/sogo/sogo.conf
|
||||
}
|
||||
|
||||
function prep_tests_mysql_combined {
|
||||
rm /etc/sogo/sogo.conf
|
||||
cp /etc/sogo/sogo-tests-mysql-ldap-combined.conf /etc/sogo/sogo.conf
|
||||
}
|
||||
|
||||
function prep_tests_mysql_auth_combined {
|
||||
rm /etc/sogo/sogo.conf
|
||||
cp /etc/sogo/sogo-tests-mysql-combined.conf /etc/sogo/sogo.conf
|
||||
}
|
||||
|
||||
function prep_tests_postgresql_combined {
|
||||
rm /etc/sogo/sogo.conf
|
||||
cp /etc/sogo/sogo-tests-postgresql-ldap-combined.conf /etc/sogo/sogo.conf
|
||||
}
|
||||
|
||||
function prep_tests_postgresql_auth_combined {
|
||||
rm /etc/sogo/sogo.conf
|
||||
cp /etc/sogo/sogo-tests-postgresql-combined.conf /etc/sogo/sogo.conf
|
||||
}
|
||||
|
||||
function error_out {
|
||||
set +x
|
||||
echo -e "$MSG"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function check_tests {
|
||||
FAIL=$(cat /tmp/out.log | grep -m 1 -Eo "([0-9]+)\s+failure" | cut -d' ' -f 1)
|
||||
TOTAL=$(cat /tmp/out.log | grep -m 1 -Eo "([0-9]+)\s+spec" | cut -d' ' -f 1)
|
||||
NOT_EXECUTED=$(cat /tmp/out.log | grep -m 1 -Eo "([0-9]+)\s+pending" | cut -d' ' -f 1)
|
||||
|
||||
# If test fail
|
||||
if [ $FAIL -gt 0 ]
|
||||
then
|
||||
TESTS_RET=1
|
||||
fi
|
||||
|
||||
# Console
|
||||
echo -e "\033[1mTests results for $1\033[0m" >> "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
echo -e "Total : $TOTAL" >> "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
if [ $FAIL -gt 0 ]
|
||||
then
|
||||
echo -e "\033[0;31mFailed : $FAIL\033[0m" >> "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
else
|
||||
echo -e "Failed : $FAIL" >> "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
fi
|
||||
echo -e "Not executed : $NOT_EXECUTED" >> "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
if [ $FAIL -gt 0 ]
|
||||
then
|
||||
echo -e "\033[0;31mTests failed\033[0m" >> "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
else
|
||||
echo -e "\033[0;36mTests success\033[0m" >> "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
fi
|
||||
echo "" >> "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
|
||||
# HTML
|
||||
echo "<table style=\"margin-bottom: 10px;\"><tbody><tr><td colspan=\"3\" style=\"background-color: aquamarine;\"><strong>$1</strong></td></tr><tr>" >> "$SRC_SOGO/Tests/results/index.html"
|
||||
echo "<td>Total</td><td>Failed</td><td>Not executed</td></tr><tr>" >> "$SRC_SOGO/Tests/results/index.html"
|
||||
if [ $FAIL -gt 0 ]
|
||||
then
|
||||
echo "<td>$TOTAL</td><td style=\"color: red;\">$FAIL</td><td>$NOT_EXECUTED</td>" >> "$SRC_SOGO/Tests/results/index.html"
|
||||
echo "</tr><tr><td colspan=\"3\" style=\"color: red;\"><strong>Tests failed</strong></td>" >> "$SRC_SOGO/Tests/results/index.html"
|
||||
else
|
||||
echo "<td>$TOTAL</td><td>$FAIL</td><td>$NOT_EXECUTED</td>" >> "$SRC_SOGO/Tests/results/index.html"
|
||||
echo "</tr><tr><td colspan=\"3\" style=\"color: #170;\"><strong>Tests success</strong></td>" >> "$SRC_SOGO/Tests/results/index.html"
|
||||
fi
|
||||
echo "</tr><tr><td colspan=\"3\"><a href=\"$2.html\" target=\"_blank\">View test report</a></td>" >> "$SRC_SOGO/Tests/results/index.html"
|
||||
echo "</tr></tbody></table>" >> "$SRC_SOGO/Tests/results/index.html"
|
||||
if [ $TESTS_RET -eq 1 ]
|
||||
then
|
||||
echo -e "\033[0;31mFailed\033[0m"
|
||||
return 131
|
||||
else
|
||||
echo -e "\033[0;36mSuccess\033[0m"
|
||||
fi
|
||||
}
|
||||
|
||||
function run_tests {
|
||||
RET=0
|
||||
USE_PKILL=$3
|
||||
|
||||
# create empty logfile
|
||||
cat /dev/null >/tmp/out.log
|
||||
|
||||
if [ "$USE_PKILL" -eq 1 ]; then
|
||||
pkill -9 sogod
|
||||
else
|
||||
service sogod stop
|
||||
fi
|
||||
# Kill residual SOGo process
|
||||
LSOF_OUT=$(lsof -i TCP@127.0.0.1 -Fp | tr -d p)
|
||||
if [ ! -z "$LSOF_OUT" ]; then
|
||||
kill -9 $LSOF_OUT
|
||||
fi
|
||||
|
||||
|
||||
# restart services
|
||||
if [ "$USE_PKILL" -eq 1 ]; then
|
||||
su -s /bin/bash -c "/usr/sbin/sogod -WOWorkersCount 3 -WOPidFile /var/run/sogo/sogo.pid -WOLogFile /var/log/sogo/sogo.log" sogo
|
||||
else
|
||||
service sogod start
|
||||
fi
|
||||
|
||||
# wait for it to settle
|
||||
sleep 3
|
||||
|
||||
# run the tests and gather output
|
||||
cd "$SRC_SOGO/Tests"
|
||||
RC=$?
|
||||
GRC=$(($GRC+$RC))
|
||||
if [ $RC -ne 0 ]; then
|
||||
MSG="$MSG\nCan't cd into the Tests folder..."
|
||||
error_out
|
||||
fi
|
||||
npm run test-junit 2>&1 | tee -a /tmp/out.log
|
||||
|
||||
RC=$?
|
||||
GRC=$(($GRC+$RC))
|
||||
if [ $RC -ne 0 ]; then
|
||||
MSG="$MSG\nError running the integration tests"
|
||||
error_out
|
||||
fi
|
||||
|
||||
# source GNUstep.sh
|
||||
# set +x
|
||||
#. /usr/share/GNUstep/Makefiles/GNUstep.sh
|
||||
# set -x
|
||||
|
||||
# teststrings
|
||||
# run only once, no point in running with all the backend
|
||||
# substitutions
|
||||
# if [[ -z ${TEST_RUN_COUNT} || ${TEST_RUN_COUNT} -lt 1 ]]; then
|
||||
# cd Integration
|
||||
# ./teststrings.sh 2>&1 | tee -a /tmp/out.log
|
||||
# fi
|
||||
|
||||
# stop sogo when we're done
|
||||
if [ "$USE_PKILL" -eq 1 ]; then
|
||||
pkill -9 sogod
|
||||
else
|
||||
service sogod stop
|
||||
fi
|
||||
|
||||
XML_FILE="$SRC_SOGO/Tests/results/$2"
|
||||
mv /tmp/results.xml "$XML_FILE"
|
||||
|
||||
check_tests "$1" "$2"
|
||||
|
||||
# Generate JSON
|
||||
TOTAL_TESTS=$(xmllint --xpath 'sum(//testsuite/@tests)' "$XML_FILE")
|
||||
TOTAL_FAILURES=$(xmllint --xpath 'sum(//testsuite/@failures)' "$XML_FILE")
|
||||
TOTAL_ERRORS=$(xmllint --xpath 'sum(//testsuite/@errors)' "$XML_FILE")
|
||||
TOTAL_FAILED=$(($TOTAL_FAILURES + $TOTAL_ERRORS))
|
||||
TOTAL_SUCCESS=$(($TOTAL_TESTS - $TOTAL_FAILED))
|
||||
if [ "$TOTAL_FAILED" -gt 0 ]; then
|
||||
FAILED=true
|
||||
TESTS_FAILED=true
|
||||
else
|
||||
FAILED=false
|
||||
fi
|
||||
JSON_DATA=$(jq -n \
|
||||
--argjson total_tests "$TOTAL_TESTS" \
|
||||
--argjson total_failed "$TOTAL_FAILED" \
|
||||
--argjson total_success "$TOTAL_SUCCESS" \
|
||||
--arg description "$1" \
|
||||
--arg scenario "$2" \
|
||||
--argjson failed "$FAILED" \
|
||||
'{scenario: $scenario, description: $description, total_tests: $total_tests, total_failed: $total_failed, total_success: $total_success, failed: $failed}')
|
||||
TESTS_RESULTS+=("$JSON_DATA")
|
||||
}
|
||||
|
||||
test() {
|
||||
#set -x
|
||||
USE_PKILL=${1:-0}
|
||||
TESTS_FAILED=false
|
||||
TESTS_RESULTS=()
|
||||
rm -f "$JSON_TEST_RESULTS_PATH"
|
||||
if [ "${SKIP_TESTS:-0}" -eq 1 ]; then
|
||||
subsubtitle "Skipping tests"
|
||||
return 0;
|
||||
fi
|
||||
|
||||
# INTEGRATION TESTS
|
||||
if [[ -d "$SRC_SOGO/Tests/results" ]]; then
|
||||
rm -Rf "$SRC_SOGO/Tests/results"
|
||||
fi
|
||||
|
||||
mkdir -p "$SRC_SOGO/Tests/results"
|
||||
echo "" > "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
echo "<html><head><style>table, th, td {border: 1px solid darkgrey;border-collapse: collapse; width: 400px; font-family: monospace;}</style></head><body>" > "$SRC_SOGO/Tests/results/index.html"
|
||||
|
||||
test_title_color="${bold}${yellow}"
|
||||
|
||||
# MySQL
|
||||
echo "${test_title_color}Running tests with LDAP auth and MySQL backend${normal}"
|
||||
prep_tests
|
||||
prep_tests_mysql
|
||||
run_tests "Running tests with LDAP auth and MySQL backend" "results-mysql-ldap.xml" $USE_PKILL
|
||||
|
||||
echo "${test_title_color}Running tests with MySQL auth and MySQL backend${normal}"
|
||||
prep_tests
|
||||
prep_tests_mysql
|
||||
prep_tests_mysql_auth
|
||||
run_tests "Running tests with MySQL auth and MySQL backend" "results-mysql.xml" $USE_PKILL
|
||||
|
||||
echo "${test_title_color}Running tests with LDAP auth and MySQL backend, combined database tables${normal}"
|
||||
prep_tests
|
||||
prep_tests_mysql
|
||||
prep_tests_mysql_combined
|
||||
run_tests "Running tests with LDAP auth and MySQL backend, combined database tables" "results-mysql-ldap-combined.xml" $USE_PKILL
|
||||
|
||||
echo "${test_title_color}Running tests with MySQL auth and MySQL backend, combined database tables${normal}"
|
||||
prep_tests
|
||||
prep_tests_mysql
|
||||
prep_tests_mysql_auth_combined
|
||||
run_tests "Running tests with MySQL auth and MySQL backend, combined database tables" "results-mysql-combined.xml" $USE_PKILL
|
||||
|
||||
|
||||
# PGSQL
|
||||
echo "${test_title_color}Running tests with LDAP auth and postgresql backend${normal}"
|
||||
prep_tests
|
||||
prep_tests_postgresql
|
||||
run_tests "Running tests with LDAP auth and postgresql backend" "results-postgresql-ldap.xml" $USE_PKILL
|
||||
|
||||
echo "${test_title_color}Running tests with postgresql auth and postgresql backend${normal}"
|
||||
prep_tests
|
||||
prep_tests_postgresql
|
||||
prep_tests_postgresql_auth
|
||||
run_tests "Running tests with postgresql auth and postgresql backend" "results-postgresql.xml" $USE_PKILL
|
||||
|
||||
echo "${test_title_color}Running tests with LDAP auth and postgresql backend, combined database tables${normal}"
|
||||
prep_tests
|
||||
prep_tests_postgresql
|
||||
prep_tests_postgresql_combined
|
||||
run_tests "Running tests with LDAP auth and postgresql backend, combined database tables" "results-postgresql-ldap-combined.xml" $USE_PKILL
|
||||
|
||||
echo "${test_title_color}Running tests with postgresql auth and postgresql backend, combined database tables${normal}"
|
||||
prep_tests
|
||||
prep_tests_postgresql
|
||||
prep_tests_postgresql_auth_combined
|
||||
run_tests "Running tests with postgresql auth and postgresql backend, combined database tables" "results-postgresql-combined.xml" $USE_PKILL
|
||||
|
||||
|
||||
# set -x
|
||||
|
||||
# JUnit
|
||||
for file in $SRC_SOGO/Tests/results/*.xml #$xmlfiles
|
||||
do
|
||||
xunit-viewer -r $file -c -o "$file.html"
|
||||
done
|
||||
cat "$SRC_SOGO/Tests/results/tests_results.txt"
|
||||
echo "</body>" >> "$SRC_SOGO/Tests/results/index.html"
|
||||
chown 644 -R "$SRC_SOGO/Tests/results"
|
||||
chown -R www-data:www-data "$SRC_SOGO/Tests/results"
|
||||
|
||||
# Restore conf
|
||||
rm /etc/sogo/sogo.conf
|
||||
cp /etc/sogo/sogo-base.conf /etc/sogo/sogo.conf
|
||||
if [ "$USE_PKILL" -eq 1 ]; then
|
||||
pkill -9 sogod
|
||||
su -s /bin/bash -c "/usr/sbin/sogod -WOWorkersCount 3 -WOPidFile /var/run/sogo/sogo.pid -WOLogFile /var/log/sogo/sogo.log" sogo
|
||||
else
|
||||
service sogod restart > /dev/null
|
||||
fi
|
||||
|
||||
# Global json results
|
||||
TESTS_RESULTS_JSON=$(printf '%s\n' "${TESTS_RESULTS[@]}" | jq -s .)
|
||||
FINAL_JSON=$(jq -n \
|
||||
--argjson tests_failed "$TESTS_FAILED" \
|
||||
--argjson tests "$TESTS_RESULTS_JSON" \
|
||||
'{failed: $tests_failed, tests: $tests}')
|
||||
echo "$FINAL_JSON" > $JSON_TEST_RESULTS_PATH
|
||||
|
||||
result_link="${bold}${magenta}--------------------------------------------------\nTests results : https://127.0.0.1/tests/index.html${normal}"
|
||||
|
||||
# If test fail
|
||||
if [ $TESTS_RET -eq 1 ]
|
||||
then
|
||||
echo -e "$result_link [${bold}${red}Failed${normal}]\n"
|
||||
return 131
|
||||
else
|
||||
echo -e "$result_link [${bold}${green}Success${normal}]\n"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
cd ${WORKSPACE} \
|
||||
./configure --enable-debug --disable-strip --enable-mfa \
|
||||
make \
|
||||
make install \
|
||||
echo "register sogo library" \
|
||||
echo "/usr/local/lib/sogo" > /etc/ld.so.conf.d/sogo.conf \
|
||||
ldconfig \
|
||||
echo "create directories and enforce permissions" \
|
||||
install -o sogo -g sogo -m 755 -d /var/run/sogo \
|
||||
install -o sogo -g sogo -m 750 -d /var/spool/sogo \
|
||||
install -o sogo -g sogo -m 750 -d /var/log/sogo
|
||||
|
||||
su -s /bin/bash - sogo -c "echo \"set debuginfod enabled off\" >> ~/.gdbinit"
|
||||
Executable
+147
@@ -0,0 +1,147 @@
|
||||
#!/bin/bash
|
||||
|
||||
SRC_SOPE=/src/SOPE/
|
||||
SRC_SOGO=/src/SOGo/
|
||||
|
||||
source /include/common.sh $SRC_SOPE $SRC_SOGO
|
||||
|
||||
options="h:b:ba:br:d"
|
||||
long_options="help,build,build-all,build-resources,debug"
|
||||
parsed_options=$(getopt -o $options -l $long_options)
|
||||
|
||||
# Check for errors in parsing
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate the parsed options
|
||||
#eval set -- "$parsed_options"
|
||||
|
||||
help() {
|
||||
echo "${green} ----------------------"
|
||||
echo "| ${bold}SOGo Dev env${normal}${green} |"
|
||||
echo " ----------------------"
|
||||
echo
|
||||
echo "${normal}Options are :${cyan}"
|
||||
|
||||
echo -e "-h, --help\t\tShow help"
|
||||
echo -e "-b, --build\t\tBuild sogo app"
|
||||
echo -e "-ba, --build-all\tClean, build sogo and sope"
|
||||
echo -e "-br, --build-resources\tBuild only sogo JS/CSS resources"
|
||||
echo -e "-d, --debug\t\tStart sogo in debug with gdb"
|
||||
echo "${normal}"
|
||||
}
|
||||
|
||||
ssl_fix() {
|
||||
if [[ -z "${LD_PRELOAD}" ]]; then
|
||||
LIBSSL_LOCATION=$(find / -type f -name "libssl.so.*" -print -quit);echo "LD_PRELOAD=$LIBSSL_LOCATION" >> /etc/default/sogo
|
||||
echo "LD_LIBRARY_PATH=/usr/local/lib/sogo:/usr/local/lib:$LD_LIBRARY_PATH" >> /etc/default/sogo
|
||||
export LD_PRELOAD=$LIBSSL_LOCATION
|
||||
else
|
||||
echo "LD_PRELOAD=$LD_PRELOAD" >> /etc/default/sogo
|
||||
echo "LD_LIBRARY_PATH=/usr/local/lib/sogo:/usr/local/lib:$LD_LIBRARY_PATH" >> /etc/default/sogo
|
||||
export LD_PRELOAD=$LD_PRELOAD
|
||||
fi
|
||||
}
|
||||
|
||||
build_resources() {
|
||||
cd $SRC_SOGO
|
||||
npm config set loglevel=error
|
||||
git config --global --add safe.directory '*'
|
||||
cd UI/WebServerResources
|
||||
make dev
|
||||
make install
|
||||
}
|
||||
|
||||
build_sogo() {
|
||||
service sogod stop
|
||||
ssl_fix
|
||||
cd $SRC_SOGO
|
||||
cd ActiveSync
|
||||
make && make install
|
||||
cd ..
|
||||
./configure --enable-saml2 --enable-debug --disable-strip --enable-mfa
|
||||
make
|
||||
make install
|
||||
build_resources
|
||||
RC=$?
|
||||
if [ $RC -ne 0 ]; then
|
||||
exit $RC
|
||||
fi
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
service sogod start
|
||||
fi
|
||||
}
|
||||
|
||||
build_all() {
|
||||
service sogod stop
|
||||
cd $SRC_SOPE
|
||||
make clean
|
||||
./configure --with-gnustep --enable-debug --disable-strip
|
||||
make
|
||||
make install
|
||||
cd $SRC_SOGO
|
||||
cd ActiveSync
|
||||
make clean
|
||||
cd ..
|
||||
make clean
|
||||
build_sogo
|
||||
RC=$?
|
||||
if [ $RC -ne 0 ]; then
|
||||
exit $RC
|
||||
fi
|
||||
}
|
||||
|
||||
debug() {
|
||||
build_sogo "norestart"
|
||||
echo "${magenta}================"
|
||||
echo "GDB reminders :"
|
||||
echo " * run : Run app (set breakpoints before)"
|
||||
echo " * break SOGo.m:494 : Add breakpoint example"
|
||||
echo " * c : continue"
|
||||
echo " * n : next"
|
||||
echo " * list 504 : show lines around"
|
||||
echo " * p var : print variable 'var'"
|
||||
echo " * po var : print object 'var'"
|
||||
echo " * bt : Show stacktrace"
|
||||
echo " * bt full : Show stacktrace with all vars"
|
||||
echo "================${normal}"
|
||||
echo ""
|
||||
|
||||
su -s /bin/bash - sogo -c "gdb --args /usr/local/sbin/sogod -WOUseWatchDog NO -WONoDetach YES -WOPort 0.0.0.0:50000 -WOWorkersCount 1 -WOPidFile /var/run/sogo/sogo.pid"
|
||||
}
|
||||
|
||||
# Process the options
|
||||
while true; do
|
||||
case "$1" in
|
||||
-b|--build)
|
||||
build_sogo
|
||||
exit 0
|
||||
;;
|
||||
-ba|--build-all)
|
||||
build_all
|
||||
exit 0
|
||||
;;
|
||||
-br|--build-resources)
|
||||
build_resources
|
||||
exit 0
|
||||
;;
|
||||
-d|--debug)
|
||||
debug
|
||||
exit 0
|
||||
;;
|
||||
-h|--help)
|
||||
help
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
bold=$(tput bold)
|
||||
normal=$(tput sgr0)
|
||||
green=$(tput setaf 2)
|
||||
white=$(tput setaf 7)
|
||||
magenta=$(tput setaf 5)
|
||||
cyan=$(tput setaf 6)
|
||||
underline=$(tput smul)
|
||||
no_underline=$(tput rmul)
|
||||
|
||||
cd /src/SOGo
|
||||
|
||||
echo "${white} "
|
||||
echo "${white} "
|
||||
echo "${white} "
|
||||
echo "${white} ${green}▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒▒▒▒${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒▒▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒▒${white} ${green}▒▒▒▒▒▒▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒▒▒▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒${white} ${green}▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒${white} ${green}▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒${white} ${green}▒▒▒▒${white} "
|
||||
echo "${white} ${green}▒▒▒▒▒▒▒▒▒▒${white} "
|
||||
echo "${white} "
|
||||
echo "${white} ${bold}${green}DEVELOPER${normal} "
|
||||
echo "${white} "
|
||||
echo "${bold}${magenta}Access : https://127.0.0.1/SOGo/${normal}"
|
||||
echo "${bold}${cyan}Use ${underline}devenv${no_underline} command to start${normal}"
|
||||
echo ""
|
||||
|
||||
HAS_OLD_SOGO_INSTANCE=$(find /usr/local/lib/sogo/ -type f -name libSOGoUI.so.* | wc -l)
|
||||
if [ "$HAS_OLD_SOGO_INSTANCE" -gt 1 ]; then
|
||||
echo "${bold}${red}/!\ You have an instable dev environment (two sogo libraries versions). This can be caused by a disynchronized SOGo code source (docker and local environment) This can be fixed by updating your local git repositories, delete and rebuild docker compose. You can also run devenv -ba.${normal}"
|
||||
fi
|
||||
|
||||
|
||||
/etc/init.d/sogod restart
|
||||
# Run for ever
|
||||
tail -f /dev/null
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
/* ********************* Main SOGo configuration file **********************
|
||||
* *
|
||||
* Since the content of this file is a dictionary in OpenStep plist format, *
|
||||
* the curly braces enclosing the body of the configuration are mandatory. *
|
||||
* See the Installation Guide for details on the format. *
|
||||
* *
|
||||
* C and C++ style comments are supported. *
|
||||
* *
|
||||
* This example configuration contains only a subset of all available *
|
||||
* configuration parameters. Please see the installation guide more details. *
|
||||
* *
|
||||
* ~sogo/GNUstep/Defaults/.GNUstepDefaults has precedence over this file, *
|
||||
* make sure to move it away to avoid unwanted parameter overrides. *
|
||||
* *
|
||||
* **************************************************************************/
|
||||
|
||||
/* Database configuration (mysql://, postgresql:// or oracle://) */
|
||||
WOPort = "0.0.0.0:50000";
|
||||
|
||||
|
||||
// MySQL
|
||||
SOGoProfileURL = "mysql://sogobuild:sogo123@mariadb:3306/sogo/sogo_user_profile";
|
||||
OCSFolderInfoURL = "mysql://sogobuild:sogo123@mariadb:3306/sogo/sogo_folder_info";
|
||||
OCSSessionsFolderURL = "mysql://sogobuild:sogo123@mariadb:3306/sogo/sogo_sessions_folder";
|
||||
OCSEMailAlarmsFolderURL = "mysql://sogobuild:sogo123@mariadb:3306/sogo/sogo_alarms_folder";
|
||||
OCSCacheFolderURL = "mysql://sogobuild:sogo123@mariadb:3306/sogo/sogo_cache_folder";
|
||||
OCSStoreURL = "mysql://sogobuild:sogo123@mariadb:3306/sogo/sogo_store";
|
||||
OCSAclURL = "mysql://sogobuild:sogo123@mariadb:3306/sogo/sogo_acl";
|
||||
|
||||
|
||||
//Password
|
||||
SOGoPasswordChangeEnabled = YES;
|
||||
SOGoPasswordRecoveryEnabled = YES;
|
||||
|
||||
SOGoFirstWeekOfYear = "January1";
|
||||
WOWorkersCount = 10;
|
||||
WOWatchDogRequestTimeout = 5;
|
||||
WOListenQueueSize = 15;
|
||||
|
||||
SOGoUserSources = (
|
||||
{
|
||||
CNFieldName = cn;
|
||||
IDFieldName = uid;
|
||||
UIDFieldName = uid;
|
||||
baseDN = "ou=users,dc=example,dc=org";
|
||||
bindDN = "cn=admin,dc=example,dc=org";
|
||||
bindPassword = "password";
|
||||
canAuthenticate = YES;
|
||||
displayName = "Shared Addresses";
|
||||
hostname = "openldap";
|
||||
id = public;
|
||||
isAddressBook = YES;
|
||||
port = 389;
|
||||
KindFieldName = description;
|
||||
MultipleBookingsFieldName="departmentNumber";
|
||||
}
|
||||
);
|
||||
|
||||
SOGoDisableSharing = ();
|
||||
SOGoDisableExport = ();
|
||||
SOGoDisableSharingAnyAuthUser = ();
|
||||
|
||||
|
||||
//Identities
|
||||
SOGoMailAuxiliaryUserAccountsEnabled = YES;
|
||||
|
||||
SOGoPageTitle = NewName;
|
||||
SOGoSieveScriptsEnabled = YES;
|
||||
SOGoForwardEnabled = YES;
|
||||
SOGoNotificationEnabled = YES;
|
||||
SOGoVacationEnabled = YES;
|
||||
SOGoEnableEMailAlarms = YES;
|
||||
SOGoEnablePublicAccess = YES;
|
||||
SOGoTimeZone = Europe/Paris;
|
||||
SOGoIMAPServer = "imapd:143";
|
||||
NGImap4AuthMechanism = "plain";
|
||||
SOGoSieveServer = "sieve://imapd:4190";
|
||||
SOGoMailingMechanism = smtp;
|
||||
SOGoSMTPServer = "smtpd";
|
||||
SOGoCalendarDefaultRoles = ("PublicDAndTViewer");
|
||||
SOGoAppointmentSendEMailNotifications = YES;
|
||||
SOGoMailDomain = "example.org";
|
||||
SOGoLanguage = "English";
|
||||
SOGoSuperUsernames = ("sogo-tests-super");
|
||||
SOGoCalendarDefaultRoles = ("PublicDAndTViewer");
|
||||
SOGoMemcachedHost = "memcached";
|
||||
SOGoXSRFValidationEnabled = NO;
|
||||
|
||||
NGImap4ConnectionStringSeparator = "/";
|
||||
|
||||
|
||||
|
||||
|
||||
/* Debug */
|
||||
SoDebugBaseURL = YES;
|
||||
SOGoDebugRequests = YES;
|
||||
ImapDebugEnabled = YES;
|
||||
LDAPDebugEnabled = YES;
|
||||
PGDebugEnabled = YES;
|
||||
SMTPDebugEnabled = YES;
|
||||
MySQL4DebugEnabled = YES;
|
||||
SOGoUIxDebugEnabled = YES;
|
||||
WOLogFile = /var/log/sogo/sogo.log;
|
||||
SOGoDebugLeaks = YES;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile
|
||||
{
|
||||
"name": "sogo",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "sogo",
|
||||
"workspaceFolder": "/workspace",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-azuretools.vscode-docker"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
services:
|
||||
memcached:
|
||||
image: memcached:1.6
|
||||
container_name: sogo_memcached
|
||||
restart: unless-stopped
|
||||
httpd:
|
||||
build:
|
||||
context: ./conf/httpd/
|
||||
dockerfile: Dockerfile
|
||||
container_name: sogo_httpd
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- sogo
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
volumes:
|
||||
- ./conf/httpd/ssl:/ssl
|
||||
- ./conf/httpd/CanadaHolidays.ics:/usr/local/apache2/htdocs/CanadaHolidays.ics
|
||||
- sogo-static-files:/usr/local/lib/GNUstep
|
||||
mariadb:
|
||||
image: mariadb:11
|
||||
container_name: sogo_mariadb
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
MYSQL_DATABASE: db
|
||||
ports:
|
||||
- 3307:3306
|
||||
volumes:
|
||||
- sogo-mariadb:/var/lib/mysql
|
||||
- ./conf/db/mysql.sql:/docker-entrypoint-initdb.d/init-sogo.sql
|
||||
openldap:
|
||||
image: bitnami/openldap:2.5
|
||||
container_name: sogo_openldap
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "389:389"
|
||||
environment:
|
||||
LDAP_PORT_NUMBER: 389
|
||||
LDAP_ADMIN_USERNAME: "admin"
|
||||
LDAP_ADMIN_PASSWORD: "password"
|
||||
LDAP_BIND_PASSWORD: "password"
|
||||
LDAP_ROOT: "dc=example,dc=org"
|
||||
LDAP_ADMIN_DN: "cn=admin,dc=example,dc=org"
|
||||
LDAP_CUSTOM_LDIF_DIR: "/ldifs"
|
||||
#BITNAMI_DEBUG: true
|
||||
volumes:
|
||||
- ./conf/ldap/users.ldif:/ldifs/users.ldif # User data
|
||||
imapd: # Dovecot
|
||||
build:
|
||||
context: ./conf/dovecot/
|
||||
dockerfile: Dockerfile
|
||||
container_name: sogo_dovecot
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- openldap
|
||||
ports:
|
||||
- "143:143"
|
||||
- "993:993"
|
||||
- "4190:4190"
|
||||
- "24:24"
|
||||
volumes:
|
||||
- sogo-mail-data:/var/mail
|
||||
- sogo-sieve-data:/var/sieve
|
||||
smtpd: # Postfix
|
||||
build:
|
||||
context: ./conf/postfix/
|
||||
dockerfile: Dockerfile
|
||||
container_name: sogo_postfix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "25:25"
|
||||
depends_on:
|
||||
- imapd
|
||||
- openldap
|
||||
entrypoint: /entrypoint.sh
|
||||
sogo:
|
||||
container_name: sogo_dev
|
||||
restart: unless-stopped
|
||||
tty: true
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: .devcontainer/Dockerfile.devcontainer
|
||||
ports:
|
||||
- 50000:50000
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
depends_on:
|
||||
- openldap
|
||||
- memcached
|
||||
- mariadb
|
||||
- imapd
|
||||
- smtpd
|
||||
volumes:
|
||||
- ..:/workspace
|
||||
- sogo-static-files:/usr/local/lib/GNUstep
|
||||
- ./conf/sogo/sogo.conf:/etc/sogo/sogo.conf
|
||||
# - /path/to/sope_folder:/src/SOPE # Uncomment this line if you want to mount SOPE source code
|
||||
|
||||
volumes:
|
||||
sogo-mariadb:
|
||||
sogo-postgres:
|
||||
sogo-mail-data:
|
||||
sogo-sieve-data:
|
||||
sogo-static-files:
|
||||
@@ -0,0 +1,72 @@
|
||||
## Dev Container
|
||||
|
||||
The SOGo dev container will deploy a complete environnement to work and test with SOGo
|
||||
|
||||
This environnement contains:
|
||||
- a mariadb database
|
||||
- an imap server (dovecot) and a smtp server (postfix)
|
||||
- a ldap server for the user source
|
||||
- an Apache server
|
||||
- The SOGo instance
|
||||
|
||||
## How to use the devcontainer
|
||||
|
||||
Simply clone the source code of the SOGo repo and open id with Visual Code Studio. If this is not already the case, you will need the [Dev Container Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). Then, simply execute the command "New Dev COntainer" and VCS will start building it.
|
||||
|
||||
|
||||
When it's all build, go to http://127.0.0.1/SOGo. You have 3 users to login with:
|
||||
`sogo-tests1`, `sogo-tests2`, `sogo-tests3` with all the same password `sogo`
|
||||
|
||||
You can only send mail and receive mail within the domain `example.org`
|
||||
|
||||
## How to work on SOGo
|
||||
|
||||
|
||||
### To modify the source and build
|
||||
|
||||
After modifyng the source in VCS, in your container's terminalthere is a command tool `devenv` with following options for building:
|
||||
```
|
||||
-b, --build Build sogo app
|
||||
-ba, --build-all Clean, build sogo and sope
|
||||
-br, --build-resources Build only sogo JS/CSS resources
|
||||
```
|
||||
|
||||
To build sogo simplye does
|
||||
```shell
|
||||
sudo devenv -b
|
||||
```
|
||||
- The options -br will only build the front
|
||||
- The options -ba will also build [SOPE](https://github.com/Alinto/sope) (low-level of SOGo, needs to be build before SOGo)
|
||||
|
||||
|
||||
### To use the debugger
|
||||
|
||||
Inside the sogo container there is a tool to launch SOGo with the [debugger gdb](https://sourceware.org/gdb/).
|
||||
|
||||
Use one of the following command:
|
||||
|
||||
```shell
|
||||
sudo devenv -d
|
||||
sudo devenv --debug
|
||||
```
|
||||
|
||||
|
||||
### To change sogo.conf
|
||||
|
||||
The sogo.conf is linked to the one here [.devcontainer/conf/sogo/sogo.conf](conf/sogo/sogo.conf)
|
||||
|
||||
Modify it as you wish then you need to restart the sogo service inside the container:
|
||||
```shell
|
||||
service sogod restart
|
||||
```
|
||||
|
||||
### To modify SOPE
|
||||
|
||||
Sometimes, you may have to change SOPE source code. In order to do that:
|
||||
- First, clone the SOPE repo locally.
|
||||
- Uncomment line 101 of the docker-compose.yml file and put the correct path to your SOPE folder. It should end with 'sope' like this: `/home/myuser/github/sope`
|
||||
- Then, you have to rebuild your devcontainer.
|
||||
- Now you can modify the source of SOPE and build it by going inside the sogo_dev container and do.
|
||||
```shell
|
||||
sudo devenv -ba
|
||||
```
|
||||
+3
-1
@@ -55,4 +55,6 @@ Tests/results.xml
|
||||
Tests/results
|
||||
|
||||
# SAML2 autogenerated files
|
||||
SoObjects/SOGo/SOGoSAML2Exceptions.*
|
||||
SoObjects/SOGo/SOGoSAML2Exceptions.*
|
||||
|
||||
node_modules
|
||||
@@ -437,8 +437,8 @@ Defaults to `300`.
|
||||
|
||||
|S |SOGoAuthenticationType
|
||||
|Parameter used to define the way by which users will be authenticated.
|
||||
For C.A.S., specify `cas`. For SAML2, specify `saml2`. For anything
|
||||
else, leave that value empty.
|
||||
For C.A.S., specify `cas`. For SAML2, specify `saml2`. For opendID Connect, specify `openid`.
|
||||
For anything else, leave that value empty.
|
||||
|
||||
|S |SOGoTrustProxyAuthentication
|
||||
|Parameter used to set whether HTTP username should be trusted.
|
||||
@@ -477,6 +477,19 @@ example.
|
||||
|
||||
Defaults to `YES` when unset.
|
||||
|
||||
|S |SOGoCalendarEnableJitsiLink
|
||||
|Parameter allowing users to create Jitsi link when creating an event.
|
||||
Be aware that a least one of the attendees will need an account from a
|
||||
platform that Jitsi supports: Google, Facebook or Github
|
||||
|
||||
Defaults to `NO` when unset.
|
||||
|
||||
|D |SOGoCalendarJitsiBaseUrl
|
||||
|Only used if SOGoCalendarEnableJitsiLink is set to YES. String for the URL of
|
||||
the jitsi server used for creating the meeting link.
|
||||
|
||||
Defaults to `https://meet.jit.si` when unset.
|
||||
|
||||
|S |SOGoSAML2PrivateKeyLocation
|
||||
|The location of the SSL private key file on the filesystem that is used
|
||||
by SOGo to sign and encrypt communications with the SAML2 identity
|
||||
@@ -1546,6 +1559,81 @@ SOGoIMAPCASServiceName should be set to the actual imap service name
|
||||
expected by pam_cas, otherwise it will fail to authenticate incoming
|
||||
connection properly.
|
||||
|
||||
|
||||
Authenticating using OPENID
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
SOGo natively supports OPENID authentication. For activating OpenId authentication you need first to make sure that
|
||||
_SOGoAuthenticationType_ is set to `openid`,
|
||||
_SOGoXSRFValidationEnabled_ is set to `NO` and set the following parameters:
|
||||
|
||||
[cols="^4,46,50a"]
|
||||
|=======================================================================
|
||||
|S |OCSOpenIdURL
|
||||
|Parameter used to set the database URL for openID session.
|
||||
|
||||
For MariaDB/MySQL, set the database URL to something like: mysql://sogo:sogo@127.0.0.1:3306/sogo/sogo_openid.
|
||||
|
||||
|
||||
|S |SOGoOpenIdConfigUrl
|
||||
|Parameter used to specify the endpoint of OpenID Provider Configuration, mandatory. For example:
|
||||
https://myopenid.net/.well-known/openid-configuration
|
||||
|
||||
|S |SOGoOpenIdClient
|
||||
|Name of your openid client, mandatory.
|
||||
|
||||
|S |SOGoOpenIdClientSecret
|
||||
|Secret of your openid client, mandatory
|
||||
|
||||
|S |SOGoOpenIdScope
|
||||
|Scope or your openid client, mandatory. List of words space separated like this:
|
||||
"openid profile email"
|
||||
|
||||
|S |SOGoOpenIdEmailParam
|
||||
|Name of the parameter from user profile that contains the mail/uid.
|
||||
|
||||
Defaults to `email` when unset.
|
||||
|
||||
|S |SOGoOpenIdEnableRefreshToken
|
||||
|Set to `YES` to Enable the mechanism of refresh token if provided. You may have to configure
|
||||
and/or add a value to your scope for it to work.
|
||||
|
||||
Defaults to `NO` when unset.
|
||||
|
||||
|S |SOGoOpenIdTokenCheckInterval
|
||||
|Number of seconds before sogo check again the user's access token validity.
|
||||
This is to prevent sogo to do too much request to the openid server.
|
||||
|
||||
Defaults to `0` when unset.
|
||||
|
||||
|S |SOGoOpenIdLogoutEnabled
|
||||
|Allow user to end their openId with the webmail. Meaning that will disconnect them from
|
||||
the others applicaitons as well.
|
||||
|
||||
Defaults to `NO` when unset.
|
||||
|=======================================================================
|
||||
|
||||
|
||||
The tricky part shows up for the imap and smtp sever. SOGo doesn't know the password
|
||||
of the user and only have its access token. A new auth mechanism has been implemented,
|
||||
the https://developers.google.com/gmail/imap/xoauth2-protocol#initial_client_response[xoauth2]
|
||||
|
||||
You can set it with the parameter _NGImap4AuthMechanism_ and/or _SOGoSMTPAuthenticationType_
|
||||
|
||||
*With dovecot:* +
|
||||
Dovecot natively supports xoauth2 and can be figured as such: https://doc.dovecot.org/2.3/configuration_manual/authentication/oauth2/
|
||||
|
||||
*With cyrus:* +
|
||||
Cyrus doesn't support xoauth2 mechanism and pluggins or homemade solutions must be found. +
|
||||
_Please note, as Alinto uses dovecot, we didn't investigate cyrus' case. If one member of the community
|
||||
finds a solution, we will be happy to update this documentation._
|
||||
|
||||
As you can see, a new database table is used for handling openid session. The table is automaticcaly created when _OCSOpenIdURL_ is set.
|
||||
If the user quits the webmail without logging out or trough another application,
|
||||
the session will stays in the table and be useless. That's why a new sogo-tool command has been added to clean this table.
|
||||
You can put it in a cron to do that periodicly. +
|
||||
See _<<sogo-tool-clean-openid-sessions,sogo-tool clean-openid-sessions>>_.
|
||||
|
||||
Authenticating using SAML2
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1678,8 +1766,8 @@ URL could be set to something like:
|
||||
|
||||
See the "EMail reminders" section in this document for more information.
|
||||
|
||||
|S |SOGoDisableMailCleaning
|
||||
|Parameter used to disable the feature 'remove mails older than X days' introduced in 5.12. Default value is `NO`.
|
||||
|S |SOGoEnableMailCleaning
|
||||
|Parameter used to enable cleaning folders and mailbox (remove mail older than X days). Default value is `NO`.
|
||||
|
||||
|S |SOGoDisableOrganizerEventCheck
|
||||
|Parameter used to disable organizer's calendar event check
|
||||
@@ -2100,8 +2188,7 @@ To disable TLS verification for localhost domains, add
|
||||
|
||||
|D |SOGoSMTPAuthenticationType
|
||||
|Activate SMTP authentication and specifies which type is in use.
|
||||
Current, only `PLAIN` is supported and other values will cause
|
||||
the authentication to fail.
|
||||
Current, Are supported `PLAIN` and 'xoauth2' for openid.
|
||||
|
||||
|D |SOGoSMTPMasterUserEnabled
|
||||
|Enable specific SMTP user account for system e-mails (notifications, reminders, ...). Default is `NO`.
|
||||
@@ -2284,6 +2371,7 @@ SASL mechanism. Using `AUTHENTICATE` instead of `LOGIN` is also necessary
|
||||
to enable UTF-8 characters in users' passwords. To enable simple use of
|
||||
`AUTHENTICATE` for this purpose, set this setting to `plain`. Please note
|
||||
that this feature might be limited at this time.
|
||||
Now support `xoauth2` mechanism when using openid. Be sure you imap server undesrtands this mechanism.
|
||||
|
||||
|D |NGImap4ConnectionGroupIdPrefix
|
||||
|Prefix to prepend to names in IMAP ACL transactions, to indicate the
|
||||
@@ -2430,6 +2518,16 @@ appended to the user's vacation message. For example:
|
||||
|
||||
See `SOGoVacationHeaderTemplateFile` for available template variables.
|
||||
|
||||
|D |SOGoVacationAllowZeroDays
|
||||
|Parameter used to enable an option of 0 for the vacation days setting.
|
||||
A value of 0 means that auto response mails are sent for every incoming mail.
|
||||
|
||||
This option has to be supported by the IMAP host
|
||||
(see https://doc.dovecot.org/2.3/settings/pigeonhole-ext/vacation/#pigeonhole_setting-sieve_vacation_min_period
|
||||
for instructions regarding Dovecot/Pigeonhole).
|
||||
|
||||
Defaults to `NO` when unset.
|
||||
|
||||
|D |SOGoForwardEnabled
|
||||
|Parameter used to activate the edition from the preferences window of a
|
||||
forwarding email address. Requires Sieve script support on the IMAP
|
||||
@@ -3607,6 +3705,22 @@ sogo-tool checkup user1
|
||||
sogo-tool checkup -d user1
|
||||
----
|
||||
|
||||
|
||||
[[sogo-tool-clean-openid-sessions]]
|
||||
|
||||
sogo-tool clean-openid-sessions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Obviously only usefull if you have set SOGo with openId authentication.
|
||||
Will clean all expired openId sessions from the database.
|
||||
|
||||
sogo-tool clean-openid-sessions
|
||||
|
||||
Example:
|
||||
----
|
||||
sogo-tool clean-openid-sessions
|
||||
----
|
||||
|
||||
sogo-tool cleanup
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -3954,7 +4068,7 @@ _Defaults_ stores all parameters that can be found in Preferences panel. So it g
|
||||
autoreply to many others options.
|
||||
|
||||
|
||||
user-preferences get|set|unset defaults|settings user key [value|-f filename] [-p credentialFile]
|
||||
user-preferences get|set|unset defaults|settings user key [value|-f filename] [-p credentialFile [-F]]
|
||||
|
||||
* First argument is the action *get*, *set* or *unset*
|
||||
* Second argument is parameter type *settings* or *defaults*
|
||||
@@ -3962,7 +4076,8 @@ autoreply to many others options.
|
||||
* Fourth argument *key* is the name of the parameters where to make the action
|
||||
* If the action is *set*, you should provide either *value* as fifth argument or a file with the value inside with *-f* filename/path
|
||||
* If the action is *set* or *unset* and the *key* concerns a sieve script (filter, vacation, forward...), you
|
||||
should provide a credential file, a one-line file that contains "username:password" of an admin account of your imap/sieve server.
|
||||
should provide a credential file, a one-line file that contains "username:password" of an admin account of your imap/sieve server. If you
|
||||
want t oforce the sieve script activation add -F after.
|
||||
|
||||
As there is a lot of parameters, this documentation will not go into details for each one. To know what are
|
||||
the key names and their value you should got to your database in table defined by _SOGoProfileURL_ in your sogo.conf.
|
||||
@@ -3991,7 +4106,7 @@ sogo-tool user-preferences unset defaults user1 Forward -p /etc/sogo/cred
|
||||
|
||||
*set* action:
|
||||
----
|
||||
sogo-tool user-preferences set defaults user1 Forward '{"forwardAddress":["sogo-tests2@sogo.alinto"],"enabled":1,"keepCopy":0}' -p cred
|
||||
sogo-tool user-preferences set defaults user1 Forward '{"forwardAddress":["sogo-tests2@sogo.alinto"],"enabled":1,"keepCopy":0}' -p cred -F
|
||||
----
|
||||
or create a json file which contains _{"forwardAddress":["sogo-tests2@sogo.alinto"],"enabled":1,"keepCopy":0}_ then:
|
||||
----
|
||||
|
||||
+21
-13
@@ -288,16 +288,16 @@ static BOOL debugLeaks;
|
||||
|
||||
e = [urlStrings objectEnumerator];
|
||||
while (ok && (tmp = [e nextObject]))
|
||||
{
|
||||
value = [defaults stringForKey: tmp];
|
||||
if (value)
|
||||
[self _checkTableWithCM: cm tableURL: value andType: tmp];
|
||||
else
|
||||
{
|
||||
value = [defaults stringForKey: tmp];
|
||||
if (value)
|
||||
[self _checkTableWithCM: cm tableURL: value andType: tmp];
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"No value specified for '%@'", tmp];
|
||||
ok = NO;
|
||||
}
|
||||
[self errorWithFormat: @"No value specified for '%@'", tmp];
|
||||
ok = NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (combined)
|
||||
{
|
||||
@@ -322,9 +322,15 @@ static BOOL debugLeaks;
|
||||
|
||||
// Create the email alarms table, if required
|
||||
if ([defaults enableEMailAlarms])
|
||||
{
|
||||
[[fm alarmsFolder] createFolderIfNotExists];
|
||||
}
|
||||
{
|
||||
[[fm alarmsFolder] createFolderIfNotExists];
|
||||
}
|
||||
|
||||
//Create mandatory openId table, if used
|
||||
if([defaults hasOpenIdType])
|
||||
{
|
||||
[[fm openIdFolder] createFolderIfNotExists];
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
@@ -374,8 +380,10 @@ static BOOL debugLeaks;
|
||||
|
||||
request = [_ctx request];
|
||||
login = [SOGoUser getDecryptedUsernameIfNeeded: _key request: request];
|
||||
|
||||
user = [SOGoUser userWithLogin: login roles: nil];
|
||||
if([login isEqualToString: @"changeLanguage"])
|
||||
user = nil;
|
||||
else
|
||||
user = [SOGoUser userWithLogin: login roles: nil];
|
||||
if (user)
|
||||
userFolder = [$(@"SOGoUserFolder") objectWithName: login
|
||||
inContainer: self];
|
||||
|
||||
@@ -27,6 +27,10 @@ The source code of the SOGo Connector extension for Thunderbird 78+ can be obtai
|
||||
|
||||
Please refer to the [FAQ](https://sogo.nu/support.html#/faq) for [compilation instructions](https://sogo.nu/support/faq/how-do-i-compile-sogo.html).
|
||||
|
||||
## Dev Container
|
||||
|
||||
A dev container is available to use, read more there [read more there](.devcontainer/readme.md)
|
||||
|
||||
## Translations
|
||||
|
||||
SOGo and its associated components are available in various languages. The following list describes the official translations alongside their maintainers:
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
*/
|
||||
|
||||
@class NSString, NSArray, NSURL, NSDictionary, NSException;
|
||||
@class GCSChannelManager, GCSAlarmsFolder, GCSAdminFolder, GCSFolder, GCSFolderType, GCSSessionsFolder;
|
||||
@class GCSChannelManager, GCSAlarmsFolder, GCSAdminFolder, GCSFolder, GCSFolderType, GCSSessionsFolder, GCSOpenIdFolder;
|
||||
|
||||
@interface GCSFolderManager : NSObject
|
||||
{
|
||||
@@ -92,6 +92,9 @@
|
||||
/* admin */
|
||||
- (GCSAdminFolder *)adminFolder;
|
||||
|
||||
/* openid */
|
||||
- (GCSOpenIdFolder *) openIdFolder;
|
||||
|
||||
/* folder types */
|
||||
|
||||
- (GCSFolderType *)folderTypeWithName:(NSString *)_name;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#import "EOAdaptorChannel+GCS.h"
|
||||
#import "GCSAlarmsFolder.h"
|
||||
#import "GCSAdminFolder.h"
|
||||
#import "GCSOpenIdFolder.h"
|
||||
#import "GCSFolder.h"
|
||||
#import "GCSFolderType.h"
|
||||
#import "GCSSessionsFolder.h"
|
||||
@@ -504,6 +505,12 @@ static BOOL _singleStoreMode = NO;
|
||||
return [GCSAdminFolder adminFolderWithFolderManager: self];
|
||||
}
|
||||
|
||||
/* openId */
|
||||
- (GCSOpenIdFolder *) openIdFolder
|
||||
{
|
||||
return [GCSOpenIdFolder openIdFolderWithFolderManager: self];
|
||||
}
|
||||
|
||||
- (NSString *)generateSQLWhereForInternalNames:(NSArray *)_names
|
||||
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
|
||||
{
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/* GCSAdminFolder.h - this file is part of $PROJECT_NAME_HERE$
|
||||
*
|
||||
* Copyright (C) 2023 Alinto
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef GCSOPENIDFOLDER_H
|
||||
#define GCSOPENIDOLDER_H
|
||||
|
||||
|
||||
@class NSCalendarDate;
|
||||
@class NSException;
|
||||
@class NSNumber;
|
||||
@class NSString;
|
||||
|
||||
@class GCSFolderManager;
|
||||
|
||||
@interface GCSOpenIdFolder : NSObject
|
||||
{
|
||||
GCSFolderManager *folderManager;
|
||||
}
|
||||
|
||||
+ (id)openIdFolderWithFolderManager:(GCSFolderManager *)newFolderManager;
|
||||
|
||||
- (void) setFolderManager: (GCSFolderManager *) newFolderManager;
|
||||
|
||||
/* operations */
|
||||
|
||||
- (void) createFolderIfNotExists;
|
||||
- (BOOL) canConnectStore;
|
||||
|
||||
- (NSString *) getRefreshToken: (NSString *) _user_session useOldSession: (BOOL) use_old_session;
|
||||
- (NSString *) getNewToken: (NSString *) _old_session;
|
||||
- (NSException *) writeOpenIdSession: (NSString *) _user_session
|
||||
withOldSession: (NSString *) _old_session
|
||||
withRefreshToken: (NSString *) _refresh_token
|
||||
withExpire: (NSNumber *) _expire
|
||||
withRefreshExpire: (NSNumber *) _refresh_expire;
|
||||
- (NSException *) deleteOpenIdSessionFor: (NSString *) _user_session;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* GCSALARMSFOLDER_H */
|
||||
@@ -0,0 +1,363 @@
|
||||
/* GCSAdminFolder.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2023 Alinto
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
|
||||
#import <GDLAccess/EOAdaptorContext.h>
|
||||
#import <GDLAccess/EOAttribute.h>
|
||||
#import <GDLAccess/EOEntity.h>
|
||||
#import <GDLAccess/EOSQLQualifier.h>
|
||||
|
||||
#import "EOQualifier+GCS.h"
|
||||
#import "GCSChannelManager.h"
|
||||
#import "GCSFolderManager.h"
|
||||
#import "GCSSpecialQueries.h"
|
||||
#import "NSURL+GCS.h"
|
||||
|
||||
#import "GCSOpenIdFolder.h"
|
||||
|
||||
static NSString *openIdFolderURLString = nil;
|
||||
|
||||
#warning GCSOpenIdFolder should share a common ancestor with GCSFolder
|
||||
|
||||
@implementation GCSOpenIdFolder
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
NSUserDefaults *ud;
|
||||
|
||||
if (!openIdFolderURLString)
|
||||
{
|
||||
ud = [NSUserDefaults standardUserDefaults];
|
||||
ASSIGN (openIdFolderURLString, [ud stringForKey: @"OCSOpenIdURL"]);
|
||||
}
|
||||
}
|
||||
|
||||
+ (id) openIdFolderWithFolderManager: (GCSFolderManager *) newFolderManager
|
||||
{
|
||||
GCSOpenIdFolder *newFolder;
|
||||
|
||||
if (openIdFolderURLString)
|
||||
{
|
||||
newFolder = [self new];
|
||||
[newFolder autorelease];
|
||||
[newFolder setFolderManager: newFolderManager];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"'OCSOpenIdURL' is not set"];
|
||||
newFolder = nil;
|
||||
}
|
||||
|
||||
return newFolder;
|
||||
}
|
||||
|
||||
- (void) setFolderManager: (GCSFolderManager *) newFolderManager
|
||||
{
|
||||
ASSIGN (folderManager, newFolderManager);
|
||||
}
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (NSURL *) _location
|
||||
{
|
||||
NSURL *location;
|
||||
|
||||
if (openIdFolderURLString)
|
||||
location = [NSURL URLWithString: openIdFolderURLString];
|
||||
else
|
||||
{
|
||||
[self warnWithFormat: @"'OCSOpenIdURL' is not set"];
|
||||
location = nil;
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
- (GCSChannelManager *) _channelManager
|
||||
{
|
||||
return [folderManager channelManager];
|
||||
}
|
||||
|
||||
- (NSString *) _storeTableName
|
||||
{
|
||||
return [[self _location] gcsTableName];
|
||||
}
|
||||
|
||||
- (EOEntity *) _storeTableEntityForChannel: (EOAdaptorChannel *) tc
|
||||
{
|
||||
static EOEntity *entity = nil;
|
||||
EOAttribute *attribute;
|
||||
NSString *tableName;
|
||||
NSString *columns[] = {@"c_user_session", @"c_old_session", @"c_session_started", @"c_refresh_token", @"c_access_token_expires_in", @"c_refresh_token_expires_in", nil };
|
||||
NSString **column;
|
||||
NSMutableArray *keys;
|
||||
NSDictionary *types;
|
||||
|
||||
if (!entity)
|
||||
{
|
||||
entity = [EOEntity new];
|
||||
tableName = [self _storeTableName];
|
||||
[entity setName: tableName];
|
||||
[entity setExternalName: tableName];
|
||||
|
||||
types = [[tc specialQueries] openIdAttributeTypes];
|
||||
|
||||
column = columns;
|
||||
while (*column)
|
||||
{
|
||||
attribute = [EOAttribute new];
|
||||
[attribute setName: *column];
|
||||
[attribute setColumnName: *column];
|
||||
[attribute setExternalType: [types objectForKey: *column]];
|
||||
[entity addAttribute: attribute];
|
||||
[attribute release];
|
||||
column++;
|
||||
}
|
||||
|
||||
keys = [NSMutableArray arrayWithCapacity: 1];
|
||||
[keys addObject: [entity attributeNamed: @"c_user_session"]];
|
||||
[entity setPrimaryKeyAttributes: keys];
|
||||
|
||||
keys = [NSMutableArray arrayWithCapacity: 5];
|
||||
[keys addObject: [entity attributeNamed: @"c_old_session"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_session_started"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_refresh_token"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_access_token_expires_in"]];
|
||||
[keys addObject: [entity attributeNamed: @"c_refresh_token_expires_in"]];
|
||||
[entity setClassProperties: keys];
|
||||
[entity setAttributesUsedForLocking: [NSArray array]];
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/* connection */
|
||||
|
||||
- (EOAdaptorChannel *) _acquireStoreChannel
|
||||
{
|
||||
return [[self _channelManager] acquireOpenChannelForURL: [self _location]];
|
||||
}
|
||||
|
||||
- (void) _releaseChannel: (EOAdaptorChannel *) _channel
|
||||
{
|
||||
[[self _channelManager] releaseChannel:_channel immediately: YES];
|
||||
}
|
||||
|
||||
- (BOOL) canConnectStore
|
||||
{
|
||||
return [[self _channelManager] canConnect:[self _location]];
|
||||
}
|
||||
|
||||
- (void) createFolderIfNotExists
|
||||
{
|
||||
EOAdaptorChannel *tc;
|
||||
NSString *sql, *tableName;
|
||||
GCSSpecialQueries *queries;
|
||||
|
||||
tc = [self _acquireStoreChannel];
|
||||
tableName = [self _storeTableName];
|
||||
|
||||
queries = [tc specialQueries];
|
||||
|
||||
sql = [NSString stringWithFormat: @"SELECT 1 FROM %@ WHERE 1 = 2", tableName];
|
||||
if ([tc evaluateExpressionX: sql])
|
||||
{
|
||||
sql = [queries createOpenIdFolderWithName: tableName];
|
||||
if (![tc evaluateExpressionX: sql])
|
||||
[self logWithFormat: @"openid folder table '%@' successfully created!", tableName];
|
||||
}
|
||||
else
|
||||
[tc cancelFetch];
|
||||
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
|
||||
/* operations */
|
||||
|
||||
/* table has the following fields:
|
||||
c_user_session varchar(255) NOT NULL,
|
||||
c_session_started int(11) NOT NULL,
|
||||
c_refresh_token varchar(4096) DEFAULT '',
|
||||
c_access_token_expires_in int(11) NOT NULL,
|
||||
c_refresh_token_expires_in int(11) DEFAULT NULL,
|
||||
*/
|
||||
|
||||
- (NSDictionary *) recordForSession: (NSString *) _user_session useOldSession: (BOOL) use_old_session
|
||||
{
|
||||
EOAdaptorChannel *tc;
|
||||
EOAdaptorContext *context;
|
||||
NSException *error;
|
||||
NSArray *attrs;
|
||||
NSDictionary *record;
|
||||
EOEntity *entity;
|
||||
EOSQLQualifier *qualifier;
|
||||
|
||||
record = nil;
|
||||
|
||||
tc = [self _acquireStoreChannel];
|
||||
if (tc)
|
||||
{
|
||||
context = [tc adaptorContext];
|
||||
entity = [self _storeTableEntityForChannel: tc];
|
||||
if(!use_old_session)
|
||||
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
|
||||
qualifierFormat: @"c_user_session='%@'", _user_session];
|
||||
else
|
||||
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
|
||||
qualifierFormat: @"c_old_session='%@'", _user_session];
|
||||
[qualifier autorelease];
|
||||
|
||||
[context beginTransaction];
|
||||
error = [tc selectAttributesX: [entity attributesUsedForFetch]
|
||||
describedByQualifier: qualifier
|
||||
fetchOrder: nil
|
||||
lock: NO];
|
||||
if (error)
|
||||
[self errorWithFormat:@"%s: cannot execute fetch: %@", __PRETTY_FUNCTION__, error];
|
||||
else
|
||||
{
|
||||
attrs = [tc describeResults: NO];
|
||||
record = [tc fetchAttributes: attrs withZone: NULL];
|
||||
[tc cancelFetch];
|
||||
}
|
||||
[context rollbackTransaction];
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
- (NSString *) getRefreshToken: (NSString *) _user_session
|
||||
{
|
||||
NSDictionary *r;
|
||||
|
||||
r = [self recordForSession: _user_session useOldSession: NO];
|
||||
if (r && [r objectForKey:@"c_refresh_token"])
|
||||
return [r objectForKey:@"c_refresh_token"];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) getNewToken: (NSString *) _old_session
|
||||
{
|
||||
NSDictionary *r;
|
||||
r = [self recordForSession: _old_session useOldSession: YES];
|
||||
if (r && [r objectForKey:@"c_user_session"])
|
||||
return [r objectForKey:@"c_user_session"];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSException *) writeOpenIdSession: (NSString *) _user_session
|
||||
withOldSession: (NSString *) _old_session
|
||||
withRefreshToken: (NSString *) _refresh_token
|
||||
withExpire: (NSNumber *) _expire
|
||||
withRefreshExpire: (NSNumber *) _refresh_expire
|
||||
{
|
||||
NSDictionary *record, *newRecord;
|
||||
NSException *error;
|
||||
NSCalendarDate *nowDate;
|
||||
int now, nowExpire, nowRefreshExpire;
|
||||
EOAdaptorChannel *tc;
|
||||
EOAdaptorContext *context;
|
||||
EOEntity *entity;
|
||||
EOSQLQualifier *qualifier;
|
||||
|
||||
error = nil;
|
||||
tc = [self _acquireStoreChannel];
|
||||
if (tc)
|
||||
{
|
||||
context = [tc adaptorContext];
|
||||
|
||||
nowDate = [NSCalendarDate date];
|
||||
now = (nowDate ? (int)[nowDate timeIntervalSince1970] : 0);
|
||||
nowExpire = now + [_expire intValue];
|
||||
if(_refresh_expire)
|
||||
nowRefreshExpire = now + [_refresh_expire intValue];
|
||||
else
|
||||
nowRefreshExpire = -1;
|
||||
if(!_old_session)
|
||||
_old_session = @"";
|
||||
|
||||
newRecord = [NSDictionary dictionaryWithObjectsAndKeys: _user_session, @"c_user_session",
|
||||
_old_session, @"c_old_session",
|
||||
[NSNumber numberWithInt:now], @"c_session_started",
|
||||
_refresh_token, @"c_refresh_token",
|
||||
[NSNumber numberWithInt:nowExpire] , @"c_access_token_expires_in",
|
||||
[NSNumber numberWithInt:nowRefreshExpire] , @"c_refresh_token_expires_in",
|
||||
nil];
|
||||
record = [self recordForSession: _user_session useOldSession: NO];
|
||||
entity = [self _storeTableEntityForChannel: tc];
|
||||
[context beginTransaction];
|
||||
if (!record)
|
||||
{
|
||||
//If the session already exist no need to update it as it is unique
|
||||
error = [tc insertRowX: newRecord forEntity: entity];
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
[context rollbackTransaction];
|
||||
[self errorWithFormat:@"%s: cannot write record: %@", __PRETTY_FUNCTION__, error];
|
||||
}
|
||||
else
|
||||
[context commitTransaction];
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
- (NSException *) deleteOpenIdSessionFor: (NSString *) _user_session
|
||||
{
|
||||
EOAdaptorChannel *tc;
|
||||
EOAdaptorContext *context;
|
||||
EOEntity *entity;
|
||||
EOSQLQualifier *qualifier;
|
||||
NSException *error;
|
||||
|
||||
error = nil;
|
||||
tc = [self _acquireStoreChannel];
|
||||
if (tc)
|
||||
{
|
||||
context = [tc adaptorContext];
|
||||
entity = [self _storeTableEntityForChannel: tc];
|
||||
qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
|
||||
qualifierFormat: @"c_user_session='%@'",_user_session];
|
||||
[qualifier autorelease];
|
||||
[context beginTransaction];
|
||||
error = [tc deleteRowsDescribedByQualifierX: qualifier];
|
||||
if (error)
|
||||
{
|
||||
[context rollbackTransaction];
|
||||
[self errorWithFormat:@"%s: cannot delete record: %@", __PRETTY_FUNCTION__, error];
|
||||
}
|
||||
else
|
||||
[context commitTransaction];
|
||||
[self _releaseChannel: tc];
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -42,6 +42,9 @@
|
||||
- (NSString *)createAdminFolderWithName:(NSString *)tableName;
|
||||
- (NSDictionary *)adminAttributeTypes;
|
||||
|
||||
- (NSString *)createOpenIdFolderWithName:(NSString *)tableName;
|
||||
- (NSDictionary *)openIdAttributeTypes;
|
||||
|
||||
- (NSString *) updateCPathInFolderInfo:(NSString *)tableName
|
||||
withCPath2:(NSString *)c_path2;
|
||||
|
||||
|
||||
@@ -95,6 +95,21 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) createOpenIdFolderWithName: (NSString *) tableName
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *) openIdAttributeTypes
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) createFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
[self subclassResponsibility: _cmd];
|
||||
@@ -195,6 +210,38 @@
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createOpenIdFolderWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_user_session VARCHAR(4096) NOT NULL,"
|
||||
@" c_old_session VARCHAR(4096) NULL,"
|
||||
@" c_session_started INT4 NOT NULL,"
|
||||
@" c_refresh_token VARCHAR(4096) NULL,"
|
||||
@" c_access_token_expires_in INT4 NOT NULL,"
|
||||
@" c_refresh_token_expires_in INT4 NULL)");
|
||||
|
||||
return [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSDictionary *) openIdAttributeTypes
|
||||
{
|
||||
static NSMutableDictionary *types = nil;
|
||||
|
||||
if (!types)
|
||||
{
|
||||
types = [NSMutableDictionary new];
|
||||
[types setObject: @"varchar" forKey: @"c_user_session"];
|
||||
[types setObject: @"varchar" forKey: @"c_old_session"];
|
||||
[types setObject: @"int" forKey: @"c_session_started"];
|
||||
[types setObject: @"varchar" forKey: @"c_refresh_token"];
|
||||
[types setObject: @"int" forKey: @"c_access_token_expires_in"];
|
||||
[types setObject: @"int" forKey: @"c_refresh_token_expires_in"];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
@@ -324,6 +371,38 @@
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createOpenIdFolderWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_user_session VARCHAR(4096) NOT NULL,"
|
||||
@" c_old_session VARCHAR(4096) NULL,"
|
||||
@" c_session_started INT4 NOT NULL,"
|
||||
@" c_refresh_token VARCHAR(4096) NULL,"
|
||||
@" c_access_token_expires_in INT4 NOT NULL,"
|
||||
@" c_refresh_token_expires_in INT4 NULL)");
|
||||
|
||||
return [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSDictionary *) openIdAttributeTypes
|
||||
{
|
||||
static NSMutableDictionary *types = nil;
|
||||
|
||||
if (!types)
|
||||
{
|
||||
types = [NSMutableDictionary new];
|
||||
[types setObject: @"varchar" forKey: @"c_user_session"];
|
||||
[types setObject: @"varchar" forKey: @"c_old_session"];
|
||||
[types setObject: @"int" forKey: @"c_session_started"];
|
||||
[types setObject: @"varchar" forKey: @"c_refresh_token"];
|
||||
[types setObject: @"int" forKey: @"c_access_token_expires_in"];
|
||||
[types setObject: @"int" forKey: @"c_refresh_token_expires_in"];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
@@ -453,6 +532,38 @@
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createOpenIdFolderWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_user_session VARCHAR2(4096) NOT NULL,"
|
||||
@" c_old_session VARCHAR2(4096) NULL,"
|
||||
@" c_session_started INTEGER NOT NULL,"
|
||||
@" c_refresh_token VARCHAR2(4096) NULL,"
|
||||
@" c_access_token_expires_in INTEGER NOT NULL,"
|
||||
@" c_refresh_token_expires_in INTEGER NULL)");
|
||||
|
||||
return [NSString stringWithFormat: sqlFolderFormat, tableName];
|
||||
}
|
||||
|
||||
- (NSDictionary *) openIdAttributeTypes
|
||||
{
|
||||
static NSMutableDictionary *types = nil;
|
||||
|
||||
if (!types)
|
||||
{
|
||||
types = [NSMutableDictionary new];
|
||||
[types setObject: @"varchar2" forKey: @"c_user_session"];
|
||||
[types setObject: @"varchar2" forKey: @"c_old_session"];
|
||||
[types setObject: @"integer" forKey: @"c_session_started"];
|
||||
[types setObject: @"varchar2" forKey: @"c_refresh_token"];
|
||||
[types setObject: @"integer" forKey: @"c_access_token_expires_in"];
|
||||
[types setObject: @"integer" forKey: @"c_refresh_token_expires_in"];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
- (NSString *) createFolderTableWithName: (NSString *) tableName
|
||||
{
|
||||
static NSString *sqlFolderFormat
|
||||
|
||||
@@ -28,13 +28,14 @@ libGDLContentStore_HEADER_FILES += \
|
||||
GCSAdminFolder.h \
|
||||
GCSContext.h \
|
||||
GCSFieldInfo.h \
|
||||
GCSFolder.h \
|
||||
GCSFolder.h \
|
||||
GCSFolderManager.h \
|
||||
GCSFolderType.h \
|
||||
GCSChannelManager.h \
|
||||
GCSOpenIdFolder.h \
|
||||
GCSSessionsFolder.h \
|
||||
GCSSpecialQueries.h \
|
||||
GCSStringFormatter.h \
|
||||
GCSStringFormatter.h \
|
||||
|
||||
libGDLContentStore_OBJC_FILES += \
|
||||
NSURL+GCS.m \
|
||||
@@ -45,13 +46,14 @@ libGDLContentStore_OBJC_FILES += \
|
||||
GCSAdminFolder.m \
|
||||
GCSContext.m \
|
||||
GCSFieldInfo.m \
|
||||
GCSFolder.m \
|
||||
GCSFolder.m \
|
||||
GCSFolderManager.m \
|
||||
GCSFolderType.m \
|
||||
GCSChannelManager.m \
|
||||
GCSOpenIdFolder.m \
|
||||
GCSSessionsFolder.m \
|
||||
GCSSpecialQueries.m \
|
||||
GCSStringFormatter.m \
|
||||
GCSStringFormatter.m \
|
||||
|
||||
# framework support
|
||||
|
||||
|
||||
@@ -464,7 +464,8 @@ lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange: (NGCalendarD
|
||||
}
|
||||
}
|
||||
|
||||
if ([self hasRecurrenceDates])
|
||||
//If there are recurrence rules and we have no date yet, its means it's infinite and rdates shouldn't be count as last dates.
|
||||
if ([self hasRecurrenceDates] && !([self hasRecurrenceRules] && !date))
|
||||
{
|
||||
NSEnumerator *rDates;
|
||||
|
||||
|
||||
@@ -165,6 +165,16 @@ CREATE TABLE sogo_admin (
|
||||
PRIMARY KEY (c_key)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE sogo_opend_id (
|
||||
c_user_session varchar(4096) NOT NULL,
|
||||
c_old_session varchar(4096) DEFAULT '',
|
||||
c_session_started int(11) NOT NULL,
|
||||
c_refresh_token varchar(4096) DEFAULT '',
|
||||
c_access_token_expires_in int(11) NOT NULL,
|
||||
c_refresh_token_expires_in int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (c_user_session)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE sogo_user_profile (
|
||||
c_uid varchar(255) NOT NULL,
|
||||
c_defaults longtext,
|
||||
|
||||
@@ -1760,7 +1760,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
{
|
||||
NSCalendarDate *now, *rc;
|
||||
|
||||
if (davCalendarStartTimeLimit > 0 && ![[context activeUser] isSuperUser])
|
||||
if ([[context request] isSoWebDAVRequest] && davCalendarStartTimeLimit > 0 && ![[context activeUser] isSuperUser])
|
||||
{
|
||||
now = [NSCalendarDate date];
|
||||
rc = [now addTimeInterval: -davTimeLimitSeconds];
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#import <NGCards/iCalEvent.h>
|
||||
#import <NGCards/iCalDateTime.h>
|
||||
#import <NGCards/iCalRepeatableEntityObject.h>
|
||||
|
||||
#import "iCalCalendar+SOGo.h"
|
||||
@@ -97,7 +98,29 @@
|
||||
elements = [self allObjects];
|
||||
count = [elements count];
|
||||
if (count)
|
||||
{
|
||||
element = [elements objectAtIndex: 0];
|
||||
if(count >1)
|
||||
{
|
||||
//If there more than one event, it may because there is the main reccurent event and some exceptions with reccurenceId
|
||||
//It's important to fetch the "main" vevent and not the other.
|
||||
NSEnumerator *elementEnum;
|
||||
NSCalendarDate *recurrenceId;
|
||||
CardGroup *elem;
|
||||
elementEnum = [elements objectEnumerator];
|
||||
while((elem = [elementEnum nextObject]))
|
||||
{
|
||||
recurrenceId = [(iCalDateTime *) [elem uniqueChildWithTag: @"recurrence-id"] dateTime];
|
||||
if(recurrenceId)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
element = elem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//NSLog(@"ERROR: given calendar contains no elements: %@", self);
|
||||
|
||||
@@ -379,7 +379,7 @@ NSNumber *iCalDistantFutureNumber = nil;
|
||||
currentAttendee = [attendees nextObject];
|
||||
while (!isAttendee
|
||||
&& currentAttendee)
|
||||
if ([user hasEmail: [currentAttendee rfc822Email]])
|
||||
if ([user hasEmail: [currentAttendee rfc822Email]] && ![self isOrganizer:[currentAttendee rfc822Email]]) // Exclude organizer if in attendees
|
||||
isAttendee = YES;
|
||||
else
|
||||
currentAttendee = [attendees nextObject];
|
||||
|
||||
@@ -132,14 +132,24 @@
|
||||
|
||||
- (NGImap4ConnectionManager *) mailManager
|
||||
{
|
||||
return [NGImap4ConnectionManager defaultConnectionManager];
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *imapAuthMech, *domain;
|
||||
|
||||
domain = [[[self context] activeUser] loginDomain];
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if([sd doesLoginTypeByDomain])
|
||||
imapAuthMech = [sd getImapAuthMechForDomain: domain];
|
||||
else
|
||||
imapAuthMech = nil;
|
||||
|
||||
return [NGImap4ConnectionManager defaultConnectionManager: imapAuthMech];
|
||||
}
|
||||
|
||||
- (NGImap4Connection *) _createIMAP4Connection
|
||||
{
|
||||
NGImap4ConnectionManager *manager;
|
||||
NGImap4Connection *newConnection;
|
||||
NSString *password;
|
||||
NSString *password, *domain;
|
||||
NGInternetSocketAddress *host;
|
||||
SOGoSystemDefaults *sd;
|
||||
BOOL usesSSO;
|
||||
@@ -152,12 +162,15 @@
|
||||
host = [NGInternetSocketAddress addressWithPort:0 onHost:[[self imap4URL] host]];
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
usesSSO = [[sd authenticationType] isEqualToString: @"cas"] || [[sd authenticationType] isEqualToString: @"saml2"];
|
||||
|
||||
domain = [[[self context] activeUser] loginDomain];
|
||||
usesSSO = [sd isSsoUsed: domain];
|
||||
|
||||
if (![[[self mailAccountFolder] nameInContainer] isEqualToString: @"0"] &&
|
||||
usesSSO &&
|
||||
[host isLocalhost])
|
||||
{
|
||||
//
|
||||
[self errorWithFormat: @"Trying to use localhost for additional IMAP account - aborting."];
|
||||
return nil;
|
||||
}
|
||||
@@ -227,19 +240,17 @@
|
||||
login = [[[self context] activeUser] login];
|
||||
|
||||
if (!login)
|
||||
login = [[[[self container] context] activeUser] login];
|
||||
login = [[[[self container] context] activeUser] login];
|
||||
|
||||
cacheKey = [NSString stringWithFormat: @"%@+%@",
|
||||
login,
|
||||
[[self mailAccountFolder] nameInContainer]];
|
||||
cacheKey = [NSString stringWithFormat: @"%@+%@", login, [[self mailAccountFolder] nameInContainer]];
|
||||
imap4 = [sogoCache imap4ConnectionForKey: cacheKey];
|
||||
if (!imap4)
|
||||
{
|
||||
imap4 = [self _createIMAP4Connection];
|
||||
[sogoCache registerIMAP4Connection: imap4
|
||||
forKey: cacheKey];
|
||||
}
|
||||
[imap4 retain];
|
||||
{
|
||||
imap4 = [self _createIMAP4Connection];
|
||||
[sogoCache registerIMAP4Connection: imap4
|
||||
forKey: cacheKey];
|
||||
}
|
||||
[imap4 retain];
|
||||
}
|
||||
|
||||
// Connection broken, try to reconnect
|
||||
|
||||
@@ -953,7 +953,10 @@ static BOOL debugSoParts = NO;
|
||||
body = [NSData data];
|
||||
|
||||
currentAttachment = [NSMutableDictionary dictionaryWithDictionary: currentInfo];
|
||||
[currentAttachment setObject: body forKey: @"body"];
|
||||
if (body)
|
||||
[currentAttachment setObject: body forKey: @"body"];
|
||||
else
|
||||
[self errorWithFormat: @"Failed add attachement %@, empty body - failed to decode : %@", currentPath, currentInfo];
|
||||
[attachments addObject: currentAttachment];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ SOGo_HEADER_FILES = \
|
||||
SOGoAuthenticator.h \
|
||||
SOGoSession.h \
|
||||
SOGoCASSession.h \
|
||||
SOGoOpenIdSession.h \
|
||||
SOGoDAVAuthenticator.h \
|
||||
SOGoProxyAuthenticator.h \
|
||||
SOGoStaticAuthenticator.h \
|
||||
@@ -161,6 +162,7 @@ SOGo_OBJC_FILES = \
|
||||
\
|
||||
SOGoSession.m \
|
||||
SOGoCASSession.m \
|
||||
SOGoOpenIdSession.m \
|
||||
SOGoDAVAuthenticator.m \
|
||||
SOGoProxyAuthenticator.m \
|
||||
SOGoStaticAuthenticator.m \
|
||||
|
||||
@@ -17,6 +17,7 @@ SOGo_LIBRARIES_DEPEND_UPON += \
|
||||
-Wl,--no-as-needed \
|
||||
-L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \
|
||||
-lmemcached \
|
||||
-lcurl \
|
||||
-lGDLAccess \
|
||||
-lNGObjWeb \
|
||||
-lNGCards \
|
||||
|
||||
@@ -1429,11 +1429,22 @@ groupObjectClasses: (NSArray *) newGroupObjectClasses
|
||||
NSMutableArray *contacts;
|
||||
EOQualifier *qualifier;
|
||||
unsigned int i;
|
||||
NSMutableString *s;
|
||||
NSString *sortAttribute;
|
||||
BOOL sortReverse;
|
||||
|
||||
contacts = [NSMutableArray array];
|
||||
|
||||
if(theDomain != nil && [theDomain length] > 0)
|
||||
{
|
||||
if ([_pristineBaseDN rangeOfString: @"%d"].location != NSNotFound)
|
||||
{
|
||||
s = [NSMutableString stringWithString: _pristineBaseDN];
|
||||
[s replaceOccurrencesOfString: @"%d" withString: theDomain options: 0 range: NSMakeRange(0, [s length])];
|
||||
ASSIGN(_baseDN, s);
|
||||
}
|
||||
}
|
||||
|
||||
if ([match length] > 0 || !_listRequiresDot)
|
||||
{
|
||||
ldapConnection = [self _ldapConnection];
|
||||
@@ -2290,8 +2301,7 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
|
||||
NSRange r;
|
||||
|
||||
r = [theLogin rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound &&
|
||||
[_pristineBaseDN rangeOfString: @"%d"].location != NSNotFound)
|
||||
if (r.location != NSNotFound && [_pristineBaseDN rangeOfString: @"%d"].location != NSNotFound)
|
||||
{
|
||||
s = [NSMutableString stringWithString: _pristineBaseDN];
|
||||
[s replaceOccurrencesOfString: @"%d" withString: [theLogin substringFromIndex: r.location+1] options: 0 range: NSMakeRange(0, [s length])];
|
||||
@@ -2471,4 +2481,4 @@ _makeLDAPChanges (NGLdapConnection *ldapConnection,
|
||||
return rc;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
@@ -95,6 +95,7 @@
|
||||
return keysWithFormat;
|
||||
}
|
||||
|
||||
|
||||
- (NSComparisonResult) caseInsensitiveDisplayNameCompare: (NSDictionary *) theDictionary
|
||||
{
|
||||
return [[self objectForKey: @"cn"] caseInsensitiveCompare: [theDictionary objectForKey: @"cn"]];
|
||||
|
||||
@@ -80,7 +80,10 @@ static int cssEscapingCount;
|
||||
{
|
||||
hostR = [self rangeOfString: @"://"];
|
||||
locationR = [[self substringFromIndex: (hostR.location + hostR.length)] rangeOfString: @"/"];
|
||||
newURL = [self substringFromIndex: (hostR.location + hostR.length + locationR.location)];
|
||||
if(locationR.location != NSNotFound)
|
||||
newURL = [self substringFromIndex: (hostR.location + hostR.length + locationR.location)];
|
||||
else
|
||||
newURL = @"";
|
||||
}
|
||||
|
||||
return newURL;
|
||||
|
||||
@@ -119,6 +119,13 @@
|
||||
|
||||
- (void) removeCASSessionWithTicket: (NSString *) ticket;
|
||||
|
||||
// OpenId Support
|
||||
- (NSString *) openIdSessionFromServer: (NSString *) endpoint;
|
||||
- (void) setOpenIdSession: (NSString *) openIdSession
|
||||
forServer: (NSString *) endpoint;
|
||||
- (NSString *) userOpendIdSessionFromServer: (NSString *) endpoint;
|
||||
- (void) setUserOpendIdSessionFromServer: (NSString *) endpoint
|
||||
nextCheckAfter: (NSString *) nextCheck;
|
||||
//
|
||||
// SAML2 support
|
||||
//
|
||||
|
||||
@@ -683,6 +683,32 @@ static memcached_st *handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// OpenId support
|
||||
|
||||
- (NSString *) openIdSessionFromServer: (NSString *) endpoint
|
||||
{
|
||||
return [self valueForKey: [NSString stringWithFormat: @"openId:%@", endpoint]];
|
||||
}
|
||||
|
||||
- (void) setOpenIdSession: (NSString *) openIdSession
|
||||
forServer: (NSString *) endpoint
|
||||
{
|
||||
[self setValue: openIdSession
|
||||
forKey: [NSString stringWithFormat: @"openId:%@", endpoint]];
|
||||
}
|
||||
|
||||
- (NSString *) userOpendIdSessionFromServer: (NSString *) identifier
|
||||
{
|
||||
return [self valueForKey: [NSString stringWithFormat: @"userOpenId:%@", identifier]];
|
||||
}
|
||||
- (void) setUserOpendIdSessionFromServer: (NSString *) identifier
|
||||
nextCheckAfter: (NSString *) nextCheck
|
||||
{
|
||||
[self setValue: nextCheck
|
||||
forKey: [NSString stringWithFormat: @"userOpenId:%@", identifier]];
|
||||
}
|
||||
|
||||
//
|
||||
// SAML2 support
|
||||
//
|
||||
|
||||
@@ -65,7 +65,8 @@
|
||||
domain: &domain
|
||||
perr: &perr
|
||||
expire: &expire
|
||||
grace: &grace]
|
||||
grace: &grace
|
||||
additionalInfo: nil]
|
||||
&& perr == PolicyNoError);
|
||||
|
||||
if (!rc)
|
||||
@@ -140,6 +141,7 @@
|
||||
return password;
|
||||
}
|
||||
|
||||
|
||||
/* create SOGoUser */
|
||||
|
||||
- (SOGoUser *) userInContext: (WOContext *)_ctx
|
||||
|
||||
@@ -62,6 +62,7 @@ extern NSString *SOGoDefaultsSourceUnmutableSource;
|
||||
|
||||
- (void) setBool: (BOOL) value forKey: (NSString *) key;
|
||||
- (BOOL) boolForKey: (NSString *) key;
|
||||
- (BOOL) boolForKey: (NSString *) key andDict: (NSDictionary*) _dict;
|
||||
|
||||
- (void) setFloat: (float) value forKey: (NSString *) key;
|
||||
- (float) floatForKey: (NSString *) key;
|
||||
|
||||
@@ -164,6 +164,29 @@ static Class NSStringKlass = Nil;
|
||||
return value;
|
||||
}
|
||||
|
||||
- (BOOL) boolForKey: (NSString *) key andDict: (NSDictionary*) _dict
|
||||
{
|
||||
id boolForKey;
|
||||
BOOL value;
|
||||
|
||||
boolForKey = [_dict objectForKey: key];
|
||||
if (boolForKey)
|
||||
{
|
||||
if ([boolForKey respondsToSelector: @selector (boolValue)])
|
||||
value = [boolForKey boolValue];
|
||||
else
|
||||
{
|
||||
[self warnWithFormat: @"expected a boolean for '%@' (ignored)",
|
||||
key];
|
||||
value = NO;
|
||||
}
|
||||
}
|
||||
else
|
||||
value = NO;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void) setFloat: (float) value
|
||||
forKey: (NSString *) key
|
||||
{
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
- (NSString *) vacationDefaultSubject;
|
||||
- (NSString *) vacationHeaderTemplateFile;
|
||||
- (NSString *) vacationFooterTemplateFile;
|
||||
- (BOOL) vacationAllowZeroDays;
|
||||
- (NSString *) mailingMechanism;
|
||||
- (NSString *) smtpServer;
|
||||
- (BOOL)smtpMasterUserEnabled;
|
||||
@@ -77,6 +78,7 @@
|
||||
- (BOOL) appointmentSendEMailNotifications;
|
||||
- (BOOL) foldersSendEMailNotifications;
|
||||
- (NSArray *) calendarDefaultRoles;
|
||||
- (NSString *) calendarJistiBaseUrl;
|
||||
- (NSArray *) contactsDefaultRoles;
|
||||
- (NSArray *) refreshViewIntervals;
|
||||
- (NSString *) subscriptionFolderFormat;
|
||||
|
||||
@@ -169,6 +169,16 @@
|
||||
return [self stringArrayForKey: @"SOGoCalendarDefaultRoles"];
|
||||
}
|
||||
|
||||
- (NSString *) calendarJistiBaseUrl
|
||||
{
|
||||
NSString *jitsiBaseUrl;
|
||||
jitsiBaseUrl = [self stringForKey: @"SOGoCalendarJitsiBaseUrl"];
|
||||
if(!jitsiBaseUrl)
|
||||
jitsiBaseUrl = @"https://meet.jit.si";
|
||||
|
||||
return jitsiBaseUrl;
|
||||
}
|
||||
|
||||
- (NSArray *) contactsDefaultRoles
|
||||
{
|
||||
return [self stringArrayForKey: @"SOGoContactsDefaultRoles"];
|
||||
@@ -267,6 +277,18 @@
|
||||
return [self stringForKey: @"SOGoVacationFooterTemplateFile"];
|
||||
}
|
||||
|
||||
- (BOOL) vacationAllowZeroDays
|
||||
{
|
||||
id o;
|
||||
|
||||
o = [self stringForKey: @"SOGoVacationAllowZeroDays"];
|
||||
|
||||
if (o)
|
||||
return [o boolValue];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *) mailingMechanism
|
||||
{
|
||||
NSString *mailingMechanism;
|
||||
|
||||
@@ -1531,6 +1531,11 @@ static NSArray *childRecordFields = nil;
|
||||
return valid;
|
||||
}
|
||||
|
||||
- (NSCalendarDate *) _getMaxStartDate
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
/**
|
||||
DAV:sync-collection Report
|
||||
https://datatracker.ietf.org/doc/html/rfc6578#section-3.2
|
||||
@@ -1559,9 +1564,10 @@ static NSArray *childRecordFields = nil;
|
||||
propElement = [(NGDOMNodeWithChildren *) documentElement
|
||||
firstElementWithTag: @"prop" inNamespace: XMLNS_WEBDAV];
|
||||
properties = [self parseDAVRequestedProperties: propElement];
|
||||
|
||||
records = [self syncTokenFieldsWithProperties: properties
|
||||
matchingSyncToken: syncToken
|
||||
fromDate: nil
|
||||
fromDate: [self _getMaxStartDate]
|
||||
initialLoad: NO];
|
||||
|
||||
[self _appendComponentProperties: [properties allKeys]
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
@class NSArray;
|
||||
@class NSException;
|
||||
@class NSString;
|
||||
@class NSDictionary;
|
||||
|
||||
@class WOContext;
|
||||
@class SOGoDomainDefaults;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#import "NSString+Utilities.h"
|
||||
#import "SOGoStaticAuthenticator.h"
|
||||
#import "SOGoEmptyAuthenticator.h"
|
||||
#import "SOGoWebAuthenticator.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
#import "SOGoUser.h"
|
||||
#import "SOGoUserManager.h"
|
||||
@@ -265,7 +266,8 @@
|
||||
inContext: (WOContext *) woContext
|
||||
systemMessage: (BOOL) isSystemMessage
|
||||
{
|
||||
NSString *currentTo, *login, *password;
|
||||
NSString *currentTo, *login, *password, *encryption, *protocol, *server;
|
||||
NSString * smtpAuthMethod;
|
||||
NSDictionary *currentAcount;
|
||||
NSMutableArray *toErrors;
|
||||
NSEnumerator *addresses;
|
||||
@@ -273,6 +275,7 @@
|
||||
NSException *result;
|
||||
NSURL * smtpUrl;
|
||||
SOGoUser* user;
|
||||
SOGoSystemDefaults *sd;
|
||||
BOOL doSmtpAuth;
|
||||
|
||||
result = nil;
|
||||
@@ -288,12 +291,30 @@
|
||||
currentAcount = [[user mailAccounts] objectAtIndex: userId];
|
||||
|
||||
//Check if we do an smtp authentication
|
||||
doSmtpAuth = [authenticationType isEqualToString: @"plain"] && ![authenticator isKindOfClass: [SOGoEmptyAuthenticator class]];
|
||||
smtpAuthMethod = authenticationType;
|
||||
doSmtpAuth = ([smtpAuthMethod isEqualToString: @"plain"] || [smtpAuthMethod isEqualToString: @"xoauth2"])
|
||||
&& ![authenticator isKindOfClass: [SOGoEmptyAuthenticator class]];
|
||||
if(!doSmtpAuth && userId > 0)
|
||||
{
|
||||
doSmtpAuth = [currentAcount objectForKey: @"smtpAuth"] ? [[currentAcount objectForKey: @"smtpAuth"] boolValue] : NO;
|
||||
}
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if(userId == 0 && [sd doesLoginTypeByDomain] && [authenticator isKindOfClass: [SOGoWebAuthenticator class]])
|
||||
{
|
||||
//Check if the authentication depends on the domain, only for webmail requets not for dav...
|
||||
NSString *username, *_domain;
|
||||
NSRange r;
|
||||
|
||||
username = [currentAcount objectForKey: @"userName"];
|
||||
r = [username rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
_domain = [username substringFromIndex: r.location+1];
|
||||
smtpAuthMethod = [sd getSmtpAuthMechForDomain: _domain];
|
||||
}
|
||||
}
|
||||
|
||||
NS_DURING
|
||||
{
|
||||
[client connect];
|
||||
@@ -304,6 +325,7 @@
|
||||
{
|
||||
login = [currentAcount objectForKey: @"userName"];
|
||||
password = [currentAcount objectForKey: @"password"];
|
||||
smtpAuthMethod = "plain"; //Only support plain for auxiliary account
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -315,15 +337,27 @@
|
||||
getExternalLoginForUID: [[authenticator userInContext: woContext] loginInDomain]
|
||||
inDomain: [[authenticator userInContext: woContext] domain]];
|
||||
|
||||
password = [authenticator passwordInContext: woContext];
|
||||
encryption = [currentAcount objectForKey: @"encryption"];
|
||||
protocol = @"imap";
|
||||
if ([encryption isEqualToString: @"ssl"] || [encryption isEqualToString: @"tls"])
|
||||
protocol = @"imaps";
|
||||
server = [NSString stringWithFormat: @"%@://%@", protocol, [currentAcount objectForKey: @"serverName"]];
|
||||
if([authenticator isKindOfClass: [SOGoWebAuthenticator class]])
|
||||
password = [authenticator smtpPasswordInContext: woContext forURL: server];
|
||||
else
|
||||
{
|
||||
password = [authenticator passwordInContext: woContext];
|
||||
smtpAuthMethod = @"plain"; //is a dav or activesync request, only support plain method
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isSystemMessage
|
||||
&& ![[[SOGoUserManager sharedUserManager] getEmailForUID: [[authenticator userInContext: woContext] loginInDomain]] isEqualToString: sender]
|
||||
&& smtpMasterUserEnabled) {
|
||||
if (![client plainAuthenticateUser: smtpMasterUserUsername
|
||||
withPassword: smtpMasterUserPassword]) {
|
||||
if (![client authenticateUser: smtpMasterUserUsername
|
||||
withPassword: smtpMasterUserPassword
|
||||
withMethod: smtpAuthMethod]) {
|
||||
result = [NSException exceptionWithHTTPStatus: 500
|
||||
reason: @"cannot send message:"
|
||||
@" (smtp) authentication failure"];
|
||||
@@ -334,8 +368,9 @@
|
||||
{
|
||||
if ([login length] == 0
|
||||
|| [login isEqualToString: @"anonymous"]
|
||||
|| ![client plainAuthenticateUser: login
|
||||
withPassword: password])
|
||||
|| ![client authenticateUser: login
|
||||
withPassword: password
|
||||
withMethod: smtpAuthMethod])
|
||||
result = [NSException exceptionWithHTTPStatus: 500
|
||||
reason: @"cannot send message:"
|
||||
@" (smtp) authentication failure"];
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/* SOGoCASSession.h - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2000-2023 Alinto.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef SOGOOPENIDSESSION_H
|
||||
#define SOGOOPENIDSESSION_H
|
||||
|
||||
/* implementation of the OpenId Connect as required for a client/proxy:
|
||||
https://openid.net/developers/how-connect-works/ */
|
||||
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <SOGo/SOGoObject.h>
|
||||
|
||||
|
||||
@class NSString;
|
||||
@class NSMutableDictionary;
|
||||
@class NSURL;
|
||||
@class NSJSONSerialization;
|
||||
|
||||
|
||||
@interface SOGoOpenIdSession : SOGoObject
|
||||
{
|
||||
//For cache
|
||||
BOOL cacheUpdateNeeded;
|
||||
int userTokenInterval;
|
||||
|
||||
//From sogo.conf
|
||||
BOOL openIdSessionIsOK;
|
||||
NSString *openIdConfigUrl;
|
||||
NSString *openIdScope;
|
||||
NSString *openIdClient;
|
||||
NSString *openIdClientSecret;
|
||||
NSString *openIdEmailParam;
|
||||
BOOL openIdEnableRefreshToken;
|
||||
BOOL sendDomainInfo;
|
||||
|
||||
NSString *forDomain;
|
||||
|
||||
|
||||
//From request to well-known/configuration
|
||||
NSString *authorizationEndpoint;
|
||||
NSString *tokenEndpoint;
|
||||
NSString *introspectionEndpoint;
|
||||
NSString *userinfoEndpoint;
|
||||
NSString *endSessionEndpoint;
|
||||
NSString *revocationEndpoint;
|
||||
|
||||
//Access token
|
||||
NSString *accessToken;
|
||||
NSString *refreshToken;
|
||||
NSString *idToken;
|
||||
NSString *tokenType;
|
||||
NSNumber *expiresIn;
|
||||
NSNumber *refreshExpiresIn;
|
||||
}
|
||||
|
||||
+ (BOOL) checkUserConfig;
|
||||
+ (SOGoOpenIdSession *) OpenIdSession: (NSString *) _domain;
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithConfig: (NSDictionary *) _config;
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithToken: (NSString *) token domain: (NSString *) _domain;
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithTokenAndConfig: (NSString *) token config: (NSDictionary *) _config;
|
||||
+ (void) deleteValueForSessionKey: (NSString *) theSessionKey;
|
||||
|
||||
- (void) initialize;
|
||||
- (void) initializeWithConfig: (NSDictionary *) _config;
|
||||
- (BOOL) sessionIsOK;
|
||||
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
||||
method: (NSString *) method
|
||||
headers: (NSDictionary *) headers
|
||||
body: (NSData *) body;
|
||||
- (NSMutableDictionary *) fecthConfiguration: (NSString *) _domain;
|
||||
- (void) setAccessToken;
|
||||
- (NSString *) getRefreshToken;
|
||||
- (NSString *) getToken;
|
||||
- (NSString *) getCurrentToken;
|
||||
- (NSString *) loginUrl: (NSString *) oldLocation;
|
||||
- (NSMutableDictionary *) fetchToken: (NSString *) code redirect: (NSString *) oldLocation;
|
||||
|
||||
- (NSString *) logoutUrl;
|
||||
- (NSMutableDictionary *) fetchUserInfo;
|
||||
- (NSString *) _login;
|
||||
- (NSString *) login: (NSString *) identifier;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* SOGOOPENIDSESSION_H */
|
||||
@@ -0,0 +1,785 @@
|
||||
/* SOGoCASSession.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2010-2014 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <NGObjWeb/WOHTTPConnection.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
||||
|
||||
#import <GDLContentStore/GCSOpenIdFolder.h>
|
||||
#import <GDLContentStore/GCSFolderManager.h>
|
||||
|
||||
#import "NSDictionary+Utilities.h"
|
||||
#import "NSString+Utilities.h"
|
||||
#import "SOGoCache.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
|
||||
#import "SOGoOpenIdSession.h"
|
||||
|
||||
static BOOL SOGoOpenIDDebugEnabled = YES;
|
||||
|
||||
@implementation SOGoOpenIdSession
|
||||
|
||||
/// Check if all required parameters to set up a OpenIdSession are in sogo.conf
|
||||
|
||||
+ (BOOL) checkUserConfig
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
if(nil == [[GCSFolderManager defaultFolderManager] openIdFolder])
|
||||
{
|
||||
[self errorWithFormat: @"Something wrong with the table defined by OCSOpenIdURL"];
|
||||
return NO;
|
||||
}
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
return ([sd openIdConfigUrl] && [sd openIdScope] && [sd openIdClient] && [sd openIdClientSecret]);
|
||||
}
|
||||
|
||||
- (void) initializeWithConfig: (NSDictionary *) _config
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
id refreshTokenBool, domainInfo;
|
||||
|
||||
if([_config objectForKey: @"SOGoOpenIdConfigUrl"] &&
|
||||
[_config objectForKey: @"SOGoOpenIdScope"] &&
|
||||
[_config objectForKey: @"SOGoOpenIdClient"] &&
|
||||
[_config objectForKey: @"SOGoOpenIdClientSecret"])
|
||||
{
|
||||
openIdConfigUrl = [_config objectForKey: @"SOGoOpenIdConfigUrl"];
|
||||
openIdScope = [_config objectForKey: @"SOGoOpenIdScope"];
|
||||
openIdClient = [_config objectForKey: @"SOGoOpenIdClient"];
|
||||
openIdClientSecret = [_config objectForKey: @"SOGoOpenIdClientSecret"];
|
||||
openIdEmailParam = [_config objectForKey: @"SOGoOpenIdEmailParam"];
|
||||
|
||||
openIdEnableRefreshToken = NO;
|
||||
refreshTokenBool = [_config objectForKey: @"SOGoOpenIdEnableRefreshToken"];
|
||||
if (refreshTokenBool && [refreshTokenBool respondsToSelector: @selector (boolValue)])
|
||||
openIdEnableRefreshToken = [refreshTokenBool boolValue];
|
||||
|
||||
sendDomainInfo = NO;
|
||||
domainInfo = [_config objectForKey: @"SOGoOpenIdSendDomainInfo"];
|
||||
if (domainInfo && [domainInfo respondsToSelector: @selector (boolValue)])
|
||||
sendDomainInfo = [domainInfo boolValue];
|
||||
|
||||
|
||||
userTokenInterval = [_config objectForKey: @"SOGoOpenIdTokenCheckInterval"];
|
||||
[self _loadSessionFromCache: forDomain];
|
||||
|
||||
if(cacheUpdateNeeded)
|
||||
{
|
||||
[self fecthConfiguration: forDomain];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"Missing parameters from sogo.conf"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) initialize: (NSString*) _domain
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
NSDictionary *config;
|
||||
NSString *type;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
SOGoOpenIDDebugEnabled = [sd openIdDebugEnabled];
|
||||
openIdSessionIsOK = NO;
|
||||
|
||||
//Check if there is a root config or config per domain
|
||||
if(_domain != nil && [sd doesLoginTypeByDomain])
|
||||
{
|
||||
forDomain = _domain;
|
||||
type = [sd getLoginTypeForDomain: _domain];
|
||||
if(type != nil && [type isEqualToString: @"openid"])
|
||||
{
|
||||
config = [sd getLoginConfigForDomain: _domain];
|
||||
[self initializeWithConfig: config];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"Missing parameters from sogo.conf"];
|
||||
}
|
||||
}
|
||||
else if ([[self class] checkUserConfig])
|
||||
{
|
||||
openIdConfigUrl = [sd openIdConfigUrl];
|
||||
openIdScope = [sd openIdScope];
|
||||
openIdClient = [sd openIdClient];
|
||||
openIdClientSecret = [sd openIdClientSecret];
|
||||
openIdEmailParam = [sd openIdEmailParam];
|
||||
openIdEnableRefreshToken = [sd openIdEnableRefreshToken];
|
||||
userTokenInterval = [sd openIdTokenCheckInterval];
|
||||
sendDomainInfo = [sd openIdSendDomainInfo];
|
||||
forDomain = _domain;
|
||||
|
||||
[self _loadSessionFromCache: _domain];
|
||||
|
||||
if(cacheUpdateNeeded)
|
||||
{
|
||||
[self fecthConfiguration: _domain];
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"Missing parameters from sogo.conf"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (WOResponse *) _performOpenIdRequest: (NSString *) endpoint
|
||||
method: (NSString *) method
|
||||
headers: (NSDictionary *) headers
|
||||
body: (NSData *) body
|
||||
{
|
||||
NSURL *url;
|
||||
NSUInteger status;
|
||||
WORequest *request;
|
||||
WOResponse *response;
|
||||
WOHTTPConnection *httpConnection;
|
||||
|
||||
|
||||
url = [NSURL URLWithString: endpoint];
|
||||
if (url)
|
||||
{
|
||||
if(SOGoOpenIDDebugEnabled)
|
||||
{
|
||||
NSLog(@"OpenId perform request: %@ %@", method, endpoint);
|
||||
NSLog(@"OpenId perform request, headers %@", headers);
|
||||
// if(body)
|
||||
// NSLog(@"OpenId perform request: content %@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
|
||||
}
|
||||
|
||||
httpConnection = [[WOHTTPConnection alloc] initWithURL: url];
|
||||
[httpConnection autorelease];
|
||||
|
||||
request = [[WORequest alloc] initWithMethod: method
|
||||
uri: [endpoint hostlessURL]
|
||||
httpVersion: @"HTTP/1.1"
|
||||
headers: headers content: body
|
||||
userInfo: nil];
|
||||
[request autorelease];
|
||||
[httpConnection sendRequest: request];
|
||||
response = [httpConnection readResponse];
|
||||
status = [response status];
|
||||
if(status >= 200 && status <500 && status != 404)
|
||||
return response;
|
||||
else if (status == 404)
|
||||
{
|
||||
[self errorWithFormat: @"OpenID endpoint not found (404): %@", endpoint];
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"OpenID server internal error during %@: %@", endpoint, response];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"OpenID can't handle endpoint (not a url): '%@'", endpoint];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) fecthConfiguration: (NSString*) _domain
|
||||
{
|
||||
NSString *content;
|
||||
WOResponse * response;
|
||||
NSUInteger status;
|
||||
NSMutableDictionary *result;
|
||||
NSDictionary *config, *headers;
|
||||
NSURL *url;
|
||||
|
||||
result = [NSMutableDictionary dictionary];
|
||||
[result setObject: self->openIdConfigUrl forKey: @"url"];
|
||||
|
||||
url = [NSURL URLWithString: self->openIdConfigUrl ];
|
||||
if (url)
|
||||
{
|
||||
if(self->sendDomainInfo && _domain != nil && [_domain length] > 0)
|
||||
headers = [NSDictionary dictionaryWithObject: _domain forKey: @"sogo-user-domain"];
|
||||
else
|
||||
headers = nil;
|
||||
|
||||
response = [self _performOpenIdRequest: self->openIdConfigUrl
|
||||
method: @"GET"
|
||||
headers: headers
|
||||
body: nil];
|
||||
|
||||
if (response)
|
||||
{
|
||||
status = [response status];
|
||||
if(status >= 200 && status <300)
|
||||
{
|
||||
content = [response contentString];
|
||||
config = [content objectFromJSONString];
|
||||
self->authorizationEndpoint = [config objectForKey: @"authorization_endpoint"];
|
||||
self->tokenEndpoint = [config objectForKey: @"token_endpoint"];
|
||||
self->userinfoEndpoint = [config objectForKey: @"userinfo_endpoint"];
|
||||
self->endSessionEndpoint = [config objectForKey: @"end_session_endpoint"];
|
||||
|
||||
//Optionnals?
|
||||
if([config objectForKey: @"introspection_endpoint"])
|
||||
self->introspectionEndpoint = [config objectForKey: @"introspection_endpoint"];
|
||||
if([config objectForKey: @"revocation_endpoint"])
|
||||
self->revocationEndpoint = [config objectForKey: @"revocation_endpoint"];
|
||||
|
||||
openIdSessionIsOK = YES;
|
||||
[self _saveSessionToCache: _domain];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Error during fetching the configuration (status %d), response: %@", status, response];
|
||||
}
|
||||
}
|
||||
else
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
else
|
||||
[result setObject: @"invalid-url" forKey: @"error"];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSession: (NSString *) _domain
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initialize: _domain];
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithConfig: (NSDictionary *) _config
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initializeWithConfig: _config];
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithToken: (NSString *) token domain: (NSString *) _domain
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
if (token)
|
||||
{
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initialize: _domain];
|
||||
|
||||
[newSession setAccessToken: token];
|
||||
}
|
||||
else
|
||||
newSession = nil;
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
+ (SOGoOpenIdSession *) OpenIdSessionWithTokenAndConfig: (NSString *) token config: (NSDictionary *) _config
|
||||
{
|
||||
SOGoOpenIdSession *newSession;
|
||||
|
||||
if (token)
|
||||
{
|
||||
newSession = [self new];
|
||||
[newSession autorelease];
|
||||
[newSession initializeWithConfig: _config];
|
||||
|
||||
[newSession setAccessToken: token];
|
||||
}
|
||||
else
|
||||
newSession = nil;
|
||||
|
||||
return newSession;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) sessionIsOk
|
||||
{
|
||||
return self->openIdSessionIsOK;
|
||||
}
|
||||
|
||||
- (void) _loadSessionFromCache: (NSString*) _domain
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *jsonSession, *cacheKey;
|
||||
NSDictionary *sessionDict;
|
||||
|
||||
if(_domain != nil && [_domain length] > 0)
|
||||
cacheKey = [self->openIdConfigUrl stringByAppendingFormat: @":%@", _domain];
|
||||
else
|
||||
cacheKey = self->openIdConfigUrl;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
jsonSession = [cache openIdSessionFromServer: cacheKey];
|
||||
if ([jsonSession length])
|
||||
{
|
||||
sessionDict = [jsonSession objectFromJSONString];
|
||||
ASSIGN (authorizationEndpoint, [sessionDict objectForKey: @"authorization_endpoint"]);
|
||||
ASSIGN (tokenEndpoint, [sessionDict objectForKey: @"token_endpoint"]);
|
||||
ASSIGN (userinfoEndpoint, [sessionDict objectForKey: @"userinfo_endpoint"]);
|
||||
ASSIGN (endSessionEndpoint, [sessionDict objectForKey: @"end_session_endpoint"]);
|
||||
|
||||
//Optionnals?
|
||||
if([sessionDict objectForKey: @"introspection_endpoint"])
|
||||
ASSIGN (introspectionEndpoint, [sessionDict objectForKey: @"introspection_endpoint"]);
|
||||
if([sessionDict objectForKey: @"revocation_endpoint"])
|
||||
ASSIGN (revocationEndpoint, [sessionDict objectForKey: @"revocation_endpoint"]);
|
||||
openIdSessionIsOK = YES;
|
||||
}
|
||||
else
|
||||
cacheUpdateNeeded = YES;
|
||||
}
|
||||
|
||||
- (void) _saveSessionToCache: (NSString*) _domain
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *jsonSession, *cacheKey;
|
||||
NSMutableDictionary *sessionDict;
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
sessionDict = [NSMutableDictionary dictionary];
|
||||
[sessionDict setObject: authorizationEndpoint forKey: @"authorization_endpoint"];
|
||||
[sessionDict setObject: tokenEndpoint forKey: @"token_endpoint"];
|
||||
[sessionDict setObject: userinfoEndpoint forKey: @"userinfo_endpoint"];
|
||||
[sessionDict setObject: endSessionEndpoint forKey: @"end_session_endpoint"];
|
||||
|
||||
//Optionnals?
|
||||
if(introspectionEndpoint)
|
||||
[sessionDict setObject: introspectionEndpoint forKey: @"introspection_endpoint"];
|
||||
if(revocationEndpoint)
|
||||
[sessionDict setObject: revocationEndpoint forKey: @"revocation_endpoint"];
|
||||
|
||||
jsonSession = [sessionDict jsonRepresentation];
|
||||
|
||||
if(_domain != nil && [_domain length] > 0)
|
||||
cacheKey = [self->openIdConfigUrl stringByAppendingFormat: @":%@", _domain];
|
||||
else
|
||||
cacheKey = self->openIdConfigUrl;
|
||||
|
||||
[cache setOpenIdSession: jsonSession
|
||||
forServer: cacheKey];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) _loadUserFromCache: (NSString *) email
|
||||
{
|
||||
/*
|
||||
Return NO if the user token must be check again, YES the user is valid for now
|
||||
*/
|
||||
SOGoCache *cache;
|
||||
NSString *identifier, *nextCheck;
|
||||
NSTimeInterval nextCheckTime, timeNow;
|
||||
|
||||
identifier = [self->openIdClient stringByAppendingString: email];
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
nextCheck = [cache userOpendIdSessionFromServer: identifier];
|
||||
if ([nextCheck length])
|
||||
{
|
||||
timeNow = [[NSDate date] timeIntervalSince1970];
|
||||
nextCheckTime = [nextCheck doubleValue];
|
||||
if(timeNow > nextCheckTime)
|
||||
return NO;
|
||||
else
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) _saveUserToCache: (NSString *) email
|
||||
{
|
||||
SOGoCache *cache;
|
||||
NSString *identifier, *nextCheck;
|
||||
NSTimeInterval nextCheckTime;
|
||||
|
||||
identifier = [self->openIdClient stringByAppendingString: email];
|
||||
nextCheckTime = [[NSDate date] timeIntervalSince1970] + self->userTokenInterval;
|
||||
nextCheck = [NSString stringWithFormat:@"%f", nextCheckTime];
|
||||
|
||||
cache = [SOGoCache sharedCache];
|
||||
[cache setUserOpendIdSessionFromServer: identifier
|
||||
nextCheckAfter: nextCheck];
|
||||
}
|
||||
|
||||
- (NSString*) loginUrl: (NSString *) oldLocation
|
||||
{
|
||||
NSString* logUrl;
|
||||
logUrl = [self->authorizationEndpoint stringByAppendingFormat: @"?scope=%@", self->openIdScope];
|
||||
logUrl = [logUrl stringByAppendingString: @"&response_type=code"];
|
||||
logUrl = [logUrl stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||
logUrl = [logUrl stringByAppendingFormat: @"&redirect_uri=%@", oldLocation];
|
||||
if(self->forDomain != nil && [self->forDomain length] > 0)
|
||||
logUrl = [logUrl stringByAppendingFormat: @"&sogo_domain=%@", forDomain];
|
||||
// logurl = [self->logurl stringByAppendingFormat: @"&state=%@", state];
|
||||
|
||||
return logUrl;
|
||||
}
|
||||
|
||||
- (NSString *) logoutUrl
|
||||
{
|
||||
return self->endSessionEndpoint;
|
||||
}
|
||||
|
||||
- (NSString*) getRefreshToken
|
||||
{
|
||||
return self->refreshToken;
|
||||
}
|
||||
|
||||
- (NSString*) getToken
|
||||
{
|
||||
return self->accessToken;
|
||||
}
|
||||
|
||||
- (void) setAccessToken: (NSString* ) token
|
||||
{
|
||||
self->accessToken = token;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) getCurrentToken
|
||||
{
|
||||
NSString *currentToken;
|
||||
|
||||
//See if the current token has been refreshed
|
||||
currentToken = [[[GCSFolderManager defaultFolderManager] openIdFolder] getNewToken: self->accessToken];
|
||||
|
||||
if(currentToken)
|
||||
{
|
||||
//be sure the old token has been cleaned
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] deleteOpenIdSessionFor: self->accessToken];
|
||||
return currentToken;
|
||||
}
|
||||
return self->accessToken;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) fetchToken: (NSString * ) code redirect: (NSString *) oldLocation
|
||||
{
|
||||
NSString *location, *form, *content;
|
||||
WOResponse *response;
|
||||
NSUInteger status;
|
||||
NSMutableDictionary *result;
|
||||
NSDictionary *headers;
|
||||
NSDictionary *tokenRet;
|
||||
NSURL *url;
|
||||
|
||||
result = [NSMutableDictionary dictionary];
|
||||
[result setObject: @"ok" forKey: @"error"];
|
||||
|
||||
location = self->tokenEndpoint;
|
||||
url = [NSURL URLWithString: location];
|
||||
if (url)
|
||||
{
|
||||
form = @"grant_type=authorization_code";
|
||||
form = [form stringByAppendingFormat: @"&code=%@", code];
|
||||
form = [form stringByAppendingFormat: @"&redirect_uri=%@", [oldLocation stringByEscapingURL]];
|
||||
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
||||
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||
|
||||
if(self->sendDomainInfo && self->forDomain != nil && [self->forDomain length] > 0)
|
||||
headers = [NSDictionary dictionaryWithObjectsAndKeys: @"application/x-www-form-urlencoded", @"content-type",
|
||||
self->forDomain, @"sogo-user-domain", nil];
|
||||
else
|
||||
headers = [NSDictionary dictionaryWithObject: @"application/x-www-form-urlencoded" forKey: @"content-type"];
|
||||
|
||||
response = [self _performOpenIdRequest: location
|
||||
method: @"POST"
|
||||
headers: headers
|
||||
body: [form dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
if (response)
|
||||
{
|
||||
status = [response status];
|
||||
if(status >= 200 && status <300)
|
||||
{
|
||||
content = [response contentString];
|
||||
tokenRet = [content objectFromJSONString];
|
||||
if (SOGoOpenIDDebugEnabled)
|
||||
{
|
||||
NSLog(@"fetch token response: %@", tokenRet);
|
||||
}
|
||||
self->accessToken = [tokenRet objectForKey: @"access_token"];
|
||||
self->refreshToken = [tokenRet objectForKey: @"refresh_token"];
|
||||
self->refreshExpiresIn = [tokenRet objectForKey: @"refresh_expires_in"];
|
||||
self->idToken = [tokenRet objectForKey: @"id_token"];
|
||||
self->tokenType = [tokenRet objectForKey: @"token_type"];
|
||||
self->expiresIn = [tokenRet objectForKey: @"expires_in"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Error during fetching the token (status %d), response: %@", status, response];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
}
|
||||
else
|
||||
[result setObject: @"invalid-url" forKey: @"error"];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) refreshToken: (NSString * ) userRefreshToken
|
||||
{
|
||||
NSString *location, *form, *content;
|
||||
WOResponse *response;
|
||||
NSUInteger status;
|
||||
NSMutableDictionary *result;
|
||||
NSDictionary *headers;
|
||||
NSDictionary *refreshTokenRet;
|
||||
NSURL *url;
|
||||
|
||||
result = [NSMutableDictionary dictionary];
|
||||
[result setObject: @"ok" forKey: @"error"];
|
||||
|
||||
if(!userRefreshToken || [userRefreshToken length] == 0)
|
||||
{
|
||||
[result setObject: @"invalid-token" forKey: @"error"];
|
||||
return result;
|
||||
}
|
||||
|
||||
location = self->tokenEndpoint;
|
||||
url = [NSURL URLWithString: location];
|
||||
if (url)
|
||||
{
|
||||
form = @"grant_type=refresh_token";
|
||||
form = [form stringByAppendingFormat: @"&scope=%@", self->openIdScope];
|
||||
form = [form stringByAppendingFormat: @"&refresh_token=%@", userRefreshToken];
|
||||
form = [form stringByAppendingFormat: @"&client_secret=%@", self->openIdClientSecret];
|
||||
form = [form stringByAppendingFormat: @"&client_id=%@", self->openIdClient];
|
||||
|
||||
if(self->sendDomainInfo && self->forDomain != nil && [self->forDomain length] > 0)
|
||||
headers = [NSDictionary dictionaryWithObjectsAndKeys: @"application/x-www-form-urlencoded", @"content-type",
|
||||
self->forDomain, @"sogo-user-domain", nil];
|
||||
else
|
||||
headers = [NSDictionary dictionaryWithObject: @"application/x-www-form-urlencoded" forKey: @"content-type"];
|
||||
|
||||
response = [self _performOpenIdRequest: location
|
||||
method: @"POST"
|
||||
headers: headers
|
||||
body: [form dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
if (response)
|
||||
{
|
||||
status = [response status];
|
||||
if(status >= 200 && status <300)
|
||||
{
|
||||
content = [response contentString];
|
||||
refreshTokenRet = [content objectFromJSONString];
|
||||
if (SOGoOpenIDDebugEnabled)
|
||||
{
|
||||
NSLog(@"refresh token response: %@", refreshTokenRet);
|
||||
}
|
||||
self->accessToken = [refreshTokenRet objectForKey: @"access_token"];
|
||||
self->refreshToken = [refreshTokenRet objectForKey: @"refresh_token"];
|
||||
self->refreshExpiresIn = [refreshTokenRet objectForKey: @"refresh_expires_in"];
|
||||
self->tokenType = [refreshTokenRet objectForKey: @"token_type"];
|
||||
self->expiresIn = [refreshTokenRet objectForKey: @"expires_in"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Error during refreshing the token (status %d), response: %@", status, response];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
}
|
||||
else
|
||||
[result setObject: @"invalid-url" forKey: @"error"];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) fetchUserInfo
|
||||
{
|
||||
NSString *location, *auth, *content;
|
||||
WOResponse *response;
|
||||
NSUInteger status;
|
||||
NSMutableDictionary *result;
|
||||
NSDictionary *profile, *headers;
|
||||
NSURL *url;
|
||||
NSString *email;
|
||||
|
||||
result = [NSMutableDictionary dictionary];
|
||||
[result setObject: @"ok" forKey: @"error"];
|
||||
|
||||
location = self->userinfoEndpoint;
|
||||
url = [NSURL URLWithString: location];
|
||||
if (url)
|
||||
{
|
||||
auth = [NSString stringWithFormat: @"Bearer %@", self->accessToken];
|
||||
if(self->sendDomainInfo && self->forDomain != nil && [self->forDomain length] > 0)
|
||||
headers = [NSDictionary dictionaryWithObjectsAndKeys: @"application/x-www-form-urlencoded", @"content-type",
|
||||
self->forDomain, @"sogo-user-domain",
|
||||
auth, @"authorization", nil];
|
||||
else
|
||||
headers = [NSDictionary dictionaryWithObjectsAndKeys: @"application/x-www-form-urlencoded", @"content-type",
|
||||
auth, @"authorization", nil];
|
||||
|
||||
response = [self _performOpenIdRequest: location
|
||||
method: @"GET"
|
||||
headers: headers
|
||||
body: nil];
|
||||
if (response)
|
||||
{
|
||||
status = [response status];
|
||||
if(status >= 200 && status <300)
|
||||
{
|
||||
content = [response contentString];
|
||||
profile = [content objectFromJSONString];
|
||||
if(SOGoOpenIDDebugEnabled && profile)
|
||||
NSLog(@"OpenId fetch user info, profile is %@", profile);
|
||||
|
||||
/*profile = {"sub":"70a3e6a1-37cf-4cf6-b114-6973aabca86a",
|
||||
"email_verified":false,
|
||||
"address":{},
|
||||
"name":"Foo Bar",
|
||||
"preferred_username":"myuser",
|
||||
"given_name":"Foo",
|
||||
"family_name":"Bar",
|
||||
"email":"myuser@user.com"}*/
|
||||
if (email = [profile objectForKey: self->openIdEmailParam])
|
||||
{
|
||||
if(self->userTokenInterval > 0)
|
||||
[self _saveUserToCache: email];
|
||||
[result setObject: email forKey: @"login"];
|
||||
}
|
||||
else
|
||||
[result setObject: @"no mail found" forKey: @"error"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Error during fetching the token (status %d), response: %@", status, response];
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[result setObject: @"http-error" forKey: @"error"];
|
||||
}
|
||||
}
|
||||
else
|
||||
[result setObject: @"invalid-url" forKey: @"error"];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+ (void) deleteValueForSessionKey: (NSString *) theSessionKey
|
||||
{
|
||||
GCSOpenIdFolder *folder;
|
||||
|
||||
folder = [[GCSFolderManager defaultFolderManager] openIdFolder];
|
||||
[folder deleteOpenIdSessionFor: theSessionKey];
|
||||
}
|
||||
|
||||
- (NSString *) _login
|
||||
{
|
||||
NSMutableDictionary *resultUserInfo, *resultResfreh;
|
||||
NSString* _oldAccessToken;
|
||||
|
||||
resultUserInfo = [self fetchUserInfo];
|
||||
if ([[resultUserInfo objectForKey: @"error"] isEqualToString: @"ok"])
|
||||
{
|
||||
//Save some infos as the refresh token to the database
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] writeOpenIdSession: self->accessToken
|
||||
withOldSession: nil
|
||||
withRefreshToken: self->refreshToken
|
||||
withExpire: self->expiresIn
|
||||
withRefreshExpire: self->refreshExpiresIn];
|
||||
return [resultUserInfo objectForKey: @"login"];
|
||||
}
|
||||
else if(openIdEnableRefreshToken)
|
||||
{
|
||||
//try to refresh
|
||||
if(self->accessToken)
|
||||
{
|
||||
self->refreshToken = [[[GCSFolderManager defaultFolderManager] openIdFolder] getRefreshToken: self->accessToken];
|
||||
//remove old session
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] deleteOpenIdSessionFor: self->accessToken];
|
||||
}
|
||||
if(self->refreshToken)
|
||||
{
|
||||
_oldAccessToken = self->accessToken;
|
||||
resultResfreh = [self refreshToken: self->refreshToken];
|
||||
if ([[resultResfreh objectForKey: @"error"] isEqualToString: @"ok"])
|
||||
{
|
||||
resultUserInfo = [self fetchUserInfo];
|
||||
if ([[resultUserInfo objectForKey: @"error"] isEqualToString: @"ok"])
|
||||
{
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] writeOpenIdSession: self->accessToken
|
||||
withOldSession: _oldAccessToken
|
||||
withRefreshToken: self->refreshToken
|
||||
withExpire: self->expiresIn
|
||||
withRefreshExpire: self->refreshExpiresIn];
|
||||
return [resultUserInfo objectForKey: @"login"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//The acces token hasn't work, delete the session in database if needed
|
||||
if(self->accessToken)
|
||||
{
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] deleteOpenIdSessionFor: self->accessToken];
|
||||
}
|
||||
|
||||
[self errorWithFormat: @"Can't get user email from profile because: %@", [resultUserInfo objectForKey: @"error"]];
|
||||
}
|
||||
else
|
||||
{
|
||||
//remove old session
|
||||
[[[GCSFolderManager defaultFolderManager] openIdFolder] deleteOpenIdSessionFor: self->accessToken];
|
||||
}
|
||||
|
||||
return @"anonymous";
|
||||
}
|
||||
|
||||
- (NSString *) login: (NSString *) email
|
||||
{
|
||||
//Check if we need to fetch userinfo
|
||||
if(self->userTokenInterval > 0 && [self _loadUserFromCache: email])
|
||||
{
|
||||
return email;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [self _login];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -247,12 +247,20 @@
|
||||
usingKey: theKey];
|
||||
|
||||
r = [decodedValue rangeOfString: @":"];
|
||||
*theLogin = [decodedValue substringToIndex: r.location];
|
||||
*thePassword = [decodedValue substringFromIndex: r.location+1];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
*theLogin = [decodedValue substringToIndex: r.location];
|
||||
*thePassword = [decodedValue substringFromIndex: r.location+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
*theLogin = nil;
|
||||
*thePassword = nil;
|
||||
}
|
||||
*theDomain = nil;
|
||||
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if ([sd enableDomainBasedUID])
|
||||
if (*theLogin &&[sd enableDomainBasedUID])
|
||||
{
|
||||
r = [*theLogin rangeOfString: @"@" options: NSBackwardsSearch];
|
||||
if (r.location != NSNotFound)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoDomainDefaults.h>
|
||||
#import <SOGo/SOGoSystemDefaults.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoTextTemplateFile.h>
|
||||
|
||||
@@ -697,8 +698,11 @@ static NSString *sieveScriptName = @"sogo";
|
||||
NSDictionary *result;
|
||||
NSString *login, *authname, *password;
|
||||
SOGoDomainDefaults *dd;
|
||||
SOGoSystemDefaults *sd;
|
||||
NGSieveClient *client;
|
||||
NSString *sieveServer, *sieveScheme, *sieveQuery, *imapServer;
|
||||
NSString *imapAuthMech, *userDomain;
|
||||
NSRange r;
|
||||
NSURL *url, *cUrl;
|
||||
int sievePort;
|
||||
BOOL connected;
|
||||
@@ -773,7 +777,20 @@ static NSString *sieveScriptName = @"sogo";
|
||||
url = [NSURL URLWithString: [NSString stringWithFormat: @"%@://%@:%d%@",
|
||||
sieveScheme, sieveServer, sievePort, sieveQuery]];
|
||||
|
||||
client = [[NGSieveClient alloc] initWithURL: url];
|
||||
//In case of differrent auth method for different domain, check it
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
imapAuthMech = nil;
|
||||
if([sd doesLoginTypeByDomain])
|
||||
{
|
||||
r = [theUsername rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
userDomain = [theUsername substringFromIndex: r.location+1];
|
||||
imapAuthMech = [sd getImapAuthMechForDomain: userDomain];
|
||||
}
|
||||
}
|
||||
|
||||
client = [[NGSieveClient alloc] initWithURL: url andAuthMech: imapAuthMech];
|
||||
|
||||
if (!client) {
|
||||
[self errorWithFormat: @"Sieve connection failed on %@", [url description]];
|
||||
@@ -1016,8 +1033,8 @@ static NSString *sieveScriptName = @"sogo";
|
||||
text = [NSString stringWithFormat: @"%@%@", text, [templateFile textForUser: user]];
|
||||
}
|
||||
|
||||
if (days == 0)
|
||||
days = 7;
|
||||
if (days <= 0)
|
||||
days = [dd vacationAllowZeroDays] ? 0 : 7;
|
||||
|
||||
vacation_script = [NSMutableString string];
|
||||
|
||||
|
||||
@@ -36,6 +36,11 @@ static const NSString *kDisableSharingCalendar = @"Calendar";
|
||||
+ (SOGoSystemDefaults *) sharedSystemDefaults;
|
||||
|
||||
- (NSArray *) domainIds;
|
||||
- (BOOL) doesLoginTypeByDomain;
|
||||
- (NSString *) getLoginTypeForDomain: (NSString*) _domain;
|
||||
- (NSString *) getLoginConfigForDomain: (NSDictionary*) _domain;
|
||||
- (NSString *) getImapAuthMechForDomain: (NSString*) _domain;
|
||||
- (NSString *) getSmtpAuthMechForDomain: (NSString*) _domain;
|
||||
- (BOOL) forbidUnknownDomainsAuth;
|
||||
- (NSArray *) domainsAllowed;
|
||||
- (BOOL) enableDomainBasedUID;
|
||||
@@ -55,6 +60,7 @@ static const NSString *kDisableSharingCalendar = @"Calendar";
|
||||
|
||||
- (BOOL) isWebAccessEnabled;
|
||||
- (BOOL) isCalendarDAVAccessEnabled;
|
||||
- (BOOL) isCalendarJitsiLinkEnabled;
|
||||
- (BOOL) isAddressBookDAVAccessEnabled;
|
||||
|
||||
- (BOOL) enableEMailAlarms;
|
||||
@@ -76,6 +82,7 @@ static const NSString *kDisableSharingCalendar = @"Calendar";
|
||||
|
||||
- (BOOL) uixDebugEnabled;
|
||||
- (BOOL) easDebugEnabled;
|
||||
- (BOOL) openIdDebugEnabled;
|
||||
- (BOOL) tnefDecoderDebugEnabled;
|
||||
- (BOOL) xsrfValidationEnabled;
|
||||
|
||||
@@ -88,11 +95,22 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
|
||||
- (NSString *) loginSuffix;
|
||||
|
||||
- (NSString *) authenticationType;
|
||||
- (BOOL) isSsoUsed: (NSString *) domain;
|
||||
- (NSString *) davAuthenticationType;
|
||||
|
||||
- (NSString *) CASServiceURL;
|
||||
- (BOOL) CASLogoutEnabled;
|
||||
|
||||
- (NSString *) openIdConfigUrl;
|
||||
- (NSString *) openIdScope;
|
||||
- (NSString *) openIdClient;
|
||||
- (NSString *) openIdClientSecret;
|
||||
- (NSString *) openIdEmailParam;
|
||||
- (BOOL) openIdEnableRefreshToken;
|
||||
- (BOOL) openIdLogoutEnabled: (NSString *) _domain;
|
||||
- (int) openIdTokenCheckInterval;
|
||||
- (BOOL) openIdSendDomainInfo;
|
||||
|
||||
- (NSString *) SAML2PrivateKeyLocation;
|
||||
- (NSString *) SAML2CertificateLocation;
|
||||
- (NSString *) SAML2IdpMetadataLocation;
|
||||
@@ -142,7 +160,7 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
|
||||
|
||||
- (NSString *)urlCreateAccount;
|
||||
|
||||
- (BOOL)disableMailCleaning;
|
||||
- (BOOL)enableMailCleaning;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -262,6 +262,143 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return [domains allKeys];
|
||||
}
|
||||
|
||||
- (BOOL) doesLoginTypeByDomain
|
||||
{
|
||||
return ([self dictionaryForKey: @"SOGoLoginTypeByDomain"] != nil);
|
||||
}
|
||||
|
||||
- (NSString *) getLoginTypeForDomain: (NSString*) _domain
|
||||
{
|
||||
NSDictionary *domains, *config;
|
||||
NSString *type;
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return nil;
|
||||
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
if([domains objectForKey: _domain])
|
||||
{
|
||||
config = [domains objectForKey: _domain];
|
||||
}
|
||||
else if([domains objectForKey: @"login_default"])
|
||||
{
|
||||
config = [domains objectForKey: @"login_default"];
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
|
||||
if((type = [config objectForKey: @"type"]))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) getImapAuthMechForDomain: (NSString*) _domain
|
||||
{
|
||||
NSDictionary *domains, *config;
|
||||
NSString *type;
|
||||
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return nil;
|
||||
|
||||
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
|
||||
if([domains objectForKey: _domain])
|
||||
{
|
||||
config = [domains objectForKey: _domain];
|
||||
}
|
||||
else if([domains objectForKey: @"login_default"])
|
||||
{
|
||||
config = [domains objectForKey: @"login_default"];
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
|
||||
if((type = [config objectForKey: @"imapAuthMech"]))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) getSmtpAuthMechForDomain: (NSString*) _domain
|
||||
{
|
||||
NSDictionary *domains, *config;
|
||||
NSString *type;
|
||||
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return nil;
|
||||
|
||||
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
|
||||
if([domains objectForKey: _domain])
|
||||
{
|
||||
config = [domains objectForKey: _domain];
|
||||
}
|
||||
else if([domains objectForKey: @"login_default"])
|
||||
{
|
||||
config = [domains objectForKey: @"login_default"];
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
|
||||
if((type = [config objectForKey: @"smtpAuthMech"]))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *) getLoginConfigForDomain: (NSDictionary*) _domain
|
||||
{
|
||||
NSDictionary *domains, *config;
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return nil;
|
||||
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
if([domains objectForKey: _domain])
|
||||
{
|
||||
config = [domains objectForKey: _domain];
|
||||
}
|
||||
else if([domains objectForKey: @"login_default"])
|
||||
{
|
||||
config = [domains objectForKey: @"login_default"];
|
||||
}
|
||||
|
||||
if(config)
|
||||
return config;
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL) hasOpenIdType
|
||||
{
|
||||
if([self doesLoginTypeByDomain])
|
||||
{
|
||||
NSDictionary *domainsConfig;
|
||||
NSEnumerator *e;
|
||||
NSString *domain, *type;
|
||||
if(![self doesLoginTypeByDomain])
|
||||
return NO;
|
||||
domainsConfig = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
||||
e = [domainsConfig keyEnumerator];
|
||||
while((domain = [e nextObject]))
|
||||
{
|
||||
if((type = [[domainsConfig objectForKey: domain] objectForKey: @"type"]))
|
||||
{
|
||||
if([type isEqualToString: @"openid"])
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
else
|
||||
return [[self authenticationType] isEqualToString: @"openid"];
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) enableDomainBasedUID
|
||||
{
|
||||
return [self boolForKey: @"SOGoEnableDomainBasedUID"];
|
||||
@@ -441,6 +578,11 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return [self boolForKey: @"SOGoCalendarDAVAccessEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) isCalendarJitsiLinkEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoCalendarEnableJitsiLink"];
|
||||
}
|
||||
|
||||
- (BOOL) isAddressBookDAVAccessEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoAddressBookDAVAccessEnabled"];
|
||||
@@ -507,6 +649,11 @@ _injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
||||
return [self boolForKey: @"SOGoEASDebugEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) openIdDebugEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoOpenIDDebugEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) tnefDecoderDebugEnabled
|
||||
{
|
||||
return [self boolForKey: @"SOGoTnefDecoderDebugEnabled"];
|
||||
@@ -577,6 +724,16 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
return [[self stringForKey: @"SOGoAuthenticationType"] lowercaseString];
|
||||
}
|
||||
|
||||
- (BOOL) isSsoUsed: (NSString *) domain
|
||||
{
|
||||
NSString* authType;
|
||||
|
||||
authType = [self getLoginTypeForDomain: domain];
|
||||
if(!authType)
|
||||
authType = [self authenticationType];
|
||||
return ([authType isEqualToString: @"cas"] || [authType isEqualToString: @"saml2"] || [authType isEqualToString: @"openid"]);
|
||||
}
|
||||
|
||||
- (NSString *) davAuthenticationType
|
||||
{
|
||||
return [[self stringForKey: @"SOGoDAVAuthenticationType"] lowercaseString];
|
||||
@@ -592,6 +749,78 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
return [self boolForKey: @"SOGoCASLogoutEnabled"];
|
||||
}
|
||||
|
||||
/* OpenId Support */
|
||||
- (NSString *) openIdConfigUrl
|
||||
{
|
||||
return [self stringForKey: @"SOGoOpenIdConfigUrl"];
|
||||
}
|
||||
|
||||
- (NSString *) openIdScope
|
||||
{
|
||||
return [self stringForKey: @"SOGoOpenIdScope"];
|
||||
}
|
||||
|
||||
- (NSString *) openIdClient
|
||||
{
|
||||
return [self stringForKey: @"SOGoOpenIdClient"];
|
||||
}
|
||||
|
||||
- (NSString *) openIdClientSecret
|
||||
{
|
||||
return [self stringForKey: @"SOGoOpenIdClientSecret"];
|
||||
}
|
||||
|
||||
- (NSString *) openIdEmailParam
|
||||
{
|
||||
NSString *emailParam;
|
||||
emailParam = [self stringForKey: @"SOGoOpenIdEmailParam"];
|
||||
if(!emailParam)
|
||||
emailParam = @"email";
|
||||
return emailParam;
|
||||
}
|
||||
|
||||
- (BOOL) openIdLogoutEnabled: (NSString *) _domain
|
||||
{
|
||||
if(_domain && [self doesLoginTypeByDomain])
|
||||
{
|
||||
NSDictionary *config;
|
||||
NSString *type;
|
||||
id value;
|
||||
if((config = [self getLoginConfigForDomain: _domain]))
|
||||
{
|
||||
if((type = [config objectForKey: @"type"]) && [type isEqualToString:@"openid"])
|
||||
return [self boolForKey: @"SOGoOpenIdLogoutEnabled" andDict: config];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return [self boolForKey: @"SOGoOpenIdLogoutEnabled"];
|
||||
}
|
||||
|
||||
- (BOOL) openIdSendDomainInfo
|
||||
{
|
||||
return [self boolForKey: @"SOGoOpenIdSendDomainInfo"];
|
||||
}
|
||||
|
||||
- (int) openIdTokenCheckInterval
|
||||
{
|
||||
|
||||
int v;
|
||||
|
||||
v = [self integerForKey: @"SOGoOpenIdTokenCheckInterval"];
|
||||
|
||||
if (!v)
|
||||
v = 0;
|
||||
if(v<0)
|
||||
v = 0;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
- (BOOL) openIdEnableRefreshToken
|
||||
{
|
||||
return [self boolForKey: @"SOGoOpenIdEnableRefreshToken"];
|
||||
}
|
||||
|
||||
/* SAML2 support */
|
||||
- (NSString *) SAML2PrivateKeyLocation
|
||||
{
|
||||
@@ -933,9 +1162,9 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
|
||||
return [self stringForKey: @"SOGoURLCreateAccount"];
|
||||
}
|
||||
|
||||
- (BOOL) disableMailCleaning
|
||||
- (BOOL) enableMailCleaning
|
||||
{
|
||||
return [self boolForKey: @"SOGoDisableMailCleaning"];
|
||||
return [self boolForKey: @"SOGoEnableMailCleaning"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -91,6 +91,7 @@
|
||||
|
||||
/* properties */
|
||||
- (NSString *) domain;
|
||||
- (NSString *) loginDomain;
|
||||
- (id <SOGoSource>) authenticationSource;
|
||||
|
||||
- (NSArray *) allEmails;
|
||||
|
||||
@@ -301,6 +301,19 @@ static const NSString *kEncryptedUserNamePrefix = @"uenc";
|
||||
return [self _fetchFieldForUser: @"c_domain"];
|
||||
}
|
||||
|
||||
- (NSString *) loginDomain
|
||||
{
|
||||
NSRange r;
|
||||
NSString *domain = nil;
|
||||
r = [self->login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
domain = [self->login substringFromIndex: r.location+1];
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
|
||||
- (id <SOGoSource>) authenticationSource
|
||||
{
|
||||
NSString *sourceID;
|
||||
|
||||
@@ -502,6 +502,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
perr: (SOGoPasswordPolicyError *) perr
|
||||
expire: (int *) expire
|
||||
grace: (int *) grace
|
||||
additionalInfo: (NSMutableDictionary **)_additionalInfo
|
||||
{
|
||||
NSObject <SOGoSource> *sogoSource;
|
||||
NSEnumerator *authIDs;
|
||||
@@ -520,8 +521,11 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
perr: perr
|
||||
expire: expire
|
||||
grace: grace];
|
||||
if (_additionalInfo && *_additionalInfo && [sogoSource userPasswordPolicy] && [[sogoSource userPasswordPolicy] count] > 0) {
|
||||
[*_additionalInfo setObject:[sogoSource userPasswordPolicy] forKey:@"userPolicies"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (checkOK && *domain == nil)
|
||||
{
|
||||
SOGoSystemDefaults *sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
@@ -554,6 +558,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
perr: (SOGoPasswordPolicyError *) _perr
|
||||
expire: (int *) _expire
|
||||
grace: (int *) _grace
|
||||
additionalInfo:(NSMutableDictionary **)_additionalInfo
|
||||
{
|
||||
return [self checkLogin: _login
|
||||
password: _pwd
|
||||
@@ -561,6 +566,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
perr: _perr
|
||||
expire: _expire
|
||||
grace: _grace
|
||||
additionalInfo: _additionalInfo
|
||||
useCache: YES];
|
||||
}
|
||||
|
||||
@@ -573,6 +579,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
perr: (SOGoPasswordPolicyError *) _perr
|
||||
expire: (int *) _expire
|
||||
grace: (int *) _grace
|
||||
additionalInfo: (NSMutableDictionary **)_additionalInfo
|
||||
useCache: (BOOL) useCache
|
||||
{
|
||||
NSString *dictPassword, *username, *jsonUser;
|
||||
@@ -726,7 +733,8 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
domain: _domain
|
||||
perr: _perr
|
||||
expire: _expire
|
||||
grace: _grace])
|
||||
grace: _grace
|
||||
additionalInfo: _additionalInfo])
|
||||
{
|
||||
checkOK = YES;
|
||||
if (!currentUser)
|
||||
@@ -1085,30 +1093,37 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
domain = nil;
|
||||
infos = nil;
|
||||
|
||||
//Try to get the domain from the uid
|
||||
r = [uid rangeOfString: @"@" options: NSBackwardsSearch];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
// The domain is probably appended to the username;
|
||||
// make sure it is a defined domain in the configuration.
|
||||
domain = [uid substringFromIndex: (r.location + r.length)];
|
||||
if ([self isDomainDefined: domain])
|
||||
username = [uid substringToIndex: r.location];
|
||||
else
|
||||
domain = nil;
|
||||
}
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if ([sd enableDomainBasedUID])
|
||||
{
|
||||
r = [uid rangeOfString: @"@" options: NSBackwardsSearch];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
// The domain is probably appended to the username;
|
||||
// make sure it is a defined domain in the configuration.
|
||||
domain = [uid substringFromIndex: (r.location + r.length)];
|
||||
if ([self isDomainDefined: domain])
|
||||
username = [uid substringToIndex: r.location];
|
||||
else
|
||||
domain = nil;
|
||||
}
|
||||
if (domain != nil)
|
||||
infos = [self contactInfosForUserWithUIDorEmail: username
|
||||
if (domain != nil)
|
||||
{
|
||||
if ([sd enableDomainBasedUID])
|
||||
infos = [self contactInfosForUserWithUIDorEmail: username
|
||||
inDomain: domain];
|
||||
}
|
||||
else
|
||||
infos = [self contactInfosForUserWithUIDorEmail: uid
|
||||
inDomain: domain];
|
||||
}
|
||||
|
||||
if (infos == nil)
|
||||
{
|
||||
// If the user was not found using the domain or if no domain was detected,
|
||||
// search using the original uid.
|
||||
infos = [self contactInfosForUserWithUIDorEmail: uid
|
||||
inDomain: nil];
|
||||
}
|
||||
|
||||
return infos;
|
||||
}
|
||||
@@ -1137,8 +1152,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
|
||||
if ([currentUser isKindOfClass: NSNullK])
|
||||
currentUser = nil;
|
||||
else if (!([currentUser objectForKey: @"emails"]
|
||||
&& [currentUser objectForKey: @"cn"]))
|
||||
else if (!([currentUser objectForKey: @"emails"] && [currentUser objectForKey: @"cn"]))
|
||||
{
|
||||
// We make sure that we either have no occurence of a cache entry or
|
||||
// that we have an occurence with only a cached password. In the
|
||||
@@ -1155,6 +1169,19 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
}
|
||||
else
|
||||
newUser = NO;
|
||||
|
||||
if(!domain)
|
||||
{
|
||||
//No domain provided is there one?
|
||||
NSRange r;
|
||||
r = [uid rangeOfString: @"@" options: NSBackwardsSearch];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
domain = [uid substringFromIndex: (r.location + r.length)];
|
||||
if (![self isDomainDefined: domain])
|
||||
domain = nil;
|
||||
}
|
||||
}
|
||||
[self _fillContactInfosForUser: currentUser
|
||||
withUIDorEmail: aUID
|
||||
inDomain: domain];
|
||||
@@ -1332,8 +1359,7 @@ static const NSString *kObfuscatedSecondaryEmailKey = @"obfuscatedSecondaryEmail
|
||||
- (NSArray *) fetchContactsMatching: (NSString *) filter
|
||||
inDomain: (NSString *) domain
|
||||
{
|
||||
return [self
|
||||
_fetchEntriesInSources: [self addressBookSourceIDsInDomain: domain]
|
||||
return [self _fetchEntriesInSources: [self addressBookSourceIDsInDomain: domain]
|
||||
matching: filter
|
||||
inDomain: domain];
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#import "SOGoConstants.h"
|
||||
|
||||
@class NSString;
|
||||
@class NSMutableDictionary;
|
||||
|
||||
@class WOContext;
|
||||
@class WOCookie;
|
||||
@@ -37,25 +38,29 @@
|
||||
|
||||
+ (id) sharedSOGoWebAuthenticator;
|
||||
|
||||
- (BOOL) checkLogin: (NSString *) _login
|
||||
password: (NSString *) _pwd
|
||||
domain: (NSString **) _domain
|
||||
perr: (SOGoPasswordPolicyError *) _perr
|
||||
expire: (int *) _expire
|
||||
grace: (int *) _grace;
|
||||
- (BOOL)checkLogin:(NSString *)_login
|
||||
password:(NSString *)_pwd
|
||||
domain:(NSString **)_domain
|
||||
perr:(SOGoPasswordPolicyError *)_perr
|
||||
expire:(int *)_expire
|
||||
grace:(int *)_grace
|
||||
additionalInfo:(NSMutableDictionary **)_additionalInfo;
|
||||
|
||||
- (BOOL) checkLogin: (NSString *) _login
|
||||
password: (NSString *) _pwd
|
||||
domain: (NSString **) _domain
|
||||
perr: (SOGoPasswordPolicyError *) _perr
|
||||
expire: (int *) _expire
|
||||
grace: (int *) _grace
|
||||
useCache: (BOOL) useCache;
|
||||
- (BOOL)checkLogin:(NSString *)_login
|
||||
password:(NSString *)_pwd
|
||||
domain:(NSString **)_domain
|
||||
perr:(SOGoPasswordPolicyError *)_perr
|
||||
expire:(int *)_expire
|
||||
grace:(int *)_grace
|
||||
additionalInfo:(NSMutableDictionary **)_additionalInfo
|
||||
useCache:(BOOL)useCache;
|
||||
|
||||
- (WOCookie *) cookieWithUsername: (NSString *) username
|
||||
andPassword: (NSString *) password
|
||||
inContext: (WOContext *) context;
|
||||
|
||||
- (NSArray *)getCookiesIfNeeded: (WOContext *)_ctx;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* _SOGOWEBAUTHENTICATOR_H_ */
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#import "SOGoCache.h"
|
||||
#import "SOGoCASSession.h"
|
||||
#import "SOGoOpenIdSession.h"
|
||||
#import "SOGoPermissions.h"
|
||||
#import "SOGoSession.h"
|
||||
#import "SOGoSystemDefaults.h"
|
||||
@@ -102,7 +103,8 @@
|
||||
domain: &domain
|
||||
perr: &perr
|
||||
expire: &expire
|
||||
grace: &grace];
|
||||
grace: &grace
|
||||
additionalInfo: nil];
|
||||
}
|
||||
|
||||
- (BOOL) checkLogin: (NSString *) _login
|
||||
@@ -111,6 +113,7 @@
|
||||
perr: (SOGoPasswordPolicyError *) _perr
|
||||
expire: (int *) _expire
|
||||
grace: (int *) _grace
|
||||
additionalInfo: (NSMutableDictionary **)_additionalInfo
|
||||
{
|
||||
return [self checkLogin: _login
|
||||
password: _pwd
|
||||
@@ -118,6 +121,7 @@
|
||||
perr: _perr
|
||||
expire: _expire
|
||||
grace: _grace
|
||||
additionalInfo: _additionalInfo
|
||||
useCache: YES];
|
||||
}
|
||||
|
||||
@@ -127,24 +131,56 @@
|
||||
perr: (SOGoPasswordPolicyError *) _perr
|
||||
expire: (int *) _expire
|
||||
grace: (int *) _grace
|
||||
additionalInfo: (NSMutableDictionary **)_additionalInfo
|
||||
useCache: (BOOL) _useCache
|
||||
{
|
||||
SOGoCASSession *session;
|
||||
SOGoCASSession *casSession;
|
||||
SOGoOpenIdSession * openIdSession;
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *authenticationType;
|
||||
NSString* loginDomain;
|
||||
BOOL rc;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
//Basic check
|
||||
if(!_login)
|
||||
return NO;
|
||||
if(_login && [_login length] == 0)
|
||||
return NO;
|
||||
|
||||
loginDomain = nil;
|
||||
if(*_domain == nil || [*_domain length] == 0)
|
||||
{
|
||||
NSRange r;
|
||||
r = [_login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
loginDomain = [_login substringFromIndex: r.location+1];
|
||||
}
|
||||
}
|
||||
|
||||
if([sd doesLoginTypeByDomain])
|
||||
authenticationType = [sd getLoginTypeForDomain: loginDomain];
|
||||
else
|
||||
authenticationType = [sd authenticationType];
|
||||
|
||||
authenticationType = [sd authenticationType];
|
||||
if ([authenticationType isEqualToString: @"cas"])
|
||||
{
|
||||
session = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO];
|
||||
if (session)
|
||||
rc = [[session login] isEqualToString: _login];
|
||||
casSession = [SOGoCASSession CASSessionWithIdentifier: _pwd fromProxy: NO];
|
||||
if (casSession)
|
||||
rc = [[casSession login] isEqualToString: _login];
|
||||
else
|
||||
rc = NO;
|
||||
}
|
||||
else if ([authenticationType isEqualToString: @"openid"])
|
||||
{
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: _pwd domain: loginDomain];
|
||||
if (openIdSession)
|
||||
rc = [[openIdSession login: _login] isEqualToString: _login];
|
||||
else
|
||||
rc = NO;
|
||||
}
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([authenticationType isEqualToString: @"saml2"])
|
||||
{
|
||||
@@ -164,8 +200,8 @@
|
||||
perr: _perr
|
||||
expire: _expire
|
||||
grace: _grace
|
||||
additionalInfo: _additionalInfo
|
||||
useCache: _useCache];
|
||||
|
||||
//[self logWithFormat: @"Checked login with ppolicy enabled: %d %d %d", *_perr, *_expire, *_grace];
|
||||
|
||||
// It's important to return the real value here. The callee will handle
|
||||
@@ -244,13 +280,15 @@
|
||||
login: &login
|
||||
domain: &domain
|
||||
password: &pwd];
|
||||
|
||||
|
||||
|
||||
if (![self checkLogin: login
|
||||
password: pwd
|
||||
domain: &domain
|
||||
perr: &perr
|
||||
expire: &expire
|
||||
grace: &grace])
|
||||
grace: &grace
|
||||
additionalInfo: nil])
|
||||
return nil;
|
||||
|
||||
if (domain && [login rangeOfString: @"@"].location == NSNotFound)
|
||||
@@ -266,32 +304,42 @@
|
||||
{
|
||||
NSString *authType, *password;
|
||||
SOGoSystemDefaults *sd;
|
||||
SOGoUser *user;
|
||||
NSRange r;
|
||||
NSString *loginDomain, *login;
|
||||
|
||||
password = [self passwordInContext: context];
|
||||
if ([password length])
|
||||
{
|
||||
user = [self userInContext: context];
|
||||
login = [user loginInDomain];
|
||||
r = [login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
loginDomain = [login substringFromIndex: r.location+1];
|
||||
else
|
||||
loginDomain = nil;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
authType = [sd authenticationType];
|
||||
if([sd doesLoginTypeByDomain])
|
||||
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||
else
|
||||
authType = [sd authenticationType];
|
||||
|
||||
if ([authType isEqualToString: @"cas"])
|
||||
{
|
||||
SOGoCASSession *session;
|
||||
SOGoUser *user;
|
||||
NSString *service, *scheme;
|
||||
|
||||
session = [SOGoCASSession CASSessionWithIdentifier: password
|
||||
fromProxy: NO];
|
||||
|
||||
user = [self userInContext: context];
|
||||
// Try configured CAS service name first
|
||||
service = [[user domainDefaults] imapCASServiceName];
|
||||
if (!service)
|
||||
{
|
||||
// We must NOT assume the scheme exists
|
||||
scheme = [server scheme];
|
||||
|
||||
if (!scheme)
|
||||
scheme = @"imap";
|
||||
|
||||
service = [NSString stringWithFormat: @"%@://%@",
|
||||
scheme, [server host]];
|
||||
}
|
||||
@@ -300,10 +348,18 @@
|
||||
[session invalidateTicketForService: service];
|
||||
|
||||
password = [session ticketForService: service];
|
||||
|
||||
if ([password length] || renew)
|
||||
[session updateCache];
|
||||
}
|
||||
else if ([authType isEqualToString: @"openid"])
|
||||
{
|
||||
SOGoOpenIdSession* session;
|
||||
|
||||
//If the token has been refresh during the request, we need to use the new access_token
|
||||
//as the one from the cookie is no more valid
|
||||
session = [SOGoOpenIdSession OpenIdSessionWithToken: password domain: loginDomain];
|
||||
password = [session getCurrentToken];
|
||||
}
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([authType isEqualToString: @"saml2"])
|
||||
{
|
||||
@@ -326,6 +382,16 @@
|
||||
return password;
|
||||
}
|
||||
|
||||
- (NSString *) smtpPasswordInContext: (WOContext *) context
|
||||
forURL: (NSURL *) server
|
||||
{
|
||||
NSString *password;
|
||||
|
||||
password = [self imapPasswordInContext: context forURL: server forceRenew:NO];
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
/* create SOGoUser */
|
||||
|
||||
- (SOGoUser *) userWithLogin: (NSString *) login
|
||||
@@ -430,4 +496,57 @@
|
||||
return authCookie;
|
||||
}
|
||||
|
||||
- (NSArray *) getCookiesIfNeeded: (WOContext *)_ctx
|
||||
{
|
||||
NSArray *listCookies = nil;
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *authType, *username, *login, *loginDomain;
|
||||
NSRange r;
|
||||
SOGoUser *user;
|
||||
|
||||
user = [self userInContext: _ctx];
|
||||
login = [user loginDomain];
|
||||
r = [login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
loginDomain = [login substringFromIndex: r.location+1];
|
||||
else
|
||||
loginDomain = nil;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if(loginDomain && [sd doesLoginTypeByDomain])
|
||||
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||
else
|
||||
authType = [sd authenticationType];
|
||||
if([authType isEqualToString:@"openid"] && [sd openIdEnableRefreshToken])
|
||||
{
|
||||
NSString *currentPassword, *newPassword;
|
||||
SOGoOpenIdSession *openIdSession;
|
||||
WOCookie* newCookie;
|
||||
|
||||
|
||||
currentPassword = [self passwordInContext: _ctx];
|
||||
newPassword = [self imapPasswordInContext: _ctx forURL: nil forceRenew: NO];
|
||||
if(currentPassword && newPassword && ![newPassword isEqualToString: currentPassword])
|
||||
{
|
||||
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSessionWithToken: newPassword domain: loginDomain];
|
||||
if (openIdSession)
|
||||
username = [openIdSession login: @""]; //Force to refresh the name
|
||||
else
|
||||
username = [[self userInContext: _ctx] login];
|
||||
newCookie = [self cookieWithUsername: username
|
||||
andPassword: newPassword
|
||||
inContext: _ctx];
|
||||
listCookies = [[NSArray alloc] initWithObjects: newCookie, nil];
|
||||
[listCookies autorelease];
|
||||
}
|
||||
if(listCookies && [listCookies isKindOfClass:[NSArray class]] && [listCookies count] > 0)
|
||||
return listCookies;
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end /* SOGoWebAuthenticator */
|
||||
+77
-25
@@ -245,6 +245,20 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL) checkLogin: (NSString *) _login
|
||||
password: (NSString *) _pwd
|
||||
perr: (SOGoPasswordPolicyError *) _perr
|
||||
expire: (int *) _expire
|
||||
grace: (int *) _grace
|
||||
{
|
||||
return [self checkLogin: _login
|
||||
password: _pwd
|
||||
perr: _perr
|
||||
expire: _perr
|
||||
grace: _grace
|
||||
disablepasswordPolicyCheck: NO];
|
||||
}
|
||||
|
||||
//
|
||||
// SQL sources don't support right now all the password policy
|
||||
// stuff supported by OpenLDAP (and others). If we want to support
|
||||
@@ -256,6 +270,7 @@
|
||||
perr: (SOGoPasswordPolicyError *) _perr
|
||||
expire: (int *) _expire
|
||||
grace: (int *) _grace
|
||||
disablepasswordPolicyCheck: (BOOL) _disablepasswordPolicyCheck
|
||||
{
|
||||
EOAdaptorChannel *channel;
|
||||
EOQualifier *qualifier;
|
||||
@@ -335,10 +350,69 @@
|
||||
else
|
||||
[self errorWithFormat:@"failed to acquire channel for URL: %@",
|
||||
[_viewURL absoluteString]];
|
||||
|
||||
if (YES == rc && !_disablepasswordPolicyCheck) {
|
||||
[self checkPasswordPolicyWithPassword:_pwd perr: _perr];
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a given password against the configured user password policies.
|
||||
*
|
||||
* This method checks if the provided password complies with all defined
|
||||
* password policies for the user. Each policy is expected to include
|
||||
* a regular expression (`regex`) that the password must match.
|
||||
*
|
||||
* If the password violates any policy, the method sets an appropriate error
|
||||
* in the `perr` parameter and stops further validation.
|
||||
*
|
||||
* @param password the password to validate.
|
||||
* @param perr will be set to indicate a policy violation, if the password
|
||||
* does not meet the required standards. Possible values include:
|
||||
* `PolicyInsufficientPasswordQuality` for insufficient complexity.
|
||||
* @return YES if the password satisfies all policies, NO otherwise.
|
||||
*
|
||||
* @note This method assumes that `_userPasswordPolicy` is an array of dictionaries,
|
||||
* where each dictionary represents a password policy with at least a "regex" key.
|
||||
* @warning If a policy does not include a "regex" key, an error will be logged, and
|
||||
* the method will continue to the next policy.
|
||||
*/
|
||||
- (BOOL) checkPasswordPolicyWithPassword: (NSString *)password perr: (SOGoPasswordPolicyError *)perr
|
||||
{
|
||||
BOOL isPolicyOk;
|
||||
NSDictionary *policy;
|
||||
NSEnumerator *policies;
|
||||
NSRange match;
|
||||
NSString *regex;
|
||||
|
||||
isPolicyOk = YES;
|
||||
|
||||
if ([_userPasswordPolicy count])
|
||||
{
|
||||
policies = [_userPasswordPolicy objectEnumerator];
|
||||
while (isPolicyOk && (policy = [policies nextObject]))
|
||||
{
|
||||
regex = [policy objectForKey: @"regex"];
|
||||
if (regex)
|
||||
{
|
||||
match = [password rangeOfString: regex options: NSRegularExpressionSearch];
|
||||
isPolicyOk = isPolicyOk && match.length > 0;
|
||||
if (match.length == 0)
|
||||
{
|
||||
// [self errorWithFormat: @"Password not conform to policy %@ (%@)", regex, [policy objectForKey: @"label"]];
|
||||
*perr = PolicyInsufficientPasswordQuality;
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"Invalid password policy (missing regex): %@", policy];
|
||||
}
|
||||
}
|
||||
|
||||
return isPolicyOk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a user's password.
|
||||
* @param login the user's login name.
|
||||
@@ -357,11 +431,8 @@
|
||||
BOOL didChange, isOldPwdOk, isPolicyOk;
|
||||
EOAdaptorChannel *channel;
|
||||
GCSChannelManager *cm;
|
||||
NSDictionary *policy;
|
||||
NSEnumerator *policies;
|
||||
NSException *ex;
|
||||
NSRange match;
|
||||
NSString *sqlstr, *regex;
|
||||
NSString *sqlstr;
|
||||
|
||||
*perr = -1;
|
||||
isOldPwdOk = NO;
|
||||
@@ -369,30 +440,11 @@
|
||||
didChange = NO;
|
||||
|
||||
// Verify current password
|
||||
isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0];
|
||||
isOldPwdOk = [self checkLogin:login password:oldPassword perr:perr expire:0 grace:0 disablepasswordPolicyCheck: YES];
|
||||
|
||||
if (isOldPwdOk || passwordRecovery)
|
||||
{
|
||||
if ([_userPasswordPolicy count])
|
||||
{
|
||||
policies = [_userPasswordPolicy objectEnumerator];
|
||||
while (isPolicyOk && (policy = [policies nextObject]))
|
||||
{
|
||||
regex = [policy objectForKey: @"regex"];
|
||||
if (regex)
|
||||
{
|
||||
match = [newPassword rangeOfString: regex options: NSRegularExpressionSearch];
|
||||
isPolicyOk = isPolicyOk && match.length > 0;
|
||||
if (match.length == 0)
|
||||
{
|
||||
// [self errorWithFormat: @"Password not conform to policy %@ (%@)", regex, [policy objectForKey: @"label"]];
|
||||
*perr = PolicyInsufficientPasswordQuality;
|
||||
}
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"Invalid password policy (missing regex): %@", policy];
|
||||
}
|
||||
}
|
||||
isPolicyOk = [self checkPasswordPolicyWithPassword:newPassword perr: perr];
|
||||
}
|
||||
|
||||
if ((isOldPwdOk || passwordRecovery) && isPolicyOk)
|
||||
|
||||
@@ -21,6 +21,7 @@ $(SOGO_TOOL)_OBJC_FILES += \
|
||||
SOGoToolRemoveDoubles.m \
|
||||
SOGoToolRenameUser.m \
|
||||
SOGoToolCheckupUser.m \
|
||||
SOGoToolCleanOpenIdSessions.m \
|
||||
SOGoToolCleanupUser.m \
|
||||
SOGoToolRestore.m \
|
||||
SOGoToolCreateFolder.m \
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/* SOGoToolCleanOpenIdSessions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2012-2021 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
|
||||
#import <GDLAccess/EOAdaptorChannel.h>
|
||||
|
||||
#import <GDLContentStore/GCSChannelManager.h>
|
||||
#import <GDLContentStore/NSURL+GCS.h>
|
||||
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoSession.h>
|
||||
#import <SOGo/SOGoOpenIdSession.h>
|
||||
|
||||
#import "SOGoTool.h"
|
||||
|
||||
@interface SOGoToolCleanOpenIdSessions : SOGoTool
|
||||
@end
|
||||
|
||||
@implementation SOGoToolCleanOpenIdSessions
|
||||
|
||||
+ (NSString *) command
|
||||
{
|
||||
return @"clean-openid-sessions";
|
||||
}
|
||||
|
||||
+ (NSString *) description
|
||||
{
|
||||
return @"clean user openid sessions that are expired";
|
||||
}
|
||||
|
||||
- (void) usage
|
||||
{
|
||||
fprintf (stderr, "clean-openid-sessions\n\n"
|
||||
"\n"
|
||||
"The clean-openid-sessions action should be configured as a cronjob.\n");
|
||||
}
|
||||
|
||||
- (BOOL) cleanExpiredOpenIdSession
|
||||
{
|
||||
BOOL rc;
|
||||
EOAdaptorChannel *channel;
|
||||
GCSChannelManager *cm;
|
||||
NSArray *attrs;
|
||||
NSDictionary *qresult;
|
||||
NSException *ex;
|
||||
NSString *sql, *sessionsFolderURL, *sessionID;
|
||||
NSURL *tableURL;
|
||||
NSUserDefaults *ud;
|
||||
|
||||
unsigned int now;
|
||||
|
||||
rc = YES;
|
||||
ud = [NSUserDefaults standardUserDefaults];
|
||||
now = [[NSCalendarDate calendarDate] timeIntervalSince1970];
|
||||
sessionID = nil;
|
||||
|
||||
sessionsFolderURL = [ud stringForKey: @"OCSOpenIdURL"];
|
||||
if (!sessionsFolderURL)
|
||||
{
|
||||
if (verbose)
|
||||
NSLog(@"Couldn't read OCSOpenIdURL");
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
tableURL = [[NSURL alloc] initWithString: sessionsFolderURL];
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
channel = [cm acquireOpenChannelForURL: tableURL];
|
||||
if (!channel)
|
||||
{
|
||||
/* FIXME: nice error msg */
|
||||
NSLog(@"Can't acquire channel");
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
sql = [NSString stringWithFormat: @"SELECT c_user_session FROM %@ WHERE c_access_token_expires_in <= %d AND c_refresh_token_expires_in <= %d",
|
||||
[tableURL gcsTableName], now, now];
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
if (ex)
|
||||
{
|
||||
NSLog(@"%@", [ex reason]);
|
||||
[ex raise];
|
||||
return rc = NO;
|
||||
}
|
||||
|
||||
attrs = [channel describeResults: NO];
|
||||
while ((qresult = [channel fetchAttributes: attrs withZone: NULL]))
|
||||
{
|
||||
sessionID = [qresult objectForKey: @"c_user_session"];
|
||||
if (sessionID)
|
||||
{
|
||||
if (verbose)
|
||||
NSLog(@"Removing session %@", sessionID);
|
||||
[SOGoOpenIdSession deleteValueForSessionKey: sessionID];
|
||||
}
|
||||
}
|
||||
[cm releaseChannel: channel immediately: YES];
|
||||
|
||||
if (verbose && sessionID == nil)
|
||||
NSLog(@"No session to remove on openId table");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (BOOL) run
|
||||
{
|
||||
BOOL rc;
|
||||
|
||||
rc = [self cleanExpiredOpenIdSession];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
#import <SOGo/SOGoSession.h>
|
||||
#import <SOGo/SOGoOpenIdSession.h>
|
||||
|
||||
#import "SOGoTool.h"
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ typedef enum
|
||||
" -p credentialFile Specify the file containing the sieve admin credentials\n"
|
||||
" The file should contain a single line:\n"
|
||||
" username:password\n"
|
||||
" -F force the activation of the sieve script in case external scripts. Must be the las targument after -p credentialsFile"
|
||||
" Examples:\n"
|
||||
" sogo-tool user-preferences get defaults janedoe SOGoLanguage\n"
|
||||
" sogo-tool user-preferences unset settings janedoe Mail\n"
|
||||
@@ -144,9 +145,19 @@ typedef enum
|
||||
account = [folder lookupName: @"0" inContext: localContext acquire: NO];
|
||||
[account setContext: localContext];
|
||||
|
||||
error = [account updateFiltersWithUsername: authname
|
||||
andPassword: authpwd
|
||||
forceActivation: NO];
|
||||
if([[arguments lastObject] isEqualToString: @"-F"])
|
||||
{
|
||||
error = [account updateFiltersWithUsername: authname
|
||||
andPassword: authpwd
|
||||
forceActivation: YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
error = [account updateFiltersWithUsername: authname
|
||||
andPassword: authpwd
|
||||
forceActivation: NO];
|
||||
}
|
||||
|
||||
if (error)
|
||||
return NO;
|
||||
}
|
||||
@@ -248,7 +259,7 @@ typedef enum
|
||||
break;
|
||||
|
||||
case UserPreferencesSet:
|
||||
if (max > 4)
|
||||
if (![arguments containsObject: @"-f"])
|
||||
{
|
||||
/* value specified on command line */
|
||||
value = [sanitizedArguments objectAtIndex: 4];
|
||||
|
||||
+1
-2
@@ -114,8 +114,7 @@
|
||||
if (count < max)
|
||||
{
|
||||
max -= count;
|
||||
toolArguments = [arguments
|
||||
subarrayWithRange: NSMakeRange (count, max)];
|
||||
toolArguments = [arguments subarrayWithRange: NSMakeRange (count, max)];
|
||||
[toolArguments retain];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,3 +233,10 @@
|
||||
"passwordRecovery_SecretQuestion2" = "What was your first car ?";
|
||||
"passwordRecovery_SecretQuestion3" = "What is your favorite movie ?";
|
||||
"passwordRecovery_SecondaryEmail" = "Secondary E-mail";
|
||||
|
||||
/* Password */
|
||||
"POLICY_MIN_LOWERCASE_LETTER" = "Minimum of %{0} lowercase letter";
|
||||
"POLICY_MIN_UPPERCASE_LETTER" = "Minimum of %{0} uppercase letter";
|
||||
"POLICY_MIN_DIGIT" = "Minimum of %{0} digit";
|
||||
"POLICY_MIN_SPECIAL_SYMBOLS" = "Minimum of %{0} special symbols";
|
||||
"POLICY_MIN_LENGTH" = "Minimum length of %{0} characters";
|
||||
@@ -233,3 +233,10 @@
|
||||
"passwordRecovery_SecretQuestion2" = "Quelle était votre première voiture ?";
|
||||
"passwordRecovery_SecretQuestion3" = "Quel est votre film préféré ?";
|
||||
"passwordRecovery_SecondaryEmail" = "E-mail secondaire";
|
||||
|
||||
/* Password */
|
||||
"POLICY_MIN_LOWERCASE_LETTER" = "Au moins %{0} lettre(s) minuscule(s)";
|
||||
"POLICY_MIN_UPPERCASE_LETTER" = "Au moins %{0} lettre(s) majuscule(s)";
|
||||
"POLICY_MIN_DIGIT" = "Au moins %{0} chiffre(s)";
|
||||
"POLICY_MIN_SPECIAL_SYMBOLS" = "Au moins %{0} caractère(s) special(aux)";
|
||||
"POLICY_MIN_LENGTH" = "Longueur d'au moins %{0} caractère(s)";
|
||||
@@ -488,13 +488,14 @@
|
||||
&& [user isSuperUser]);
|
||||
}
|
||||
|
||||
- (BOOL) usesCASAuthentication
|
||||
|
||||
- (BOOL) usesOpenIdAuthentication
|
||||
{
|
||||
SOGoSystemDefaults *sd;
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
return [[sd authenticationType] isEqualToString: @"cas"];
|
||||
return [[sd authenticationType] isEqualToString: @"openid"];
|
||||
}
|
||||
|
||||
- (BOOL) usesSAML2Authenticationx
|
||||
@@ -537,19 +538,34 @@
|
||||
BOOL canLogoff;
|
||||
id auth;
|
||||
SOGoSystemDefaults *sd;
|
||||
NSString *authType;
|
||||
NSString *authType, *login, *loginDomain;
|
||||
NSRange r;
|
||||
|
||||
auth = [[self clientObject] authenticatorInContext: context];
|
||||
if ([auth respondsToSelector: @selector (cookieNameInContext:)])
|
||||
{
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
authType = [sd authenticationType];
|
||||
if ([authType isEqualToString: @"cas"])
|
||||
canLogoff = [sd CASLogoutEnabled];
|
||||
else if ([authType isEqualToString: @"saml2"])
|
||||
canLogoff = [sd SAML2LogoutEnabled];
|
||||
|
||||
login = [[context activeUser] login];
|
||||
r = [login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
loginDomain = [login substringFromIndex: r.location+1];
|
||||
else
|
||||
canLogoff = [[auth cookieNameInContext: context] length] > 0;
|
||||
loginDomain = nil;
|
||||
if(loginDomain && [sd doesLoginTypeByDomain])
|
||||
authType = [sd getLoginTypeForDomain: loginDomain];
|
||||
else
|
||||
authType = [sd authenticationType];
|
||||
|
||||
if ([authType isEqualToString: @"cas"])
|
||||
canLogoff = [sd CASLogoutEnabled];
|
||||
else if ([authType isEqualToString: @"saml2"])
|
||||
canLogoff = [sd SAML2LogoutEnabled];
|
||||
else if ([authType isEqualToString: @"openid"])
|
||||
canLogoff = [sd openIdLogoutEnabled: loginDomain];
|
||||
else
|
||||
canLogoff = [[auth cookieNameInContext: context] length] > 0;
|
||||
}
|
||||
else
|
||||
canLogoff = NO;
|
||||
|
||||
@@ -85,6 +85,7 @@ _xmlCharsetForCharset (NSString *charset)
|
||||
{ @"windows-1255", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1255 -> utf8 conversion
|
||||
{ @"windows-1256", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1255 -> utf8 conversion
|
||||
{ @"windows-1257", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1257 -> utf8 conversion
|
||||
{ @"windows-874", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-874 -> utf8 conversion
|
||||
{ @"gb2312", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
||||
{ @"gbk", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
||||
{ @"gb18030", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
||||
@@ -521,7 +522,25 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content)
|
||||
{
|
||||
/* [resultPart appendString:
|
||||
@"src=\"/SOGo.woa/WebServerResources/empty.gif\""]; */
|
||||
name = @"unsafe-src";
|
||||
|
||||
//Check if the image is base64 ant not url
|
||||
NSUInteger i, c;
|
||||
BOOL isBase64 = NO;
|
||||
for (i = 0, c = [_attributes count]; i < c; i++) {
|
||||
NSString *type, *value;
|
||||
type = [_attributes nameAtIndex:i];
|
||||
if ([type isEqualToString:@"src"])
|
||||
{
|
||||
value = [_attributes valueAtIndex:i];
|
||||
if([value length] > 4 && [[value substringToIndex: 4] isEqualToString:@"data"])
|
||||
{
|
||||
isBase64 = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!isBase64)
|
||||
name = @"unsafe-src";
|
||||
}
|
||||
else
|
||||
skipAttribute = YES;
|
||||
|
||||
@@ -170,60 +170,95 @@
|
||||
|
||||
emailEvent = [self _emailEvent];
|
||||
if (emailEvent)
|
||||
{
|
||||
*eventObject = [self _eventObjectWithUID: [emailEvent uid]];
|
||||
if ([*eventObject isNew])
|
||||
{
|
||||
*eventObject = [self _eventObjectWithUID: [emailEvent uid]];
|
||||
if ([*eventObject isNew])
|
||||
{
|
||||
chosenEvent = emailEvent;
|
||||
[*eventObject saveCalendar: [emailEvent parent]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([emailEvent recurrenceId])
|
||||
{
|
||||
// Event attached to email is not complete -- retrieve it
|
||||
// from the database.
|
||||
NSString *recurrenceTime;
|
||||
|
||||
recurrenceTime = [NSString stringWithFormat: @"%f",
|
||||
[[emailEvent recurrenceId] timeIntervalSince1970]];
|
||||
calendarEvent = (iCalEvent *)[*eventObject lookupOccurrence: recurrenceTime];
|
||||
}
|
||||
else
|
||||
calendarEvent = (iCalEvent *) [*eventObject component: NO secure: NO];
|
||||
|
||||
if (calendarEvent != nil)
|
||||
{
|
||||
// Calendar event still exists -- verify which of the calendar
|
||||
// and email events is the most recent. We must also update
|
||||
// the event (or recurrence-id) with the email's content, otherwise
|
||||
// we would never get major properties updates
|
||||
if ([calendarEvent compare: emailEvent] == NSOrderedAscending)
|
||||
{
|
||||
iCalCalendar *parent;
|
||||
|
||||
parent = [calendarEvent parent];
|
||||
[parent removeChild: calendarEvent];
|
||||
[parent addChild: emailEvent];
|
||||
[*eventObject saveCalendar: parent];
|
||||
[*eventObject flush];
|
||||
chosenEvent = emailEvent;
|
||||
}
|
||||
else
|
||||
{
|
||||
chosenEvent = calendarEvent;
|
||||
if (![[[chosenEvent parent] method] length])
|
||||
[[chosenEvent parent] setMethod: [[emailEvent parent] method]];
|
||||
}
|
||||
}
|
||||
else
|
||||
chosenEvent = emailEvent;
|
||||
}
|
||||
|
||||
organizer = [chosenEvent organizer];
|
||||
if (![[organizer rfc822Email] length])
|
||||
[self _fixOrganizerInEvent: chosenEvent];
|
||||
chosenEvent = emailEvent;
|
||||
[*eventObject saveCalendar: [emailEvent parent]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([emailEvent recurrenceId])
|
||||
{
|
||||
// Event attached to email is not complete -- retrieve it
|
||||
// from the database.
|
||||
NSString *recurrenceTime;
|
||||
|
||||
recurrenceTime = [NSString stringWithFormat: @"%f",
|
||||
[[emailEvent recurrenceId] timeIntervalSince1970]];
|
||||
calendarEvent = (iCalEvent *)[*eventObject lookupOccurrence: recurrenceTime];
|
||||
}
|
||||
else
|
||||
calendarEvent = (iCalEvent *) [*eventObject component: NO secure: NO];
|
||||
|
||||
if (calendarEvent != nil)
|
||||
{
|
||||
// Calendar event still exists -- verify which of the calendar
|
||||
// and email events is the most recent. We must also update
|
||||
// the event (or recurrence-id) with the email's content, otherwise
|
||||
// we would never get major properties updates
|
||||
if ([calendarEvent compare: emailEvent] == NSOrderedAscending)
|
||||
{
|
||||
iCalCalendar *parent;
|
||||
parent = [calendarEvent parent];
|
||||
|
||||
//careful, sogo vcalendar objets for reccurent event needs to have the main event first, then all the vevent with reccurence-id
|
||||
if(![emailEvent hasRecurrenceRules] || [emailEvent recurrenceId])
|
||||
{
|
||||
[parent removeChild: calendarEvent];
|
||||
[parent addChild: emailEvent];
|
||||
}
|
||||
else
|
||||
{
|
||||
//The main recurrent event has been changed, put it first and also change all the exception with the correct reccurence-id
|
||||
NSMutableArray *childrenNew;
|
||||
NSEnumerator *childrenOldEnum, *childrenNewEnum;
|
||||
iCalEvent *child;
|
||||
NSCalendarDate *oldStartDate, *newStartDate, *recId;
|
||||
NSTimeInterval interval;
|
||||
|
||||
oldStartDate = [calendarEvent startDate];
|
||||
newStartDate = [emailEvent startDate];
|
||||
interval = [newStartDate timeIntervalSinceDate: oldStartDate];
|
||||
|
||||
[parent removeChild: calendarEvent];
|
||||
childrenNew = [NSMutableArray array];
|
||||
childrenOldEnum = [[parent childrenWithTag:@"vevent"] objectEnumerator];
|
||||
while((child = [childrenOldEnum nextObject]))
|
||||
{
|
||||
[parent removeChild: child];
|
||||
if((recId = [child recurrenceId]))
|
||||
[child setRecurrenceId: [recId dateByAddingTimeInterval: interval]];
|
||||
[childrenNew addObject: child];
|
||||
}
|
||||
[parent addChild: emailEvent];
|
||||
childrenNewEnum = [childrenNew objectEnumerator];
|
||||
while((child = [childrenNewEnum nextObject]))
|
||||
{
|
||||
[parent addChild: child];
|
||||
}
|
||||
}
|
||||
|
||||
[*eventObject saveCalendar: parent];
|
||||
[*eventObject flush];
|
||||
chosenEvent = emailEvent;
|
||||
}
|
||||
else
|
||||
{
|
||||
chosenEvent = calendarEvent;
|
||||
if (![[[chosenEvent parent] method] length])
|
||||
[[chosenEvent parent] setMethod: [[emailEvent parent] method]];
|
||||
}
|
||||
}
|
||||
else
|
||||
chosenEvent = emailEvent;
|
||||
}
|
||||
|
||||
organizer = [chosenEvent organizer];
|
||||
if (![[organizer rfc822Email] length])
|
||||
[self _fixOrganizerInEvent: chosenEvent];
|
||||
}
|
||||
else
|
||||
chosenEvent = nil;
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@
|
||||
/* MailListView */
|
||||
"Sender" = "Sender";
|
||||
"Subject or Sender" = "Subject or Sender";
|
||||
"Subject or Recipient" = "Subject or Recipient";
|
||||
"To or Cc" = "To or Cc";
|
||||
"Entire Message" = "Entire Message";
|
||||
"Date" = "Date";
|
||||
|
||||
@@ -171,6 +171,7 @@
|
||||
/* MailListView */
|
||||
"Sender" = "Expéditeur";
|
||||
"Subject or Sender" = "Sujet ou expéditeur";
|
||||
"Subject or Recipient" = "Sujet ou destinataire";
|
||||
"To or Cc" = "Pour ou Copie à";
|
||||
"Entire Message" = "Message Complet";
|
||||
"Date" = "Date";
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
|
||||
#import <NGHttp/NGHttpRequest.h>
|
||||
|
||||
@@ -60,11 +61,72 @@
|
||||
|
||||
@implementation UIxMailAccountActions
|
||||
|
||||
- (NSArray *)sortChildrenRecursively: (NSArray *)children {
|
||||
NSInteger i, j;
|
||||
NSMutableArray *sortedArray;
|
||||
NSArray *unsortedChildren, *sortedChildren;
|
||||
NSDictionary *child;
|
||||
NSMutableDictionary *mutableChild;
|
||||
NSString *name1, *name2;
|
||||
|
||||
if (nil == children || 0 == [children count]) {
|
||||
return children;
|
||||
}
|
||||
|
||||
sortedArray = [NSMutableArray array];
|
||||
|
||||
for (child in children) {
|
||||
unsortedChildren = [child objectForKey:@"children"];
|
||||
sortedChildren = [self sortChildrenRecursively:unsortedChildren];
|
||||
mutableChild = [child mutableCopy];
|
||||
[mutableChild setObject:sortedChildren forKey:@"children"];
|
||||
[sortedArray addObject:mutableChild];
|
||||
}
|
||||
|
||||
for (i = 0; i < [sortedArray count] - 1; i++) {
|
||||
for (j = i + 1; j < [sortedArray count]; j++) {
|
||||
name1 = [[sortedArray objectAtIndex:i] objectForKey:@"name"];
|
||||
name2 = [[sortedArray objectAtIndex:j] objectForKey:@"name"];
|
||||
if ([name1 compare:name2 options:NSCaseInsensitiveSearch] == NSOrderedDescending) {
|
||||
[sortedArray exchangeObjectAtIndex:i withObjectAtIndex:j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sortedArray;
|
||||
}
|
||||
|
||||
- (NSDictionary *)sortMailboxesRecursively: (NSDictionary *)data {
|
||||
NSArray *mailboxes, *mailboxChildren, *sortedMailboxChildren;
|
||||
NSMutableDictionary *sortedData, *mutableMailbox;
|
||||
NSMutableArray *sortedMailboxes;
|
||||
NSDictionary *mailbox;
|
||||
|
||||
mailboxes = [data objectForKey:@"mailboxes"];
|
||||
sortedData = [data mutableCopy];
|
||||
|
||||
sortedMailboxes = [NSMutableArray array];
|
||||
for (mailbox in mailboxes) {
|
||||
mutableMailbox = [mailbox mutableCopy];
|
||||
|
||||
mailboxChildren = [mailbox objectForKey:@"children"];
|
||||
sortedMailboxChildren = [self sortChildrenRecursively:mailboxChildren];
|
||||
|
||||
[mutableMailbox setObject:sortedMailboxChildren forKey:@"children"];
|
||||
[sortedMailboxes addObject:mutableMailbox];
|
||||
}
|
||||
|
||||
[sortedData setObject:sortedMailboxes forKey:@"mailboxes"];
|
||||
|
||||
return sortedData;
|
||||
}
|
||||
|
||||
- (WOResponse *) listMailboxesAction
|
||||
{
|
||||
SOGoMailAccount *co;
|
||||
NSArray *folders;
|
||||
NSDictionary *data;
|
||||
NSAutoreleasePool *pool;
|
||||
|
||||
co = [self clientObject];
|
||||
|
||||
@@ -74,6 +136,9 @@
|
||||
folders, @"mailboxes",
|
||||
[co getInboxQuota], @"quotas",
|
||||
nil];
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
data = [self sortMailboxesRecursively: data];
|
||||
DESTROY(pool);
|
||||
|
||||
return [self responseWithStatus: 200
|
||||
andJSONRepresentation: data];
|
||||
|
||||
@@ -1368,7 +1368,7 @@
|
||||
request = [[self context] request];
|
||||
jsonRequest = [[request contentAsString] objectFromJSONString];
|
||||
|
||||
if ([[SOGoSystemDefaults sharedSystemDefaults] disableMailCleaning])
|
||||
if (![[SOGoSystemDefaults sharedSystemDefaults] enableMailCleaning])
|
||||
return [self responseWithStatus: 401];
|
||||
|
||||
if (![self isDateStringValid: [jsonRequest objectForKey: @"date"]]) {
|
||||
@@ -1401,7 +1401,8 @@
|
||||
}
|
||||
folder = [account folderWithTraversal: folderName andClassName: nil];
|
||||
// Disable clean for trash folder
|
||||
if (![folderName isEqualToString: [account trashFolderNameInContext: [self context]]])
|
||||
if (![folderName isEqualToString: [account trashFolderNameInContext: [self context]]]
|
||||
&& ![folderName isEqualToString: [account templatesFolderNameInContext: [self context]]])
|
||||
[self cleanFolderWithFolder: folder
|
||||
withQualifier: searchQualifier
|
||||
recursive: isRecursive
|
||||
|
||||
@@ -466,6 +466,11 @@
|
||||
return sort;
|
||||
}
|
||||
|
||||
- (NSString *)sanitizeFilterString:(NSString *)str
|
||||
{
|
||||
return [str stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
|
||||
}
|
||||
|
||||
- (EOQualifier *) searchQualifier
|
||||
{
|
||||
EOQualifier *qualifier, *notDeleted, *searchQualifier;
|
||||
@@ -498,8 +503,8 @@
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
filter = [filters objectAtIndex:i];
|
||||
searchBy = [filter objectForKey: @"searchBy"];
|
||||
searchInput = [filter objectForKey: @"searchInput"];
|
||||
searchBy = [self sanitizeFilterString: [filter objectForKey: @"searchBy"]];
|
||||
searchInput = [self sanitizeFilterString:[filter objectForKey: @"searchInput"]];
|
||||
|
||||
if (searchBy && searchInput)
|
||||
{
|
||||
|
||||
@@ -617,7 +617,7 @@
|
||||
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
|
||||
result = ![sd disableMailCleaning];
|
||||
result = [sd enableMailCleaning];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ See <a href=\"http://www.sogo.nu/en/support/community.html\">this page</a> for v
|
||||
"Change your Password" = "Change your Password";
|
||||
"The password was changed successfully." = "The password was changed successfully.";
|
||||
"Your password has expired, please enter a new one below" = "Your password has expired, please enter a new one below";
|
||||
"Your password is too weak. Please choose a stronger password to enhance your security" = "Your password is too weak. Please choose a stronger password to enhance your security";
|
||||
"Password must not be empty." = "Password must not be empty.";
|
||||
"The passwords do not match. Please try again." = "The passwords do not match. Please try again.";
|
||||
"Password Grace Period" = "Password Grace Period";
|
||||
@@ -130,4 +131,4 @@ See <a href=\"http://www.sogo.nu/en/support/community.html\">this page</a> for v
|
||||
"Invalid configuration for email password recovery" = "Invalid configuration for email password recovery";
|
||||
"Password recovery email in error" = "Password recovery email in error";
|
||||
"Password reset" = "Password reset";
|
||||
"Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}" = "Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}";
|
||||
"Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}" = "Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}";
|
||||
@@ -85,6 +85,7 @@
|
||||
"Change your Password" = "Changez votre mot de passe";
|
||||
"The password was changed successfully." = "Votre mot de passe a bien été changé.";
|
||||
"Your password has expired, please enter a new one below" = "Votre mot de passe est expiré, veuillez entrer un nouveau mot de passe";
|
||||
"Your password is too weak. Please choose a stronger password to enhance your security" = "Votre mot de passe est trop faible. Veuillez choisir un mot de passe plus sécurisé pour renforcer votre protection";
|
||||
"Password must not be empty." = "Le mot de passe ne doit pas être vide.";
|
||||
"The passwords do not match. Please try again." = "Les mots de passe ne sont pas identiques. Essayez de nouveau.";
|
||||
"Password Grace Period" = "Période de grâce pour le mot de passe";
|
||||
@@ -127,4 +128,4 @@
|
||||
"Invalid configuration for email password recovery" = "Configuration invalide pour la récupération de mot de passe par e-mail";
|
||||
"Password recovery email in error" = "Erreur lors de l'envoi de l'email de récupération";
|
||||
"Password reset" = "Réinitialisation de mot de passe";
|
||||
"Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}" = "Bonjour %{0},\nUne demande de changement de mot de passe a été initiée.\n\nSi vous n'êtes pas à l'origine de cet e-mail, n'en tenez pas compte.\n\nSi vous en êtes bien à l'origine, veuillez cliquer sur le lien ci-dessous pour modifier votre mot de passe: %{1}";
|
||||
"Hi %{0},\nThere was a request to change your password!\n\nIf you did not make this request then please ignore this email.\n\nOtherwise, please click this link to change your password: %{1}" = "Bonjour %{0},\nUne demande de changement de mot de passe a été initiée.\n\nSi vous n'êtes pas à l'origine de cet e-mail, n'en tenez pas compte.\n\nSi vous en êtes bien à l'origine, veuillez cliquer sur le lien ci-dessous pour modifier votre mot de passe: %{1}";
|
||||
+474
-80
@@ -40,6 +40,7 @@
|
||||
#import <SOGo/SOGoBuild.h>
|
||||
#import <SOGo/SOGoCache.h>
|
||||
#import <SOGo/SOGoCASSession.h>
|
||||
#import <SOGo/SOGoOpenIdSession.h>
|
||||
#if defined(SAML2_CONFIG)
|
||||
#import <SOGo/SOGoSAML2Session.h>
|
||||
#endif /* SAML2_ENABLE */
|
||||
@@ -52,6 +53,7 @@
|
||||
#import <SOGo/SOGoEmptyAuthenticator.h>
|
||||
#import <SOGo/SOGoMailer.h>
|
||||
#import <SOGo/SOGoAdmin.h>
|
||||
#import <SOGo/SOGoPasswordPolicy.h>
|
||||
|
||||
#if defined(MFA_CONFIG)
|
||||
#include <liboath/oath.h>
|
||||
@@ -144,6 +146,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
|
||||
- (WOCookie *) _authLocationCookie: (BOOL) cookieReset
|
||||
withName: (NSString *) cookieName
|
||||
withValue: (NSString *) _value
|
||||
{
|
||||
WOCookie *locationCookie;
|
||||
NSString *appName;
|
||||
@@ -151,7 +154,10 @@ static const NSString *kJwtKey = @"jwt";
|
||||
NSCalendarDate *date;
|
||||
|
||||
rq = [context request];
|
||||
locationCookie = [WOCookie cookieWithName: cookieName value: [rq uri]];
|
||||
if(_value)
|
||||
locationCookie = [WOCookie cookieWithName: cookieName value: _value];
|
||||
else
|
||||
locationCookie = [WOCookie cookieWithName: cookieName value: [rq uri]];
|
||||
appName = [rq applicationName];
|
||||
[locationCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
||||
if (cookieReset)
|
||||
@@ -164,17 +170,48 @@ static const NSString *kJwtKey = @"jwt";
|
||||
return locationCookie;
|
||||
}
|
||||
|
||||
- (WOCookie *) _domainCookie: (BOOL) cookieReset
|
||||
withDomain: (NSString *) _domain
|
||||
{
|
||||
WOCookie *domainCookie;
|
||||
NSString *appName;
|
||||
WORequest *rq;
|
||||
NSCalendarDate *date;
|
||||
|
||||
rq = [context request];
|
||||
domainCookie = [WOCookie cookieWithName: @"sogo-user-domain" value: _domain];
|
||||
appName = [rq applicationName];
|
||||
[domainCookie setPath: [NSString stringWithFormat: @"/%@/", appName]];
|
||||
if (cookieReset)
|
||||
{
|
||||
date = [NSCalendarDate calendarDate];
|
||||
[date setTimeZone: [NSTimeZone timeZoneForSecondsFromGMT: 0]];
|
||||
[domainCookie setExpires: [date yesterday]];
|
||||
}
|
||||
|
||||
return domainCookie;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (WOResponse *) _responseWithLDAPPolicyError: (int) error
|
||||
- (WOResponse *) _responseWithLDAPPolicyError: (int) error additionalInfos: (NSDictionary *) additionalInfos
|
||||
{
|
||||
NSDictionary *jsonError;
|
||||
|
||||
jsonError = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: error]
|
||||
forKey: @"LDAPPasswordPolicyError"];
|
||||
return [self responseWithStatus: 403
|
||||
andJSONRepresentation: jsonError];
|
||||
if (additionalInfos) {
|
||||
jsonError = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithInt:error], @"LDAPPasswordPolicyError",
|
||||
additionalInfos, @"additionalInfos",
|
||||
nil];
|
||||
} else {
|
||||
jsonError = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithInt:error], @"LDAPPasswordPolicyError",
|
||||
nil];
|
||||
}
|
||||
|
||||
return [self responseWithStatus:403
|
||||
andJSONRepresentation:jsonError];
|
||||
}
|
||||
|
||||
- (void) _checkAutoReloadWebCalendars: (SOGoUser *) loggedInUser
|
||||
@@ -197,6 +234,27 @@ static const NSString *kJwtKey = @"jwt";
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
- (void)translateAdditionalLoginInformations:(NSMutableDictionary **)additionalLoginInformations
|
||||
{
|
||||
NSDictionary *policy;
|
||||
NSMutableDictionary *translations;
|
||||
|
||||
if (additionalLoginInformations && *additionalLoginInformations) {
|
||||
if ([*additionalLoginInformations objectForKey:@"userPolicies"]) {
|
||||
translations = [[NSMutableDictionary alloc] init];
|
||||
for (policy in [*additionalLoginInformations objectForKey:@"userPolicies"]) {
|
||||
[translations setObject:[self commonLabelForKey: [policy objectForKey:@"label"]] forKey: [policy objectForKey:@"label"]];
|
||||
}
|
||||
[*additionalLoginInformations setObject:[SOGoPasswordPolicy createPasswordPolicyLabels: [*additionalLoginInformations objectForKey:@"userPolicies"] withTranslations: translations]
|
||||
forKey:@"userPolicies"];
|
||||
[translations release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@@ -210,6 +268,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
SOGoUserSettings *us;
|
||||
SOGoUser *loggedInUser;
|
||||
NSDictionary *params;
|
||||
NSMutableDictionary *additionalLoginInformations;
|
||||
NSString *username, *password, *language, *domain, *remoteHost;
|
||||
NSArray *supportedLanguages, *creds;
|
||||
|
||||
@@ -223,6 +282,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
auth = [[WOApplication application] authenticatorInContext: context];
|
||||
request = [context request];
|
||||
params = [[request contentAsString] objectFromJSONString];
|
||||
additionalLoginInformations = [[NSMutableDictionary alloc] init];
|
||||
|
||||
username = [params objectForKey: @"userName"];
|
||||
password = [params objectForKey: @"password"];
|
||||
@@ -232,9 +292,11 @@ static const NSString *kJwtKey = @"jwt";
|
||||
/* this will always be set to something more or less useful by
|
||||
* [WOHttpTransaction applyAdaptorHeadersWithHttpRequest] */
|
||||
remoteHost = [request headerForKey:@"x-webobjects-remote-host"];
|
||||
b = [auth checkLogin: username password: password domain: &domain
|
||||
perr: &err expire: &expire grace: &grace additionalInfo: &additionalLoginInformations useCache: NO];
|
||||
[self translateAdditionalLoginInformations: &additionalLoginInformations];
|
||||
|
||||
if ((b = [auth checkLogin: username password: password domain: &domain
|
||||
perr: &err expire: &expire grace: &grace useCache: NO])
|
||||
if (b
|
||||
&& (err == PolicyNoError)
|
||||
// no password policy
|
||||
&& ((expire < 0 && grace < 0) // no password policy or everything is alright
|
||||
@@ -334,7 +396,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
#endif
|
||||
|
||||
if ([us objectForKey: @"ForceResetPassword"]) {
|
||||
response = [self _responseWithLDAPPolicyError: PolicyPasswordExpired];
|
||||
response = [self _responseWithLDAPPolicyError: PolicyPasswordExpired additionalInfos: additionalLoginInformations];
|
||||
} else {
|
||||
[self _checkAutoReloadWebCalendars: loggedInUser];
|
||||
|
||||
@@ -377,7 +439,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
[self logWithFormat: @"Login from '%@' for user '%@' might not have worked - password policy: %d grace: %d expire: %d bound: %d",
|
||||
remoteHost, username, err, grace, expire, b];
|
||||
|
||||
response = [self _responseWithLDAPPolicyError: err];
|
||||
response = [self _responseWithLDAPPolicyError: err additionalInfos: additionalLoginInformations];
|
||||
}
|
||||
|
||||
if (rememberLogin)
|
||||
@@ -385,9 +447,12 @@ static const NSString *kJwtKey = @"jwt";
|
||||
else
|
||||
[response addCookie: [self _cookieWithUsername: nil]];
|
||||
|
||||
[additionalLoginInformations release];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
- (NSDictionary *) _casRedirectKeys
|
||||
{
|
||||
NSDictionary *redirectKeys;
|
||||
@@ -441,42 +506,43 @@ static const NSString *kJwtKey = @"jwt";
|
||||
if ([login isEqualToString: @"anonymous"])
|
||||
login = nil;
|
||||
if (!login)
|
||||
{
|
||||
rq = [context request];
|
||||
ticket = [rq formValueForKey: @"ticket"];
|
||||
if ([ticket length])
|
||||
{
|
||||
rq = [context request];
|
||||
ticket = [rq formValueForKey: @"ticket"];
|
||||
if ([ticket length])
|
||||
{
|
||||
casSession = [SOGoCASSession CASSessionWithTicket: ticket
|
||||
fromProxy: NO];
|
||||
login = [casSession login];
|
||||
if ([login length])
|
||||
{
|
||||
auth = [[WOApplication application]
|
||||
authenticatorInContext: context];
|
||||
casCookie = [auth cookieWithUsername: login
|
||||
andPassword: [casSession identifier]
|
||||
inContext: context];
|
||||
[casSession updateCache];
|
||||
newLocation = [rq cookieValueForKey: @"cas-location"];
|
||||
/* login callback, we expire the "cas-location" cookie, created
|
||||
below */
|
||||
casLocationCookie = [self _authLocationCookie: YES
|
||||
withName: @"cas-location"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* anonymous and no ticket, possibly a logout request from CAS
|
||||
* See: https://wiki.jasig.org/display/CASUM/Single+Sign+Out
|
||||
*/
|
||||
logoutRequest = [rq formValueForKey: @"logoutRequest"];
|
||||
if ([logoutRequest length])
|
||||
{
|
||||
[SOGoCASSession handleLogoutRequest: logoutRequest];
|
||||
return [self responseWithStatus: 200];
|
||||
}
|
||||
}
|
||||
casSession = [SOGoCASSession CASSessionWithTicket: ticket
|
||||
fromProxy: NO];
|
||||
login = [casSession login];
|
||||
if ([login length])
|
||||
{
|
||||
auth = [[WOApplication application]
|
||||
authenticatorInContext: context];
|
||||
casCookie = [auth cookieWithUsername: login
|
||||
andPassword: [casSession identifier]
|
||||
inContext: context];
|
||||
[casSession updateCache];
|
||||
newLocation = [rq cookieValueForKey: @"cas-location"];
|
||||
/* login callback, we expire the "cas-location" cookie, created
|
||||
below */
|
||||
casLocationCookie = [self _authLocationCookie: YES
|
||||
withName: @"cas-location"
|
||||
withValue: nil];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* anonymous and no ticket, possibly a logout request from CAS
|
||||
* See: https://wiki.jasig.org/display/CASUM/Single+Sign+Out
|
||||
*/
|
||||
logoutRequest = [rq formValueForKey: @"logoutRequest"];
|
||||
if ([logoutRequest length])
|
||||
{
|
||||
[SOGoCASSession handleLogoutRequest: logoutRequest];
|
||||
return [self responseWithStatus: 200];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ticket = nil;
|
||||
|
||||
@@ -500,7 +566,8 @@ static const NSString *kJwtKey = @"jwt";
|
||||
newLocation = [SOGoCASSession CASURLWithAction: @"login"
|
||||
andParameters: [self _casRedirectKeys]];
|
||||
casLocationCookie = [self _authLocationCookie: NO
|
||||
withName: @"cas-location"];
|
||||
withName: @"cas-location"
|
||||
withValue: nil];
|
||||
}
|
||||
response = [self redirectToLocation: newLocation];
|
||||
if (casCookie)
|
||||
@@ -511,6 +578,131 @@ static const NSString *kJwtKey = @"jwt";
|
||||
return response;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) _openidDefaultAction: (NSString *) _domain
|
||||
{
|
||||
WOResponse *response;
|
||||
NSString *login, *redirectLocation, *serverUrl;
|
||||
NSString *sessionState, *code, *refreshTokenValue;
|
||||
NSURL *newLocation, *oldLocation;
|
||||
NSDictionary *formValues;
|
||||
SOGoUser *loggedInUser;
|
||||
WOCookie *openIdCookie, *openIdCookieLocation, *openIdRefreshCookie, *domainCookie;
|
||||
WORequest *rq;
|
||||
SOGoWebAuthenticator *auth;
|
||||
SOGoOpenIdSession *openIdSession;
|
||||
id value;
|
||||
|
||||
openIdCookie = nil;
|
||||
openIdCookieLocation = nil;
|
||||
openIdRefreshCookie = nil;
|
||||
domainCookie = nil;
|
||||
newLocation = nil;
|
||||
|
||||
rq = [context request];
|
||||
|
||||
//Check if the domain is stored in a cookie if not given
|
||||
if(_domain == nil || [_domain length] == 0)
|
||||
_domain = [rq cookieValueForKey: @"sogo-user-domain"]; //_domain can still be nil aftert his
|
||||
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSession: _domain];
|
||||
|
||||
if(![openIdSession sessionIsOk])
|
||||
{
|
||||
return [NSException exceptionWithHTTPStatus: 502 /* Bad OpenId Configuration */
|
||||
reason: [NSString stringWithFormat: @"OpenId server not found or has unexpected behavior, contact your admin."]];
|
||||
}
|
||||
|
||||
login = [[context activeUser] login];
|
||||
if ([login isEqualToString: @"anonymous"])
|
||||
login = nil;
|
||||
if (!login)
|
||||
{
|
||||
//You get here if you nerver been logged in or if you token is expired
|
||||
serverUrl = [[context serverURL] absoluteString];
|
||||
redirectLocation = [NSString stringWithFormat: @"%@/%@/", serverUrl, [rq applicationName]];
|
||||
if((formValues = [rq formValues]) && [formValues objectForKey: @"code"])
|
||||
{
|
||||
//You get here if this is the callback of openid after you logged in
|
||||
|
||||
//NOT MANDATORY
|
||||
// value = [formValues objectForKey: @"session_state"];
|
||||
// if ([value isKindOfClass: [NSArray class]])
|
||||
// sessionState = [value lastObject];
|
||||
// else
|
||||
// sessionState = value;
|
||||
|
||||
value = [formValues objectForKey: @"code"];
|
||||
if ([value isKindOfClass: [NSArray class]])
|
||||
code = [value lastObject];
|
||||
else
|
||||
code = value;
|
||||
[openIdSession fetchToken: code redirect: redirectLocation];
|
||||
login = [openIdSession login: @""];
|
||||
if ([login length])
|
||||
{
|
||||
auth = [[WOApplication application] authenticatorInContext: context];
|
||||
openIdCookie = [auth cookieWithUsername: login
|
||||
andPassword: [openIdSession getToken]
|
||||
inContext: context];
|
||||
}
|
||||
newLocation = [rq cookieValueForKey: @"openid-location"];
|
||||
openIdCookieLocation = [self _authLocationCookie: YES withName: @"openid-location" withValue: nil];
|
||||
domainCookie = [self _domainCookie: YES withDomain: _domain];
|
||||
}
|
||||
// else if((formValues = [rq formValues]) && [formValues objectForKey: @"action"])
|
||||
// {
|
||||
// value = [formValues objectForKey: @"action"];
|
||||
// if ([value isKindOfClass: [NSArray class]])
|
||||
// code = [value lastObject];
|
||||
// else
|
||||
// code = value;
|
||||
// if([code isEqualToString:@"redirect"])
|
||||
// {
|
||||
// //this action onlye serve to make a redirection to openId server after a GET request
|
||||
// newLocation = [openIdSession loginUrl: redirectLocation];
|
||||
// }
|
||||
//}
|
||||
else
|
||||
{
|
||||
// //You get here the first time you access sogo, it redirect to last location
|
||||
// if([[rq method] isEqualToString: @"POST"])
|
||||
// //To avoid making a redirection to openid server after a post request, we first redirect to a get method
|
||||
// newLocation = [NSString stringWithFormat: @"%@?action=redirect", redirectLocation];
|
||||
// else
|
||||
if(_domain != nil && [_domain length] > 0)
|
||||
{
|
||||
//add the domain cookie to get it after the redirect
|
||||
domainCookie = [self _domainCookie: NO withDomain: _domain];
|
||||
}
|
||||
newLocation = [openIdSession loginUrl: redirectLocation];
|
||||
openIdCookieLocation = [self _authLocationCookie: NO withName: @"openid-location" withValue: nil];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!newLocation)
|
||||
{
|
||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||
newLocation = [NSString stringWithFormat: @"%@%@", oldLocation, [login stringByEscapingURL]];
|
||||
}
|
||||
|
||||
loggedInUser = [SOGoUser userWithLogin: login];
|
||||
[self _checkAutoReloadWebCalendars: loggedInUser];
|
||||
}
|
||||
|
||||
response = [self redirectToLocation: newLocation];
|
||||
if (openIdCookie)
|
||||
[response addCookie: openIdCookie];
|
||||
if (openIdCookieLocation)
|
||||
[response addCookie: openIdCookieLocation];
|
||||
if(domainCookie)
|
||||
[response addCookie: domainCookie];
|
||||
//[response setStatus: 303];
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined(SAML2_CONFIG)
|
||||
- (id <WOActionResults>) _saml2DefaultAction
|
||||
{
|
||||
@@ -534,7 +726,8 @@ static const NSString *kJwtKey = @"jwt";
|
||||
newLocation = [rq cookieValueForKey: @"saml2-location"];
|
||||
if (newLocation)
|
||||
saml2LocationCookie = [self _authLocationCookie: YES
|
||||
withName: @"saml2-location"];
|
||||
withName: @"saml2-location"
|
||||
withValue: nil];
|
||||
else
|
||||
{
|
||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||
@@ -549,7 +742,8 @@ static const NSString *kJwtKey = @"jwt";
|
||||
{
|
||||
newLocation = [SOGoSAML2Session authenticationURLInContext: context];
|
||||
saml2LocationCookie = [self _authLocationCookie: NO
|
||||
withName: @"saml2-location"];
|
||||
withName: @"saml2-location"
|
||||
withValue: nil];
|
||||
}
|
||||
|
||||
response = [self redirectToLocation: newLocation];
|
||||
@@ -570,43 +764,191 @@ static const NSString *kJwtKey = @"jwt";
|
||||
login = nil;
|
||||
|
||||
if (login)
|
||||
{
|
||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||
response
|
||||
= [self redirectToLocation: [NSString stringWithFormat: @"%@%@",
|
||||
oldLocation,
|
||||
[[SOGoUser getEncryptedUsernameIfNeeded:login request: [context request]] stringByEscapingURL]]];
|
||||
}
|
||||
{
|
||||
oldLocation = [[self clientObject] baseURLInContext: context];
|
||||
response = [self redirectToLocation: [NSString stringWithFormat: @"%@%@", oldLocation,
|
||||
[[SOGoUser getEncryptedUsernameIfNeeded:login request: [context request]] stringByEscapingURL]]];
|
||||
}
|
||||
else
|
||||
{
|
||||
oldLocation = [[context request] uri];
|
||||
if ([context clientObject]
|
||||
&& ![oldLocation hasSuffix: @"/"]
|
||||
&& ![oldLocation hasSuffix: @"/view"])
|
||||
response = [self redirectToLocation:
|
||||
[NSString stringWithFormat: @"%@/", oldLocation]];
|
||||
else
|
||||
response = self;
|
||||
}
|
||||
{
|
||||
oldLocation = [[context request] uri];
|
||||
if ([context clientObject] && ![oldLocation hasSuffix: @"/"] && ![oldLocation hasSuffix: @"/view"])
|
||||
response = [self redirectToLocation: [NSString stringWithFormat: @"%@/", oldLocation]];
|
||||
else
|
||||
response = self;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (WOResponse *) connectNameAction
|
||||
{
|
||||
WOResponse *response;
|
||||
WORequest *request;
|
||||
NSDictionary *params;
|
||||
NSString *username, *language, *domain, *type, *serverUrl, *redirectLocation;
|
||||
NSRange r;
|
||||
|
||||
request = [context request];
|
||||
params = [[request contentAsString] objectFromJSONString];
|
||||
|
||||
username = [params objectForKey: @"userName"];
|
||||
|
||||
//Extract the domain
|
||||
r = [username rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
domain = [username substringFromIndex: r.location+1];
|
||||
type = [[SOGoSystemDefaults sharedSystemDefaults] getLoginTypeForDomain: domain];
|
||||
if(type != nil)
|
||||
{
|
||||
if([type isEqualToString: @"plain"])
|
||||
{
|
||||
//Only reload the page with the name
|
||||
serverUrl = [[context serverURL] absoluteString];
|
||||
redirectLocation = [NSString stringWithFormat: @"%@/%@/login?hint=%@", serverUrl, [request applicationName], username];
|
||||
//response = [self redirectToLocation: [NSString stringWithFormat: @"%@/", redirectLocation]];
|
||||
response = [self responseWithStatus: 200 andJSONRepresentation:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys: redirectLocation, @"redirect", nil]];
|
||||
}
|
||||
else if([type isEqualToString: @"openid"])
|
||||
{
|
||||
SOGoOpenIdSession *openIdSession;
|
||||
WOCookie *domainCookie, *openIdCookieLocation;
|
||||
|
||||
//With openId, the user will be redirected to the openid server for login
|
||||
//With set the domain in a cookie to know it after the openid does the callbacl
|
||||
serverUrl = [[context serverURL] absoluteString];
|
||||
redirectLocation = [NSString stringWithFormat: @"%@/%@/", serverUrl, [request applicationName]];
|
||||
|
||||
openIdSession = [SOGoOpenIdSession OpenIdSession: domain];
|
||||
|
||||
domainCookie = [self _domainCookie: NO withDomain: domain];
|
||||
openIdCookieLocation = [self _authLocationCookie: NO withName: @"openid-location" withValue: redirectLocation];
|
||||
|
||||
response = [self responseWithStatus: 200 andJSONRepresentation:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys: [openIdSession loginUrl: redirectLocation], @"redirect", nil]];
|
||||
[response addCookie: domainCookie];
|
||||
[response addCookie: openIdCookieLocation];
|
||||
}
|
||||
else if([type isEqualToString: @"cas"] || [type isEqualToString: @"saml2"])
|
||||
{
|
||||
[self logWithFormat: @"Unsupported type for now: %@", type];
|
||||
response = [self responseWithStatus: 400
|
||||
andString: @"Domain Authentication type not supported"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Unknown type: %@", type];
|
||||
response = [self responseWithStatus: 400
|
||||
andString: @"Unknwon Authentication type"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Auth type for Domain given is not set or there is no default value: %@", domain];
|
||||
response = [self responseWithStatus: 400
|
||||
andString: @"Domain unknown"];
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Domain is required but not found for user recovery exception for user %@", username];
|
||||
response = [self responseWithStatus: 400
|
||||
andString: @"Domain needed in the login"];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
- (id <WOActionResults>) defaultAction
|
||||
{
|
||||
NSString *authenticationType;
|
||||
NSString *authenticationType, *loginDomain, *type, *_domain;
|
||||
SOGoSystemDefaults* sd;
|
||||
id <WOActionResults> result;
|
||||
|
||||
authenticationType = [[SOGoSystemDefaults sharedSystemDefaults]
|
||||
authenticationType];
|
||||
if ([authenticationType isEqualToString: @"cas"])
|
||||
result = [self _casDefaultAction];
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([authenticationType isEqualToString: @"saml2"])
|
||||
result = [self _saml2DefaultAction];
|
||||
#endif /* SAML2_CONFIG */
|
||||
else
|
||||
result = [self _standardDefaultAction];
|
||||
loginDomain = nil;
|
||||
sd = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
if([sd doesLoginTypeByDomain])
|
||||
{
|
||||
NSString *login;
|
||||
//In this mode sogo will ask the mail of the user before doing any authentication
|
||||
//Check if a user is already logged in
|
||||
|
||||
_domain = [[context request] cookieValueForKey: @"sogo-user-domain"]; //_domain can still be nil aftert his
|
||||
if(_domain != nil)
|
||||
{
|
||||
//This is a callback of an openid session.
|
||||
return [self _openidDefaultAction: _domain];
|
||||
}
|
||||
|
||||
login = [[context activeUser] login];
|
||||
if ([login isEqualToString: @"anonymous"])
|
||||
login = nil;
|
||||
if(!login && !_domain)
|
||||
return [self _standardDefaultAction];
|
||||
else
|
||||
{
|
||||
//User already logged in. Extract the domain in that case
|
||||
NSRange r;
|
||||
r = [login rangeOfString: @"@"];
|
||||
if (r.location != NSNotFound)
|
||||
{
|
||||
loginDomain = [login substringFromIndex: r.location+1];
|
||||
type = [sd getLoginTypeForDomain: loginDomain];
|
||||
if(type)
|
||||
{
|
||||
if([type isEqualToString: @"plain"])
|
||||
{
|
||||
result = [self _standardDefaultAction];
|
||||
}
|
||||
else if([type isEqualToString: @"openid"])
|
||||
{
|
||||
result = [self _openidDefaultAction: loginDomain];
|
||||
}
|
||||
else if([type isEqualToString: @"cas"] || [type isEqualToString: @"saml2"])
|
||||
{
|
||||
[self logWithFormat: @"Unsupported type for now: %@", type];
|
||||
result = [self responseWithStatus: 400
|
||||
andString: @"Domain Authentication type not supported"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Unknown type: %@", type];
|
||||
result = [self responseWithStatus: 400
|
||||
andString: @"Unknwon Authentication type"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self logWithFormat: @"Auth type for Domain given is not set or there is no default value: %@", loginDomain];
|
||||
result = [self responseWithStatus: 400
|
||||
andString: @"Domain unknown"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
loginDomain = nil;
|
||||
result = [self _standardDefaultAction];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
authenticationType = [sd authenticationType];
|
||||
|
||||
if ([authenticationType isEqualToString: @"cas"])
|
||||
result = [self _casDefaultAction];
|
||||
else if ([authenticationType isEqualToString: @"openid"])
|
||||
result = [self _openidDefaultAction: loginDomain];
|
||||
#if defined(SAML2_CONFIG)
|
||||
else if ([authenticationType isEqualToString: @"saml2"])
|
||||
result = [self _saml2DefaultAction];
|
||||
#endif /* SAML2_CONFIG */
|
||||
else
|
||||
result = [self _standardDefaultAction];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -636,6 +978,57 @@ static const NSString *kJwtKey = @"jwt";
|
||||
return ([[self loginDomains] count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL) doLoginUsernameFirst
|
||||
{
|
||||
return [[SOGoSystemDefaults sharedSystemDefaults] doesLoginTypeByDomain];
|
||||
}
|
||||
|
||||
- (BOOL) doFullLogin
|
||||
{
|
||||
//Either we directly do the full login (meaning the user inputs its username and password)
|
||||
//Or we do it in two times:
|
||||
//phase 1: user types its username first -> only show the username input
|
||||
//phase 2: user types its password -> show all inputs
|
||||
//In phase 2, the username will be in the query at key "login"
|
||||
if([self doLoginUsernameFirst]){
|
||||
WORequest *rq;
|
||||
BOOL hasLogin;
|
||||
NSDictionary *formValues;
|
||||
|
||||
rq = [context request];
|
||||
hasLogin = ((formValues=[rq formValues]) && [formValues objectForKey: @"hint"]);
|
||||
return hasLogin;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) doPartialLogin
|
||||
{
|
||||
return ![self doFullLogin];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *) getLoginHint
|
||||
{
|
||||
id value;
|
||||
WORequest *rq;
|
||||
NSString* login;
|
||||
NSDictionary *formValues;
|
||||
|
||||
login = @"";
|
||||
|
||||
rq = [context request];
|
||||
if((formValues=[rq formValues]) && (value=[formValues objectForKey: @"hint"]))
|
||||
{
|
||||
if ([value isKindOfClass: [NSArray class]])
|
||||
login = [value lastObject];
|
||||
else
|
||||
login = value;
|
||||
}
|
||||
return login;
|
||||
}
|
||||
|
||||
- (BOOL) hasPasswordRecovery
|
||||
{
|
||||
return [[SOGoSystemDefaults sharedSystemDefaults] isPasswordRecoveryEnabled];
|
||||
@@ -808,7 +1201,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
}
|
||||
}
|
||||
else
|
||||
response = [self _responseWithLDAPPolicyError: error];
|
||||
response = [self _responseWithLDAPPolicyError: error additionalInfos: nil];
|
||||
}
|
||||
|
||||
return response;
|
||||
@@ -999,8 +1392,7 @@ static const NSString *kJwtKey = @"jwt";
|
||||
message = [[request contentAsString] objectFromJSONString];
|
||||
username = [message objectForKey: @"userName"];
|
||||
domain = [message objectForKey: @"domain"];
|
||||
if ([[SOGoSystemDefaults sharedSystemDefaults]
|
||||
isPasswordRecoveryEnabled]) {
|
||||
if ([[SOGoSystemDefaults sharedSystemDefaults] isPasswordRecoveryEnabled]) {
|
||||
// If no domain, try to retrieve domain from username
|
||||
if (nil != domain && domain != [NSNull null]) {
|
||||
domainName = domain;
|
||||
@@ -1062,8 +1454,9 @@ static const NSString *kJwtKey = @"jwt";
|
||||
|
||||
- (BOOL) hasUrlCreateAccount
|
||||
{
|
||||
return ([[SOGoSystemDefaults sharedSystemDefaults]
|
||||
urlCreateAccount] != nil);
|
||||
NSString *url;
|
||||
url = [[SOGoSystemDefaults sharedSystemDefaults] urlCreateAccount];
|
||||
return (url != nil && [url length] > 0);
|
||||
}
|
||||
|
||||
- (NSString *) urlCreateAccount
|
||||
@@ -1072,4 +1465,5 @@ static const NSString *kJwtKey = @"jwt";
|
||||
urlCreateAccount];
|
||||
}
|
||||
|
||||
|
||||
@end /* SOGoRootPage */
|
||||
Loaded 100 of 214 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user