diff --git a/README.md b/README.md index 911586d..d46b2cb 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,68 @@ -Baïkal for YunoHost -------------------- +# Baïkal for YunoHost -[Baïkal](http://baikal-server.com/) is a Cal and CardDAV server, based on -sabre/dav, that includes an administrative interface for easy management. +[![Integration level](https://dash.yunohost.org/integration/baikal.svg)](https://dash.yunohost.org/appci/app/baikal) ![](https://ci-apps.yunohost.org/ci/badges/baikal.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/baikal.maintain.svg) +[![Install Baïkal with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=baikal) -**Shipped version:** 0.4.4 +> *This package allow you to install Baïkal quickly and simply on a YunoHost server. +If you don't have YunoHost, please see [here](https://yunohost.org/#/install) to know how to install and enjoy it.* -## TODO +## Overview +[Baïkal](http://baikal-server.com/) is a CalDAV and CardDAV server, based on +sabre/dav, that includes an administration interface for easy management. - * Update `config.php` and `config.system.php` at upgrade +**Shipped version:** 0.7.1 -## Links ## +## Screenshots -**Baïkal**: http://baikal-server.com/ +## Demo -**YunoHost**: https://yunohost.org/ +* [YunoHost demo](https://demo.yunohost.org/baikal/admin/) + * Accounts: + * `demo/demo` then `admin/demo` + +## Configuration + +## Documentation + + * Official documentation: http://sabre.io/baikal/ + * YunoHost documentation: https://yunohost.org/#/app_baikal + +## YunoHost specific features + +In addition to Baïkal core features, the following are made available with this package: + + * Serve `/.well-known` paths for CalDAV and CardDAV on the domain + +#### Multi-users support + +#### Supported architectures + +* x86-64 - [![Build Status](https://ci-apps.yunohost.org/ci/logs/baikal%20%28Apps%29.svg)](https://ci-apps.yunohost.org/ci/apps/baikal/) +* ARMv8-A - [![Build Status](https://ci-apps-arm.yunohost.org/ci/logs/baikal%20%28Apps%29.svg)](https://ci-apps-arm.yunohost.org/ci/apps/baikal/) + +## Limitations + +## Additional information + +* There is a breaking change in the management of the administrator password when upgrading to 0.7.0. You must change the admin password! +* To be able to change the admin password, please visit the page: `https://you.domain.tld/yunohost/admin/#/apps/baikal/actions` and set a new password. + +## Links + + * Report a bug: https://github.com/YunoHost-Apps/baikal_ynh/issues + * Baïkal website: http://baikal-server.com/ + * Upstream app repository: https://github.com/sabre-io/Baikal + * YunoHost website: https://yunohost.org/ + +--- + +## Developers infos + +Please do your pull request to the [testing branch](https://github.com/YunoHost-Apps/baikal_ynh/tree/testing). + +To try the testing branch, please proceed like that. +``` +sudo yunohost app install https://github.com/YunoHost-Apps/baikal_ynh/tree/testing --debug +or +sudo yunohost app upgrade baikal -u https://github.com/YunoHost-Apps/baikal_ynh/tree/testing --debug +``` diff --git a/actions.toml b/actions.toml new file mode 100644 index 0000000..aa446e3 --- /dev/null +++ b/actions.toml @@ -0,0 +1,10 @@ +[reset_admin_password] +name = "Reset the admin password" +command = "/bin/bash scripts/actions/reset_admin_password" +accepted_return_codes = [0] +description = "Change the admin password of the app." + [reset_admin_password.arguments] + [reset_admin_password.arguments.password] + type = "password" + ask.en = "Set the password for the administration" + ask.fr = "Définissez le mot de passe pour l'administration" \ No newline at end of file diff --git a/conf/app.src b/conf/app.src new file mode 100644 index 0000000..7eb737e --- /dev/null +++ b/conf/app.src @@ -0,0 +1,6 @@ +SOURCE_URL=https://github.com/sabre-io/Baikal/releases/download/0.7.1/baikal-0.7.1.zip +SOURCE_SUM=dade7d8dd740ed66f6d87368a6ceff845938ba57d7f45063f8b9cea6278c1c0a +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=zip +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= diff --git a/conf/baikal.yaml b/conf/baikal.yaml new file mode 100644 index 0000000..47135ed --- /dev/null +++ b/conf/baikal.yaml @@ -0,0 +1,29 @@ +system: + configured_version: '0.7.1' + timezone: '__TIMEZONE__' + card_enabled: true + cal_enabled: true + invite_from: 'noreply@localhost' + dav_auth_type: 'LDAP-UserBind' + admin_passwordhash: __PASSWORDHASH__ + auth_realm: BaikalDAV + base_uri: '__PATH__' +# Auth Backend LDAP-UserBind; LDAP URI + dav_ldap_uri: 'ldap://127.0.0.1/' +# Auth Backend LDAP-UserBind; Template for userbind +# %n => username +# %u => user part of username when it is an email +# %u => domain part of username when it is an email + dav_ldap_dn_template: 'uid=%n,ou=users,dc=yunohost,dc=org' +# Auth Backend LDAP-UserBind; attribute for displayname + dav_ldap_displayname_attr: 'cn' +# Auth Backend LDAP-UserBind; attribute for email + dav_ldap_email_attr: 'mail' +database: + encryption_key: '__DESKEY__' + sqlite_file: "absolute/path/to/Specific/db/db.sqlite" + mysql: true + mysql_host: 'localhost' + mysql_dbname: '__DBNAME__' + mysql_username: '__DBUSER__' + mysql_password: '__DBPASS__' diff --git a/conf/config.php b/conf/config.php deleted file mode 100644 index 5b71d3a..0000000 --- a/conf/config.php +++ /dev/null @@ -1,62 +0,0 @@ - -# All rights reserved -# -# http://baikal-server.com -# -# This script is part of the Baïkal Server project. The Baïkal -# Server project 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 of the License, or (at your option) any later version. -# -# The GNU General Public License can be found at -# http://www.gnu.org/copyleft/gpl.html. -# -# This script 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. -# -# This copyright notice MUST APPEAR in all copies of the script! -# -############################################################################## - -############################################################################## -# Required configuration -# You *have* to review these settings for Baïkal to run properly -# - -# Timezone of your users, if unsure, check http://en.wikipedia.org/wiki/List_of_tz_database_time_zones -define("PROJECT_TIMEZONE", '#TIMEZONE#'); - -# CardDAV ON/OFF switch; default TRUE -define("BAIKAL_CARD_ENABLED", TRUE); - -# CalDAV ON/OFF switch; default TRUE -define("BAIKAL_CAL_ENABLED", TRUE); - -# WebDAV authentication type; default Digest -define("BAIKAL_DAV_AUTH_TYPE", 'LDAP-UserBind'); - -# Auth Backend LDAP-UserBind; LDAP URI -define("BAIKAL_DAV_LDAP_URI", 'ldap://127.0.0.1/'); - -# Auth Backend LDAP-UserBind; Template for userbind -# %n => username -# %u => user part of username when it is an email -# %u => domain part of username when it is an email -define("BAIKAL_DAV_LDAP_DN_TEMPLATE", 'uid=%n,ou=users,dc=yunohost,dc=org'); - -# Auth Backend LDAP-UserBind; attribute for displayname -define("BAIKAL_DAV_LDAP_DISPLAYNAME_ATTR", 'cn'); - -# Auth Backend LDAP-UserBind; attribute for email -define("BAIKAL_DAV_LDAP_EMAIL_ATTR", 'mail'); - -# Baïkal Web admin password hash; Set via Baïkal Web Admin -define("BAIKAL_ADMIN_PASSWORDHASH", '#PASSWORDHASH#'); diff --git a/conf/config.system.php b/conf/config.system.php deleted file mode 100644 index 72af22d..0000000 --- a/conf/config.system.php +++ /dev/null @@ -1,72 +0,0 @@ - -# All rights reserved -# -# http://baikal-server.com -# -# This script is part of the Baïkal Server project. The Baïkal -# Server project 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 of the License, or (at your option) any later version. -# -# The GNU General Public License can be found at -# http://www.gnu.org/copyleft/gpl.html. -# -# This script 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. -# -# This copyright notice MUST APPEAR in all copies of the script! -# -############################################################################## - -############################################################################## -# System configuration -# Should not be changed, unless YNWYD -# -# RULES -# 0. All folder pathes *must* be suffixed by "/" -# 1. All URIs *must* be suffixed by "/" if pointing to a folder -# - -# If you change this value, you'll have to re-generate passwords for all your users -define("BAIKAL_AUTH_REALM", 'BaikalDAV'); - -# Should begin and end with a "/" -define("BAIKAL_CARD_BASEURI", "#PATH#/card.php/"); - -# Should begin and end with a "/" -define("BAIKAL_CAL_BASEURI", "#PATH#/cal.php/"); - -# Should begin and end with a "/" -define("BAIKAL_DAV_BASEURI", "#PATH#/dav.php/"); - -# Define path to Baïkal Database SQLite file -define("PROJECT_SQLITE_FILE", PROJECT_PATH_SPECIFIC . "db/db.sqlite"); - -# MySQL > Use MySQL instead of SQLite ? -define("PROJECT_DB_MYSQL", TRUE); - -# MySQL > Host, including ':portnumber' if port is not the default one (3306) -define("PROJECT_DB_MYSQL_HOST", 'localhost'); - -# MySQL > Database name -define("PROJECT_DB_MYSQL_DBNAME", '#DBNAME#'); - -# MySQL > Username -define("PROJECT_DB_MYSQL_USERNAME", '#DBUSER#'); - -# MySQL > Password -define("PROJECT_DB_MYSQL_PASSWORD", '#DBPASS#'); - -# A random 32 bytes key that will be used to encrypt data -define("BAIKAL_ENCRYPTION_KEY", '#DESKEY#'); - -# The currently configured Baïkal version -define("BAIKAL_CONFIGURED_VERSION", '0.4.4'); diff --git a/conf/nginx.conf b/conf/nginx.conf index d7b75ab..db0ab0d 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -1,27 +1,32 @@ -location #LOCATION# { - alias #DESTDIR#/html; - if ($scheme = http) { - rewrite ^ https://$server_name$request_uri? permanent; - } - - index index.php; - - location ~ ^(.+\.php)(.*)$ { - fastcgi_split_path_info ^(.+\.php)(.*)$; - fastcgi_pass unix:/var/run/php5-fpm.sock; - include fastcgi_params; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param SCRIPT_FILENAME $request_filename; - } +location = /.well-known/carddav { + return 301 https://$server_name__PATH__/card.php; +} +location = /.well-known/caldav { + return 301 https://$server_name__PATH__/cal.php; } -location ~ ^#PATH#/(\.|Core|Specific) { +#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/ { + + # Path to source + alias __FINALPATH__/html/; + + # Force usage of https + if ($scheme = http) { + rewrite ^ https://$server_name$request_uri? permanent; + } + + index index.php; + + location ~ ^(.+\.php)(.*)$ { + include fastcgi_params; + fastcgi_split_path_info ^(.+\.php)(.*)$; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_pass unix:/var/run/php/php__PHPVERSION__-fpm-__NAME__.sock; + } + + location ~ ^__PATH__/(\.|Core|Specific) { deny all; -} - -location /.well-known/carddav { - rewrite ^(.*)$ #PATH#/card.php redirect; -} -location /.well-known/caldav { - rewrite ^(.*)$ #PATH#/cal.php redirect; + } } diff --git a/conf/php-fpm.conf b/conf/php-fpm.conf new file mode 100644 index 0000000..cc8c244 --- /dev/null +++ b/conf/php-fpm.conf @@ -0,0 +1,430 @@ +; Start a new pool named 'www'. +; the variable $pool can be used in any directive and will be replaced by the +; pool name ('www' here) +[__NAMETOCHANGE__] + +; Per pool prefix +; It only applies on the following directives: +; - 'access.log' +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or /usr) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = __USER__ +group = __USER__ + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +listen = /var/run/php/php__PHPVERSION__-fpm-__NAMETOCHANGE__.sock + +; Set listen(2) backlog. +; Default Value: 511 (-1 on FreeBSD and OpenBSD) +;listen.backlog = 511 + +; Set permissions for unix socket, if one is used. In Linux, read/write +; permissions must be set in order to allow connections from a web server. Many +; BSD-derived systems allow connections regardless of permissions. +; Default Values: user and group are set as the running user +; mode is set to 0660 +listen.owner = www-data +listen.group = www-data +;listen.mode = 0660 +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a comma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = +;listen.acl_groups = + +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. +; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original +; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address +; must be separated by a comma. If this value is left blank, connections will be +; accepted from any ip address. +; Default Value: any +;listen.allowed_clients = 127.0.0.1 + +; Specify the nice(2) priority to apply to the pool processes (only if set) +; The value can vary from -19 (highest priority) to 20 (lower priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool processes will inherit the master process priority +; unless it specified otherwise +; Default Value: no set +; process.priority = -19 + +; Set the process dumpable flag (PR_SET_DUMPABLE prctl) even if the process user +; or group is differrent than the master process user. It allows to create process +; core dump and ptrace the process for the pool user. +; Default Value: no +; process.dumpable = yes + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 6 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = 3 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 3 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 5 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; + +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +;pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. It shows the following informations: +; pool - the name of the pool; +; process manager - static, dynamic or ondemand; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; +; idle processes - the number of idle processes; +; active processes - the number of active processes; +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; +; max children reached - number of times, the process limit has been reached, +; when pm tries to start more children (works only for +; pm 'dynamic' and 'ondemand'); +; Value are updated in real time. +; Example output: +; pool: www +; process manager: static +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; +; By default the status page output is formatted as text/plain. Passing either +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: +; http://www.foo.bar/status +; http://www.foo.bar/status?json +; http://www.foo.bar/status?html +; http://www.foo.bar/status?xml +; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: +; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full +; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full +; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then informations are related to the +; last request the process has served. Otherwise informations are related to +; the current request being served. +; Example output: +; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: /usr/share/php/7.0/fpm/status.html +; +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;pm.status_path = /status + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. This could be used to test from outside +; that FPM is alive and responding, or to +; - create a graph of FPM availability (rrd or such); +; - remove a server from a group if it is not responding (load balancing); +; - trigger alerts for the operating team (24/7). +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;ping.path = /ping + +; This directive may be used to customize the response of a ping request. The +; response is formatted as text/plain with a 200 response code. +; Default Value: pong +;ping.response = pong + +; The access log file +; Default: not set +;access.log = log/$pool.access.log + +; The access log format. +; The following syntax is allowed +; %%: the '%' character +; %C: %CPU used by the request +; it can accept the following format: +; - %{user}C for user CPU only +; - %{system}C for system CPU only +; - %{total}C for user + system CPU (default) +; %d: time taken to serve the request +; it can accept the following format: +; - %{seconds}d (default) +; - %{miliseconds}d +; - %{mili}d +; - %{microseconds}d +; - %{micro}d +; %e: an environment variable (same as $_ENV or $_SERVER) +; it must be associated with embraces to specify the name of the env +; variable. Some exemples: +; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e +; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e +; %f: script filename +; %l: content-length of the request (for POST request only) +; %m: request method +; %M: peak of memory allocated by PHP +; it can accept the following format: +; - %{bytes}M (default) +; - %{kilobytes}M +; - %{kilo}M +; - %{megabytes}M +; - %{mega}M +; %n: pool name +; %o: output header +; it must be associated with embraces to specify the name of the header: +; - %{Content-Type}o +; - %{X-Powered-By}o +; - %{Transfert-Encoding}o +; - .... +; %p: PID of the child that serviced the request +; %P: PID of the parent of the child that serviced the request +; %q: the query string +; %Q: the '?' character if query string exists +; %r: the request URI (without the query string, see %q and %Q) +; %R: remote IP address +; %s: status (response code) +; %t: server time the request was received +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %T: time the log has been written (the request has finished) +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %u: remote user +; +; Default: "%R - %u %t \"%m %r\" %s" +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" + +; The log file for slow requests +; Default Value: not set +; Note: slowlog is mandatory if request_slowlog_timeout is set +;slowlog = log/$pool.log.slow + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_slowlog_timeout = 0 + +; The timeout for serving a single request after which the worker process will +; be killed. This option should be used when the 'max_execution_time' ini option +; does not stop script execution for some reason. A value of '0' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +request_terminate_timeout = 1d + +; Set open file descriptor rlimit. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Chroot to this directory at the start. This value must be defined as an +; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. +; Note: chrooting is a great security feature and should be used whenever +; possible. However, all PHP paths will be relative to the chroot +; (error_log, sessions.save_path, ...). +; Default Value: not set +;chroot = + +; Chdir to this directory at the start. +; Note: relative path can be used. +; Default Value: current directory or / when chroot +chdir = __FINALPATH__ + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +;catch_workers_output = yes + +; Clear environment in FPM workers +; Prevents arbitrary environment variables from reaching FPM worker processes +; by clearing the environment in workers before env vars specified in this +; pool configuration are added. +; Setting to "no" will make all environment variables available to PHP code +; via getenv(), $_ENV and $_SERVER. +; Default Value: yes +;clear_env = no + +; Limits the extensions of the main script FPM will allow to parse. This can +; prevent configuration mistakes on the web server side. You should only limit +; FPM to .php extensions to prevent malicious users to use other extensions to +; execute php code. +; Note: set an empty value to allow all extensions. +; Default Value: .php +;security.limit_extensions = .php .php3 .php4 .php5 .php7 + +; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from +; the current environment. +; Default Value: clean env +;env[HOSTNAME] = $HOSTNAME +;env[PATH] = /usr/local/bin:/usr/bin:/bin +;env[TMP] = /tmp +;env[TMPDIR] = /tmp +;env[TEMP] = /tmp + +; Additional php.ini defines, specific to this pool of workers. These settings +; overwrite the values previously defined in the php.ini. The directives are the +; same as the PHP SAPI: +; php_value/php_flag - you can set classic ini defines which can +; be overwritten from PHP call 'ini_set'. +; php_admin_value/php_admin_flag - these directives won't be overwritten by +; PHP call 'ini_set' +; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. + +; Defining 'extension' will load the corresponding shared extension from +; extension_dir. Defining 'disable_functions' or 'disable_classes' will not +; overwrite previously defined php.ini values, but will append the new value +; instead. + +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or /usr) + +; Default Value: nothing is defined by default except the values in php.ini and +; specified at startup with the -d argument +;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com +;php_flag[display_errors] = off +;php_admin_value[error_log] = /var/log/fpm-php.www.log +;php_admin_flag[log_errors] = on +;php_admin_value[memory_limit] = 32M + +; Common values to change to increase file upload limit +; php_admin_value[upload_max_filesize] = 50M +; php_admin_value[post_max_size] = 50M +; php_admin_flag[mail.add_x_header] = Off + +; Other common parameters +; php_admin_value[max_execution_time] = 600 +; php_admin_value[max_input_time] = 300 +; php_admin_value[memory_limit] = 256M +; php_admin_flag[short_open_tag] = On diff --git a/manifest.json b/manifest.json index 8500857..d7c2308 100644 --- a/manifest.json +++ b/manifest.json @@ -1,25 +1,25 @@ { - "packaging_format": 1, "id": "baikal", "name": "Baikal", + "packaging_format": 1, "description": { - "en": "Lightweight CalDAV+CardDAV server", - "fr": "Serveur CalDAV+CardDAV léger" + "en": "Lightweight CalDAV and CardDAV server", + "fr": "Serveur CalDAV et CardDAV léger" }, + "version": "0.7.1~ynh1", "url": "http://baikal-server.com/", - "license": "GPL-3", - "version": "0.4.4", + "license": "GPL-3.0", "maintainer": { "name": "julien", "email": "julien.malik@paraiso.me" }, - "multi_instance": false, "requirements": { - "yunohost": ">= 2.3.16" + "yunohost": ">= 3.8.1" }, + "multi_instance": false, "services": [ "nginx", - "php5-fpm", + "php7.0-fpm", "mysql" ], "arguments": { diff --git a/patches/add-ldap-auth.patch b/patches/add-ldap-auth.patch deleted file mode 100644 index b4cb6ed..0000000 --- a/patches/add-ldap-auth.patch +++ /dev/null @@ -1,317 +0,0 @@ ---- /dev/null -+++ b/Core/Frameworks/Baikal/Core/AbstractExternalAuth.php -@@ -0,0 +1,130 @@ -+ -+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License -+ */ -+abstract class AbstractExternalAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic { -+ -+ /** -+ * enable autocreation of user -+ * -+ * @var PDO -+ */ -+ protected $enableAutoCreation; -+ -+ /** -+ * Reference to PDO connection -+ * -+ * @var PDO -+ */ -+ private $pdo; -+ -+ /** -+ * PDO table name we'll be using -+ * -+ * @var string -+ */ -+ private $tableName; -+ -+ /** -+ * Creates the backend object. -+ * -+ * If the filename argument is passed in, it will parse out the specified file fist. -+ * -+ * @param PDO $pdo -+ * @param string $realm -+ * @param string $tableName The PDO table name to use -+ */ -+ public function __construct(\PDO $pdo, $realm = 'BaikalDAV', $tableName = 'users') { -+ -+ $this->pdo = $pdo; -+ $this->tableName = $tableName; -+ $this->enableAutoCreation = true; -+ } -+ -+ /** -+ * Validates a username and password -+ * -+ * This method should return true or false depending on if login -+ * succeeded. -+ * -+ * @param string $username -+ * @param string $password -+ * @return bool -+ */ -+ public function validateUserPass($username, $password) { -+ -+ if (!$this->validateUserPassExternal($username, $password)) -+ return false; -+ -+ $this->currentUser = $username; -+ if ($this->enableAutoCreation) -+ $this->autoUserCreation($username); -+ -+ return true; -+ } -+ -+ /** -+ * Validates a username and password agains external backend -+ * -+ * This method should return true or false depending on if login -+ * succeeded. -+ * -+ * @param string $username -+ * @param string $password -+ * @return bool -+ */ -+ public abstract function validateUserPassExternal($username, $password); -+ -+ /** -+ * return the displayname and email from the external Backend -+ * -+ * @param string $username -+ * @return array ('displayname' => string, 'email' => string) -+ */ -+ public function getAccountValues($username) { -+ -+ return array(); -+ } -+ -+ /** -+ * create an internal user, when user not exists -+ * -+ * @param string $username -+ */ -+ private function autoUserCreation($username) { -+ -+ /* search user in DB and do nothing, when user exists */ -+ $stmt = $this->pdo->prepare('SELECT username FROM '.$this->tableName.' WHERE username = ?'); -+ $stmt->execute(array($username)); -+ $result = $stmt->fetchAll(); -+ if (count($result) != 0) -+ return; -+ -+ /* get account values from backend */ -+ $values = $this->getAccountValues($username); -+ if (!isset($values['displayname']) OR strlen($values['displayname']) === 0) -+ $values['displayname'] = $username; -+ if (!isset($values['email']) OR strlen($values['email']) === 0) { -+ if(filter_var($username, FILTER_VALIDATE_EMAIL)) -+ $values['email'] = $username; -+ else -+ $values['email'] = 'unset-mail'; -+ } -+ -+ /* create user */ -+ $user = new \Baikal\Model\User(); -+ $user->set('username', $username); -+ $user->set('displayname', $values['displayname']); -+ $user->set('email', $values['email']); -+ $user->persist(); -+ } -+ -+} ---- /dev/null -+++ b/Core/Frameworks/Baikal/Core/LDAPUserBindAuth.php -@@ -0,0 +1,75 @@ -+ -+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License -+ */ -+class LDAPUserBindAuth extends AbstractExternalAuth { -+ -+ /** -+ * AccountValues for getAccountValues -+ * -+ * @var array ('displayname' => string, 'email' => string) -+ */ -+ private $accountValues; -+ -+ /** -+ * Validates a username and password over ldap -+ * -+ * @param string $username -+ * @param string $password -+ * @return bool -+ */ -+ public function validateUserPassExternal($username, $password) { -+ -+ /* create ldap connection */ -+ $conn = ldap_connect(BAIKAL_DAV_LDAP_URI); -+ if (!$conn) -+ return false; -+ if (!ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3)) -+ return false; -+ -+ /* bind with user -+ * error_handler have to change, because a failed bind raises an error -+ * this raise a secuity issue because in the stack trace is the password of user readable -+ */ -+ $arr = explode('@', $username, 2); -+ $dn = str_replace('%n', $username, BAIKAL_DAV_LDAP_DN_TEMPLATE); -+ $dn = str_replace('%u', $arr[0], $dn); -+ if(isset($arr[1])) $dn = str_replace('%d', $arr[1], $dn); -+ -+ set_error_handler("\Baikal\Core\LDAPUserBindAuth::exception_error_handler"); -+ $bind = ldap_bind($conn, $dn, $password); -+ restore_error_handler(); -+ if (!$bind) { -+ ldap_close($conn); -+ return false; -+ } -+ -+ /* read displayname and email from user */ -+ $this->accountValues = array(); -+ $sr = ldap_read($conn, $dn, '(objectclass=*)', array(BAIKAL_DAV_LDAP_DISPLAYNAME_ATTR, BAIKAL_DAV_LDAP_EMAIL_ATTR)); -+ $entry = ldap_get_entries($conn, $sr); -+ if (isset($entry[0][BAIKAL_DAV_LDAP_DISPLAYNAME_ATTR][0])) -+ $this->accountValues['displayname'] = $entry[0][BAIKAL_DAV_LDAP_DISPLAYNAME_ATTR][0]; -+ if (isset($entry[0][BAIKAL_DAV_LDAP_EMAIL_ATTR][0])) -+ $this->accountValues['email'] = $entry[0][BAIKAL_DAV_LDAP_EMAIL_ATTR][0]; -+ -+ /* close */ -+ ldap_close($conn); -+ return true; -+ } -+ -+ public function getAccountValues($username) { -+ -+ return $this->accountValues; -+ } -+ -+ # WorkAround error_handler in failed bind of LDAP -+ public static function exception_error_handler($errno, $errstr, $errfile, $errline) { -+ } -+} -diff --git a/Core/Frameworks/Baikal/Core/Server.php b/Core/Frameworks/Baikal/Core/Server.php -index 8026854..8d306fe 100644 ---- a/Core/Frameworks/Baikal/Core/Server.php -+++ b/Core/Frameworks/Baikal/Core/Server.php -@@ -133,6 +133,8 @@ class Server { - - if ($this->authType === 'Basic') { - $authBackend = new \Baikal\Core\PDOBasicAuth($this->pdo, $this->authRealm); -+ } elseif ($this->authType === 'LDAP-UserBind') { -+ $authBackend = new \Baikal\Core\LDAPUserBindAuth($this->pdo, $this->authRealm); - } else { - $authBackend = new \Sabre\DAV\Auth\Backend\PDO($this->pdo); - $authBackend->setRealm($this->authRealm); -diff --git a/Core/Frameworks/Baikal/Model/Config/Standard.php b/Core/Frameworks/Baikal/Model/Config/Standard.php -index 6107377..39f90bd 100644 ---- a/Core/Frameworks/Baikal/Model/Config/Standard.php -+++ b/Core/Frameworks/Baikal/Model/Config/Standard.php -@@ -46,6 +46,22 @@ class Standard extends \Baikal\Model\Config { - "type" => "string", - "comment" => "HTTP authentication type for WebDAV; default Digest" - ], -+ "BAIKAL_DAV_LDAP_URI" => [ -+ "type" => "string", -+ "comment" => "URI to LDAP Server (for ldap-userbind auth); default ldapi:///" -+ ], -+ "BAIKAL_DAV_LDAP_DN_TEMPLATE" => [ -+ "type" => "string", -+ "comment" => "User DN for bind; with replacments %n => username, %u => user part, %d => domain part of username" -+ ], -+ "BAIKAL_DAV_LDAP_DISPLAYNAME_ATTR" => [ -+ "type" => "string", -+ "comment" => "LDAP-attribute for displayname; default cn" -+ ], -+ "BAIKAL_DAV_LDAP_EMAIL_ATTR" => [ -+ "type" => "string", -+ "comment" => "LDAP-attribute for email; default mail" -+ ], - "BAIKAL_ADMIN_PASSWORDHASH" => [ - "type" => "string", - "comment" => "Baïkal Web admin password hash; Set via Baïkal Web Admin", -@@ -58,6 +74,10 @@ class Standard extends \Baikal\Model\Config { - "BAIKAL_CARD_ENABLED" => true, - "BAIKAL_CAL_ENABLED" => true, - "BAIKAL_DAV_AUTH_TYPE" => "Digest", -+ "BAIKAL_DAV_LDAP_URI" => "ldapi:///", -+ "BAIKAL_DAV_LDAP_DN_TEMPLATE" => "uid=%n,dc=example,dc=com", -+ "BAIKAL_DAV_LDAP_DISPLAYNAME_ATTR" => "cn", -+ "BAIKAL_DAV_LDAP_EMAIL_ATTR" => "mail", - "BAIKAL_ADMIN_PASSWORDHASH" => "" - ]; - -@@ -85,7 +105,31 @@ class Standard extends \Baikal\Model\Config { - $oMorpho->add(new \Formal\Element\Listbox([ - "prop" => "BAIKAL_DAV_AUTH_TYPE", - "label" => "WebDAV authentication type", -- "options" => [ "Digest", "Basic" ] -+ "options" => [ "Digest", "Basic", "LDAP-UserBind" ] -+ ])); -+ -+ $oMorpho->add(new \Formal\Element\Text([ -+ "prop" => "BAIKAL_DAV_LDAP_URI", -+ "label" => "LDAP URI" -+ ])); -+ -+ $oMorpho->add(new \Formal\Element\Text([ -+ "prop" => "BAIKAL_DAV_LDAP_DN_TEMPLATE", -+ "label" => "LDAP DN template", -+ "popover" => [ -+ "title" => "posible placeholder", -+ "content" => "%n - username
%u - user part of username , when it is an email address)
%d - domain part", -+ ] -+ ])); -+ -+ $oMorpho->add(new \Formal\Element\Text([ -+ "prop" => "BAIKAL_DAV_LDAP_DISPLAYNAME_ATTR", -+ "label" => "LDAP attribute for DisplayName" -+ ])); -+ -+ $oMorpho->add(new \Formal\Element\Text([ -+ "prop" => "BAIKAL_DAV_LDAP_EMAIL_ATTR", -+ "label" => "LDAP attribute for eMail" - ])); - - $oMorpho->add(new \Formal\Element\Password([ -@@ -180,6 +224,21 @@ define("BAIKAL_CAL_ENABLED", TRUE); - # WebDAV authentication type; default Digest - define("BAIKAL_DAV_AUTH_TYPE", "Digest"); - -+# Auth Backend LDAP-UserBind; LDAP URI -+define("BAIKAL_DAV_LDAP_URI", 'ldapi:///'); -+ -+# Auth Backend LDAP-UserBind; Template for userbind -+# %n => username -+# %u => user part of username when it is an email -+# %u => domain part of username when it is an email -+define("BAIKAL_DAV_LDAP_DN_TEMPLATE", 'cn=%u,dc=%d,ou=domains,o=server'); -+ -+# Auth Backend LDAP-UserBind; attribute for displayname -+define("BAIKAL_DAV_LDAP_DISPLAYNAME_ATTR", 'cn'); -+ -+# Auth Backend LDAP-UserBind; attribute for email -+define("BAIKAL_DAV_LDAP_EMAIL_ATTR", 'mail'); -+ - # Baïkal Web admin password hash; Set via Baïkal Web Admin - define("BAIKAL_ADMIN_PASSWORDHASH", ""); - CODE; diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 0000000..eb2a30e --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,24 @@ +## Problem +- *Description of why you made this PR* + +## Solution +- *And how you fix that* + +## PR Status +- [ ] Code finished. +- [ ] Tested with Package_check. +- [ ] Fix or enhancement tested. +- [ ] Upgrade from last version tested. +- [ ] Can be reviewed and tested. + +## Validation +--- +*Minor decision* +- **Upgrade previous version** : +- [ ] **Code review** : +- [ ] **Approval (LGTM)** : +- [ ] **Approval (LGTM)** : +- **CI succeeded** : +[![Build Status](https://ci-apps-hq.yunohost.org/jenkins/job/baikal_ynh%20PR-NUM-/badge/icon)](https://ci-apps-hq.yunohost.org/jenkins/job/baikal_ynh%20PR-NUM-/) +*Please replace '-NUM-' in this link by the PR number.* +When the PR is marked as ready to merge, you have to wait for 3 days before really merging it. diff --git a/scripts/_common.sh b/scripts/_common.sh index b743839..6eef4b8 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -1,43 +1,46 @@ -# -# Common variables -# +#!/bin/bash -# Baikal version -VERSION=0.4.4 +#================================================= +# COMMON VARIABLES +#================================================= -# Baikal source archive checksum -BAIKAL_SOURCE_SHA256="cced612353862bce532ed458eda0675b5e1e5790f92969bf13992c6567943efc" +# dependencies used by the app +YNH_PHP_VERSION="7.3" -# Remote URL to fetch Baikal source archive -BAIKAL_SOURCE_URL="https://github.com/fruux/Baikal/releases/download/${VERSION}/baikal-${VERSION}.zip" +pkg_dependencies="php${YNH_PHP_VERSION}-xml php${YNH_PHP_VERSION}-mbstring php${YNH_PHP_VERSION}-mysql" -# App package root directory should be the parent folder -PKGDIR=$(cd ../; pwd) +#================================================= +# EXPERIMENTAL HELPERS +#================================================= -# -# Common helpers -# +# Check if an URL is already handled +# usage: is_url_handled --domain=DOMAIN --path=PATH_URI +is_url_handled() { + # Declare an array to define the options of this helper. + local legacy_args=dp + declare -Ar args_array=( [d]=domain= [p]=path= ) + local domain + local path + # Manage arguments with getopts + ynh_handle_getopts_args "$@" -# Source app helpers -source /usr/share/yunohost/helpers + # Try to get the url with curl, and keep the http code and an eventual redirection url. + local curl_output="$(curl --insecure --silent --output /dev/null \ + --write-out '%{http_code};%{redirect_url}' https://127.0.0.1$path --header "Host: $domain" --resolve $domain:443:127.0.0.1)" -# Download and extract Baikal sources to the given directory -# usage: extract_baikal DESTDIR -extract_baikal() { - local DESTDIR=$1 - local bk_archive="${DESTDIR}/baikal.zip" + # Cut the output and keep only the first part to keep the http code + local http_code="${curl_output%%;*}" + # Do the same thing but keep the second part, the redirection url + local redirection="${curl_output#*;}" - wget -q -O "$bk_archive" "$BAIKAL_SOURCE_URL" \ - || ynh_die "Unable to download Baikal archive" - echo "$BAIKAL_SOURCE_SHA256 $bk_archive" | sha256sum -c >/dev/null \ - || ynh_die "Invalid checksum of downloaded archive" - unzip -q "$bk_archive" -d "$DESTDIR" \ - || ynh_die "Unable to extract Baikal archive" - mv "${DESTDIR}/baikal/"* "$DESTDIR" - rm -rf "$bk_archive" "${DESTDIR}/baikal" - - # apply patches - (cd "$DESTDIR" \ - && for p in ${PKGDIR}/patches/*.patch; do patch -p1 < $p; done) \ - || die "Unable to apply patches to Baikal" + # Return 1 if the url isn't handled. + # Which means either curl got a 404 (or the admin) or the sso. + # A handled url should redirect to a publicly accessible url. + # Return 1 if the url has returned 404 + if [ "$http_code" = "404" ] || [[ $redirection =~ "/yunohost/admin" ]]; then + return 1 + # Return 1 if the url is redirected to the SSO + elif [[ $redirection =~ "/yunohost/sso" ]]; then + return 1 + fi } diff --git a/scripts/actions/reset_admin_password b/scripts/actions/reset_admin_password new file mode 100644 index 0000000..c2b0be8 --- /dev/null +++ b/scripts/actions/reset_admin_password @@ -0,0 +1,57 @@ +#!/bin/bash + +#================================================= +# GENERIC STARTING +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source scripts/_common.sh +source /usr/share/yunohost/helpers + +#================================================= +# RETRIEVE ARGUMENTS +#================================================= + +# Get password +password=${YNH_ACTION_PASSWORD} + +app=${YNH_APP_INSTANCE_NAME:-$YNH_APP_ID} +final_path=$(ynh_app_setting_get --app=$app --key=final_path) + +#================================================= +# CHECK IF ARGUMENTS ARE CORRECT +#================================================= + +#================================================= +# CHECK IF AN ACTION HAS TO BE DONE +#================================================= + +password_hash_old=$(ynh_app_setting_get --app=$app --key=password_hash) +password_hash=$(echo -n admin:BaikalDAV:$password | sha256sum | cut -d ' ' -f 1) +if [ $password_hash == $password_hash_old ] +then + ynh_die "This is the same password." 0 +fi + +#================================================= +# SPECIFIC ACTION +#================================================= +# CHANGE THE PASSWORD +#================================================= +ynh_script_progression --message="Changing the password..." --weight=1 + +bk_conf="${final_path}/config/baikal.yaml" + +ynh_backup_if_checksum_is_different --file="${final_path}/config/baikal.yaml" + +ynh_replace_string --match_string="${password_hash_old}" --replace_string="${password_hash}" --target_file="$bk_conf" +ynh_app_setting_set --app=$app --key=password_hash --value=$password_hash + +ynh_store_file_checksum --file="${final_path}/config/baikal.yaml" + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Execution completed" --last \ No newline at end of file diff --git a/scripts/backup b/scripts/backup index d0fb5e7..3a806bb 100644 --- a/scripts/backup +++ b/scripts/backup @@ -1,27 +1,65 @@ #!/bin/bash -# Retrieve arguments -backup_dir=$1 -app=$2 +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= -# Set app specific variables -dbname=$app -dbuser=$app +# source ../settings/scripts/_common.sh +source /usr/share/yunohost/helpers -# Source app helpers -. /usr/share/yunohost/helpers +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= -# Retrieve app settings -domain=$(ynh_app_setting_get "$app" domain) -path=$(ynh_app_setting_get "$app" path) -dbpass=$(ynh_app_setting_get "$app" mysqlpwd) +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors -# Copy the app files -DESTDIR="/var/www/$app" -ynh_backup "$DESTDIR" "sources" +#================================================= +# LOAD SETTINGS +#================================================= +ynh_print_info --message="Loading installation settings..." -# Copy the conf files -ynh_backup "/etc/nginx/conf.d/${domain}.d/${app}.conf" "nginx.conf" +app=$YNH_APP_INSTANCE_NAME -# Dump the database -mysqldump -u "$dbuser" -p"$dbpass" --no-create-db "$dbname" > ./dump.sql +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +domain=$(ynh_app_setting_get --app=$app --key=domain) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +phpversion=$(ynh_app_setting_get --app=$app --key=phpversion) + +#================================================= +# DECLARE DATA AND CONF FILES TO BACKUP +#================================================= +ynh_print_info --message="Declaring files to be backed up..." + +#================================================= +# BACKUP THE APP MAIN DIR +#================================================= + +ynh_backup --src_path="$final_path" + +#================================================= +# BACKUP THE NGINX CONFIGURATION +#================================================= + +ynh_backup --src_path="/etc/nginx/conf.d/$domain.d/$app.conf" + +#================================================= +# BACKUP THE PHP-FPM CONFIGURATION +#================================================= + +ynh_backup --src_path="/etc/php/$phpversion/fpm/pool.d/$app.conf" + +#================================================= +# BACKUP THE MYSQL DATABASE +#================================================= +ynh_print_info --message="Backing up the MySQL database..." + +ynh_mysql_dump_db --database="$db_name" > db.sql + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_print_info --message="Backup script completed for Baïkal. (YunoHost will then actually copy those files to the archive)." diff --git a/scripts/change_url b/scripts/change_url new file mode 100644 index 0000000..4d491c8 --- /dev/null +++ b/scripts/change_url @@ -0,0 +1,118 @@ +#!/bin/bash + +#================================================= +# GENERIC STARTING +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# RETRIEVE ARGUMENTS +#================================================= + +old_domain=$YNH_APP_OLD_DOMAIN +old_path=$YNH_APP_OLD_PATH + +new_domain=$YNH_APP_NEW_DOMAIN +new_path=$YNH_APP_NEW_PATH + +app=$YNH_APP_INSTANCE_NAME + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --weight=2 + +# Needed for helper "ynh_add_nginx_config" +final_path=$(ynh_app_setting_get --app=$app --key=final_path) + +#================================================= +# CHECK WHICH PARTS SHOULD BE CHANGED +#================================================= + +change_domain=0 +if [ "$old_domain" != "$new_domain" ] +then + change_domain=1 +fi + +change_path=0 +if [ "$old_path" != "$new_path" ] +then + change_path=1 +fi + +#================================================= +# CHECK IF THE APP CAN BE MOVED WITH THESE ARGS +#================================================= + +if [ $change_domain -eq 1 ] +then + # Check if .well-known is available for the new domain. + if is_url_handled --domain="$new_domain" --path="/.well-known/caldav" || is_url_handled --domain="$new_domain" --path="/.well-known/carddav" + then + ynh_die --message="Another app already uses the domain $new_domain to serve a calDAV/cardDAV feature. Please use another domain." + fi +fi + +#================================================= +# STANDARD MODIFICATIONS +#================================================= +# MODIFY URL IN NGINX CONF +#================================================= +ynh_script_progression --message="Updating NGINX web server configuration..." --weight=2 + +nginx_conf_path=/etc/nginx/conf.d/$old_domain.d/$app.conf + +# Change the path in the nginx config file +if [ $change_path -eq 1 ] +then + # Make a backup of the original nginx config file if modified + ynh_backup_if_checksum_is_different --file="$nginx_conf_path" + # Set global variables for nginx helper + domain="$old_domain" + path_url="$new_path" + # Create a dedicated nginx config + ynh_add_nginx_config +fi + +# Change the domain for nginx +if [ $change_domain -eq 1 ] +then + # Delete file checksum for the old conf file location + ynh_delete_file_checksum --file="$nginx_conf_path" + mv $nginx_conf_path /etc/nginx/conf.d/$new_domain.d/$app.conf + # Store file checksum for the new config file location + ynh_store_file_checksum --file="/etc/nginx/conf.d/$new_domain.d/$app.conf" +fi + +#================================================= +# SPECIFIC MODIFICATIONS +#================================================= +# UPDATE CONFIGURATION +#================================================= +ynh_script_progression --message="Updating Baïkal configuration..." + +ynh_backup_if_checksum_is_different --file="${final_path}/config/baikal.yaml" + +ynh_replace_string --match_string="base_uri: '$old_path'" --replace_string="base_uri: '$new_path'" --target_file="${final_path}/config/baikal.yaml" + +ynh_store_file_checksum --file="${final_path}/config/baikal.yaml" + +#================================================= +# GENERIC FINALISATION +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Change of URL completed for Baïkal" --last diff --git a/scripts/install b/scripts/install index 30635e2..91021c1 100644 --- a/scripts/install +++ b/scripts/install @@ -1,82 +1,174 @@ #!/bin/bash -set -e +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= -# Retrieve arguments -domain=$1 -path=${2%/} -password=$3 -app=${!#} - -# Load common variables and helpers source ./_common.sh +source /usr/share/yunohost/helpers -# Set app specific variables -dbname=$app -dbuser=$app +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= -# Check domain/path availability -sudo yunohost app checkurl "${domain}${path}" -a "$app" \ - || exit 1 +ynh_abort_if_errors -# Check destination directory -DESTDIR="/var/www/${app}" -[[ -d "$DESTDIR" ]] && ynh_die \ -"The destination directory '${DESTDIR}' already exists.\ - You should safely delete it before installing this app." +#================================================= +# RETRIEVE ARGUMENTS FROM THE MANIFEST +#================================================= -# Create tmp directory and fetch app inside -TMPDIR=$(ynh_mkdir_tmp) -extract_baikal "$TMPDIR" +domain=$YNH_APP_ARG_DOMAIN +path_url=$YNH_APP_ARG_PATH +password=$YNH_APP_ARG_PASSWORD -# Generate random DES key & password -deskey=$(ynh_string_random 24) -dbpass=$(ynh_string_random) +app=$YNH_APP_INSTANCE_NAME -# Initialize database -ynh_mysql_create_db "$dbname" "$dbuser" "$dbpass" -ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" \ - < "${TMPDIR}/Core/Resources/Db/MySQL/db.sql" +#================================================= +# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS +#================================================= +ynh_script_progression --message="Validating installation parameters..." +final_path=/var/www/$app +test ! -e "$final_path" || ynh_die "This path already contains a folder" + +# Register (book) web path +ynh_webpath_register --app=$app --domain=$domain --path_url=$path_url + +# Check if .well-known is available for this domain. +if is_url_handled --domain="$domain" --path="/.well-known/caldav" || is_url_handled --domain="$domain" --path="/.well-known/carddav" +then + ynh_die --message="Another app already uses the domain $domain to serve a calDAV/cardDAV feature. Please use another domain." +fi + +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= +ynh_script_progression --message="Storing installation settings..." --weight=2 + +ynh_app_setting_set --app=$app --key=domain --value=$domain +ynh_app_setting_set --app=$app --key=path --value=$path_url + +#================================================= +# STANDARD MODIFICATIONS +#================================================= +# INSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Installing dependencies..." --weight=5 + +ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# CREATE A MYSQL DATABASE +#================================================= +ynh_script_progression --message="Creating a MySQL database..." + +db_name=$(ynh_sanitize_dbid $app) +ynh_app_setting_set --app=$app --key=db_name --value=$db_name +ynh_mysql_setup_db --db_user=$db_name --db_name=$db_name + +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= +ynh_script_progression --message="Setting up source files..." --weight=8 + +ynh_app_setting_set --app=$app --key=final_path --value=$final_path +# Download, check integrity, uncompress and patch the source from app.src +ynh_setup_source --dest_dir="$final_path" + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring NGINX web server..." + +# Create a dedicated NGINX config +ynh_add_nginx_config + +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Configuring system user..." --weight=3 + +# Create a system user +ynh_system_user_create --username=$app + +#================================================= +# PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring PHP-FPM..." + +# Create a dedicated PHP-FPM config +ynh_add_fpm_config + +#================================================= +# SPECIFIC SETUP +#================================================= +# INITIALIZE DATABASE +#================================================= +ynh_script_progression --message="Configuring Baïkal..." --weight=3 + +ynh_mysql_connect_as --user=$db_name --password="$db_pwd" --database=$db_name \ + < "${final_path}/Core/Resources/Db/MySQL/db.sql" + +#================================================= +# CONFIGURE BAIKAL +#================================================= + +bk_conf="${final_path}/config/baikal.yaml" +cp ../conf/baikal.yaml "$bk_conf" + +ynh_replace_string --match_string="__TIMEZONE__" --replace_string="$(cat /etc/timezone)" --target_file="$bk_conf" # Create admin password hash -password_hash=$(echo -n admin:BaikalDAV:$password | md5sum | cut -d ' ' -f 1) +password_hash=$(echo -n admin:BaikalDAV:$password | sha256sum | cut -d ' ' -f 1) +ynh_replace_string --match_string="__PASSWORDHASH__" --replace_string="${password_hash}" --target_file="$bk_conf" +ynh_app_setting_set --app=$app --key=password_hash --value=$password_hash -# Copy and set Baikal configuration -bk_conf="${TMPDIR}/Specific/config.php" -cp ../conf/config.php "$bk_conf" -sed -i "s@#TIMEZONE#@$(cat /etc/timezone)@g" "$bk_conf" -sed -i "s@#PASSWORDHASH#@${password_hash}@g" "$bk_conf" +ynh_replace_string --match_string="__PATH__" --replace_string="${path_url%/}" --target_file="$bk_conf" +ynh_replace_string --match_string="__DBNAME__" --replace_string="$db_name" --target_file="$bk_conf" +ynh_replace_string --match_string="__DBUSER__" --replace_string="$db_name" --target_file="$bk_conf" +ynh_replace_string --match_string="__DBPASS__" --replace_string="$db_pwd" --target_file="$bk_conf" -bk_conf="${TMPDIR}/Specific/config.system.php" -cp ../conf/config.system.php "$bk_conf" -sed -i "s@#PATH#@${path}@g" "$bk_conf" -sed -i "s@#DBNAME#@${dbname}@g" "$bk_conf" -sed -i "s@#DBUSER#@${dbuser}@g" "$bk_conf" -sed -i "s@#DBPASS#@${dbpass}@g" "$bk_conf" -sed -i "s@#DESKEY#@${deskey}@g" "$bk_conf" +deskey=$(ynh_string_random 24) +ynh_app_setting_set --app=$app --key=encrypt_key --value="$deskey" +ynh_replace_string --match_string="__DESKEY__" --replace_string="$deskey" --target_file="$bk_conf" + +# Store the config file checksum into the app settings +ynh_store_file_checksum --file="$bk_conf" # Disable installation -touch "${TMPDIR}/Specific/INSTALL_DISABLED" +touch "${final_path}/Specific/INSTALL_DISABLED" -# Install files and set permissions -sudo mv "$TMPDIR" "$DESTDIR" -sudo chown -R www-data: "$DESTDIR" +#================================================= +# GENERIC FINALIZATION +#================================================= +# SECURE FILES AND DIRECTORIES +#================================================= -# Copy and set nginx configuration -nginx_conf="/etc/nginx/conf.d/${domain}.d/${app}.conf" -sed -i "s@#PATH#@${path}@g" ../conf/nginx.conf -sed -i "s@#LOCATION#@${path:-/}@g" ../conf/nginx.conf -sed -i "s@#DESTDIR#@${DESTDIR}@g" ../conf/nginx.conf -sudo cp ../conf/nginx.conf "$nginx_conf" +# Set permissions +chown -R root: "$final_path" +chown $app "$final_path/config/baikal.yaml" +chmod 640 "$final_path/config/baikal.yaml" -# Save app settings -ynh_app_setting_set "$app" password "$password" -ynh_app_setting_set "$app" encrypt_key "$deskey" -ynh_app_setting_set "$app" mysqlpwd "$dbpass" +#================================================= +# SETUP SSOWAT +#================================================= +ynh_script_progression --message="Configuring SSOwat..." --weight=2 -# Set SSOwat rules -ynh_app_setting_set "$app" skipped_uris "/" -ynh_app_setting_set "$app" protected_uris "/admin/" +# Allow public access on / +ynh_permission_update --permission "main" --add "visitors" +# But restrain on /admin +ynh_permission_create --permission "admin" --url "/admin" --allowed "all_users" -# Reload services -sudo service nginx reload || true +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Installation of Baïkal completed" --last diff --git a/scripts/remove b/scripts/remove index 19f22e6..1c3060c 100644 --- a/scripts/remove +++ b/scripts/remove @@ -1,25 +1,79 @@ #!/bin/bash -# Retrieve arguments -app=${!#} +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= -# Load common variables and helpers -source ./_common.sh +source _common.sh +source /usr/share/yunohost/helpers -# Set app specific variables -dbname=$app -dbuser=$app +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." -# Drop MySQL database and user -ynh_mysql_drop_db $dbname || true -ynh_mysql_drop_user $dbuser || true +app=$YNH_APP_INSTANCE_NAME -# Retrieve domain from app settings -domain=$(ynh_app_setting_get $app domain) +domain=$(ynh_app_setting_get --app=$app --key=domain) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +final_path=$(ynh_app_setting_get --app=$app --key=final_path) -# Delete app directory and configurations -sudo rm -rf "/var/www/${app}" -[[ -n $domain ]] && sudo rm -f "/etc/nginx/conf.d/${domain}.d/${app}.conf" +#================================================= +# STANDARD REMOVE +#================================================= +# REMOVE THE MYSQL DATABASE +#================================================= +ynh_script_progression --message="Removing the MySQL database" --weight=2 -# Reload services -sudo service nginx reload || true +# Remove a database if it exists, along with the associated user +ynh_mysql_remove_db --db_user=$db_name --db_name=$db_name + +#================================================= +# REMOVE DEPENDENCIES +#================================================= +ynh_script_progression --message="Removing dependencies..." --weight=4 + +# Remove metapackage and its dependencies +ynh_remove_app_dependencies + +#================================================= +# REMOVE APP MAIN DIR +#================================================= +ynh_script_progression --message="Removing Baïkal main directory" + +# Remove the app directory securely +ynh_secure_remove --file="$final_path" + +#================================================= +# REMOVE NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Removing NGINX web server configuration" + +# Remove the dedicated NGINX config +ynh_remove_nginx_config + +#================================================= +# REMOVE PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Removing PHP-FPM configuration" + +# Remove the dedicated PHP-FPM config +ynh_remove_fpm_config + +#================================================= +# GENERIC FINALIZATION +#================================================= +# REMOVE DEDICATED USER +#================================================= +ynh_script_progression --message="Removing the dedicated system user" + +# Delete a system user +ynh_system_user_delete --username=$app + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Removal of Baïkal completed" --last diff --git a/scripts/restore b/scripts/restore index 4e6184f..820e1de 100644 --- a/scripts/restore +++ b/scripts/restore @@ -1,47 +1,116 @@ #!/bin/bash -# Retrieve arguments -backup_dir=$1 -app=$2 +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= -# Set app specific variables -dbname=$app -dbuser=$app +source ../settings/scripts/_common.sh +source /usr/share/yunohost/helpers -# Source app helpers -. /usr/share/yunohost/helpers +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= -# Retrieve old app settings -domain=$(ynh_app_setting_get "$app" domain) -path=$(ynh_app_setting_get "$app" path) -dbpass=$(ynh_app_setting_get "$app" mysqlpwd) +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors -# Check domain/path availability -sudo yunohost app checkurl "${domain}${path}" -a "$app" \ - || exit 1 +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading settings..." -# Check destination directory -DESTDIR="/var/www/$app" -[[ -d $DESTDIR ]] && ynh_die \ -"The destination directory '$DESTDIR' already exists.\ - You should safely delete it before restoring this app." +app=$YNH_APP_INSTANCE_NAME -# Check configuration files -nginx_conf="/etc/nginx/conf.d/${domain}.d/${app}.conf" -[[ -f $nginx_conf ]] && ynh_die \ -"The NGINX configuration already exists at '${nginx_conf}'. - You should safely delete it before restoring this app." +domain=$(ynh_app_setting_get --app=$app --key=domain) +path_url=$(ynh_app_setting_get --app=$app --key=path) +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +phpversion=$(ynh_app_setting_get --app=$app --key=phpversion) -# Restore the app files and set permissions -sudo cp -a ./sources "$DESTDIR" -sudo chown -R www-data: "$DESTDIR" +#================================================= +# CHECK IF THE APP CAN BE RESTORED +#================================================= +ynh_script_progression --message="Validating restoration parameters..." --weight=2 -# Create and restore the database -ynh_mysql_create_db "$dbname" "$dbuser" "$dbpass" -ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" < ./dump.sql +ynh_webpath_available --domain=$domain --path_url=$path_url \ + || ynh_die --message="Path not available: ${domain}${path_url}" +test ! -d $final_path \ + || ynh_die --message="There is already a directory: $final_path " -# Restore configuration files -sudo cp -a ./nginx.conf "$nginx_conf" +# Check if .well-known is available for this domain. +if is_url_handled --domain="$domain" --path="/.well-known/caldav" || is_url_handled --domain="$domain" --path="/.well-known/carddav" +then + ynh_die --message="Another app already uses the domain $domain to serve a calDAV/cardDAV feature. Please use another domain." +fi -# Reload services -sudo service nginx reload || true +#================================================= +# STANDARD RESTORATION STEPS +#================================================= +# REINSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Reinstalling dependencies..." --weight=5 + +ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# RESTORE THE NGINX CONFIGURATION +#================================================= + +ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf" + +#================================================= +# RESTORE THE APP MAIN DIR +#================================================= +ynh_script_progression --message="Restoring Baïkal main directory..." + +ynh_restore_file --origin_path="$final_path" + +#================================================= +# RESTORE THE MYSQL DATABASE +#================================================= +ynh_script_progression --message="Restoring the MySQL database..." --weight=2 + +db_pwd=$(ynh_app_setting_get --app=$app --key=mysqlpwd) +ynh_mysql_setup_db --db_user=$db_name --db_name=$db_name --db_pwd=$db_pwd +ynh_mysql_connect_as --user=$db_name --password=$db_pwd --database=$db_name < ./db.sql + +#================================================= +# RECREATE THE DEDICATED USER +#================================================= +ynh_script_progression --message="Recreating the dedicated system user..." --weight=3 + +# Create the dedicated user (if not existing) +ynh_system_user_create --username=$app + +#================================================= +# RESTORE USER RIGHTS +#================================================= + +# Set permissions +chown -R root: "$final_path" +chown $app "$final_path/config/baikal.yaml" +chmod 640 "$final_path/config/baikal.yaml" + +#================================================= +# RESTORE THE PHP-FPM CONFIGURATION +#================================================= + +ynh_restore_file --origin_path="/etc/php/$phpversion/fpm/pool.d/$app.conf" + +#================================================= +# GENERIC FINALIZATION +#================================================= +# RELOAD NGINX AND PHP-FPM +#================================================= +ynh_script_progression --message="Reloading NGINX web server and PHP-FPM..." + +ynh_systemd_action --service_name=php$phpversion-fpm --action=reload +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Restoration completed for Baïkal" --last diff --git a/scripts/upgrade b/scripts/upgrade index e7b3dff..d463884 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -1,58 +1,213 @@ #!/bin/bash -set -u +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= -# Load common variables and helpers -. ./_common.sh +source _common.sh +source /usr/share/yunohost/helpers -# Set app specific variables -app=${!#} -dbname=$app -dbuser=$app +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --weight=2 -# Retrieve arguments -domain=$(ynh_app_setting_get "$app" domain) -path=$(ynh_app_setting_get "$app" path) -path=${path%/} +app=$YNH_APP_INSTANCE_NAME -# Check destination directory -DESTDIR="/var/www/$app" -[[ ! -d $DESTDIR ]] && ynh_die \ -"The destination directory '$DESTDIR' does not exist.\ - The app is not correctly installed, you should remove it first." +domain=$(ynh_app_setting_get --app=$app --key=domain) +path_url=$(ynh_app_setting_get --app=$app --key=path) +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) -# Create tmp directory and fetch new app inside -TMPDIR=$(ynh_mkdir_tmp) -extract_baikal "$TMPDIR" +#================================================= +# CHECK VERSION +#================================================= -# Get Specific folder from current installation -# FIXME: config.php and config.system.php are not updated with possible -# new or changed configurations -rm -rf "${TMPDIR}/Specific" -sudo cp -r "${DESTDIR}/Specific" "$TMPDIR" -sudo chown -hR "${USER}" "${TMPDIR}/Specific" +upgrade_type=$(ynh_check_app_version_changed) -# Run Baikal upgrade from tmp directory -cp -r ../sources/bin "$TMPDIR" -php "${TMPDIR}/bin/upgrade.sh" \ - || echo "The Baïkal upgrade failed, you should try to go to " \ - "https://${domain}${path}/admin/install" +#================================================= +# ENSURE DOWNWARD COMPATIBILITY +#================================================= +ynh_script_progression --message="Ensuring downward compatibility..." -# Install new app and set permissions -sudo rm -rf "$DESTDIR" -sudo mv "$TMPDIR" "$DESTDIR" -sudo chown -R www-data: "$DESTDIR" +# If final_path doesn't exist, create it +if [ -z "$final_path" ]; then + final_path=/var/www/$app + ynh_app_setting_set --app=$app --key=final_path --value=$final_path +fi -# Copy and set nginx configuration -nginx_conf="/etc/nginx/conf.d/${domain}.d/${app}.conf" -sed -i "s@#PATH#@${path}@g" ../conf/nginx.conf -sed -i "s@#LOCATION#@${path:-/}@g" ../conf/nginx.conf -sed -i "s@#DESTDIR#@${DESTDIR}@g" ../conf/nginx.conf -sudo cp ../conf/nginx.conf "$nginx_conf" +# If db_name doesn't exist, create it +if [ -z "$db_name" ]; then + db_name=$(ynh_sanitize_dbid --db_name=$app) + ynh_app_setting_set --app=$app --key=db_name --value=$db_name +fi -# Set SSOwat rules -ynh_app_setting_set "$app" skipped_uris "/" -ynh_app_setting_set "$app" protected_uris "/admin/" +#================================================= +# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP +#================================================= +ynh_script_progression --message="Backing up Baïkal before upgrading (may take a while)..." --weight=4 -# Reload services -sudo service nginx reload || true +# Backup the current version of the app +ynh_backup_before_upgrade +ynh_clean_setup () { + # restore it if the upgrade fails + ynh_restore_upgradebackup +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# STANDARD UPGRADE STEPS +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= + +if [ "$upgrade_type" == "UPGRADE_APP" ] +then + ynh_script_progression --message="Upgrading source files..." --weight=3 + + # Keep the Specific and config folders intact: https://sabre.io/baikal/upgrade/ + mkdir -p "$final_path/config" + temp_folder=$(mktemp -d) + mv "$final_path/Specific" "$temp_folder" + mv "$final_path/config" "$temp_folder" + # Download, check integrity, uncompress and patch the source from app.src + ynh_setup_source --dest_dir="$final_path" + + ynh_secure_remove --file="$final_path/Specific" + ynh_secure_remove --file="$final_path/config" + + mv "$temp_folder/Specific" "$final_path" + mv "$temp_folder/config" "$final_path" + ynh_secure_remove --file="$temp_folder" +fi + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Upgrading NGINX web server configuration..." + +# Create a dedicated NGINX config +ynh_add_nginx_config +phpversion=$(ynh_app_setting_get --app=$app --key=phpversion) + +#================================================= +# UPGRADE DEPENDENCIES +#================================================= +ynh_script_progression --message="Upgrading dependencies..." --weight=5 + +ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Making sure dedicated system user exists..." + +# Create a dedicated user (if not existing) +ynh_system_user_create --username=$app + +#================================================= +# PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Upgrading PHP-FPM configuration..." --weight=2 + +# Create a dedicated PHP-FPM config +ynh_add_fpm_config + +#================================================= +# SPECIFIC UPGRADE +#================================================= +# UPGRADE BAIKAL +#================================================= + +if [ "$upgrade_type" == "UPGRADE_APP" ] +then + #================================================= + # UPGRADE BAIKAL CONFIGURATION + #================================================= + ynh_script_progression --message="Upgrading Baïkal configuration..." --weight=2 + + bk_conf="${final_path}/config/baikal.yaml" + ynh_backup_if_checksum_is_different --file="$bk_conf" + cp ../conf/baikal.yaml "$bk_conf" + + ynh_replace_string --match_string="__TIMEZONE__" --replace_string="$(cat /etc/timezone)" --target_file="$bk_conf" + password_hash=$(ynh_app_setting_get --app=$app --key=password_hash) + # If the password_hash is not in the app's config, recreate it from the password. + if [ -z "$password_hash" ]; then + password=$(ynh_app_setting_get --app=$app --key=password) + password_hash=$(echo -n admin:BaikalDAV:$password | md5sum | cut -d ' ' -f 1) + ynh_app_setting_set --app=$app --key=password_hash --value=$password_hash + fi + ynh_replace_string --match_string="__PASSWORDHASH__" --replace_string="${password_hash}" --target_file="$bk_conf" + + ynh_replace_string --match_string="__PATH__" --replace_string="${path_url%/}" --target_file="$bk_conf" + ynh_replace_string --match_string="__DBNAME__" --replace_string="$db_name" --target_file="$bk_conf" + ynh_replace_string --match_string="__DBUSER__" --replace_string="$db_name" --target_file="$bk_conf" + db_pwd=$(ynh_app_setting_get --app=$app --key=mysqlpwd) + ynh_replace_string --match_string="__DBPASS__" --replace_string="$db_pwd" --target_file="$bk_conf" + + deskey=$(ynh_app_setting_get --app=$app --key=encrypt_key) + ynh_app_setting_set --app=$app --key=encrypt_key --value="$deskey" + ynh_replace_string --match_string="__DESKEY__" --replace_string="$deskey" --target_file="$bk_conf" + + #================================================= + # UPGRADE BAIKAL + #================================================= + ynh_script_progression --message="Upgrading Baïkal..." + + # Run Baikal upgrade + php"${phpversion}" "${final_path}/bin/upgrade.sh" + + # Cleanup old baikal-admin sessions + # since we may have changed owner of the session file + grep --files-with-matches --recursive "CSRF_TOKEN|s:" /var/lib/php/sessions | xargs rm -f + + # Store the config file checksum into the app settings + ynh_store_file_checksum --file="$bk_conf" + # Remove checksums of old files + ynh_delete_file_checksum --file="${final_path}/Specific/config.php" + ynh_delete_file_checksum --file="${final_path}/Specific/config.system.php" +fi + +#================================================= +# GENERIC FINALIZATION +#================================================= +# SECURE FILES AND DIRECTORIES +#================================================= + +# Set permissions +chown -R root: "$final_path" +chown $app "$final_path/config/baikal.yaml" +chmod 640 "$final_path/config/baikal.yaml" + +#================================================= +# SETUP SSOWAT +#================================================= +ynh_script_progression --message="Configuring SSOwat..." --weight=2 + +# Upgrade from the legacy permissions system +protected_uris=$(ynh_app_setting_get --app="$app" --key=protected_uris) +if [ -n "${protected_uris}" ]; then + ynh_app_setting_delete --app="$app" --key=protected_uris + + # Allow public access on / + ynh_permission_update --permission "main" --add "visitors" + # But restrain on /admin + ynh_permission_create --permission "admin" --url "/admin" --allowed "all_users" +fi + +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Upgrade of Baïkal completed" --last diff --git a/sources/extra_files/app/Core/Frameworks/Baikal/Core/AbstractExternalAuth.php b/sources/extra_files/app/Core/Frameworks/Baikal/Core/AbstractExternalAuth.php new file mode 100644 index 0000000..e7859ea --- /dev/null +++ b/sources/extra_files/app/Core/Frameworks/Baikal/Core/AbstractExternalAuth.php @@ -0,0 +1,130 @@ + + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +abstract class AbstractExternalAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic { + + /** + * enable autocreation of user + * + * @var PDO + */ + protected $enableAutoCreation; + + /** + * Reference to PDO connection + * + * @var PDO + */ + private $pdo; + + /** + * PDO table name we'll be using + * + * @var string + */ + private $tableName; + + /** + * Creates the backend object. + * + * If the filename argument is passed in, it will parse out the specified file fist. + * + * @param PDO $pdo + * @param string $realm + * @param string $tableName The PDO table name to use + */ + public function __construct(\PDO $pdo, $realm = 'BaikalDAV', $tableName = 'users') { + + $this->pdo = $pdo; + $this->tableName = $tableName; + $this->enableAutoCreation = true; + } + + /** + * Validates a username and password + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + public function validateUserPass($username, $password) { + + if (!$this->validateUserPassExternal($username, $password)) + return false; + + $this->currentUser = $username; + if ($this->enableAutoCreation) + $this->autoUserCreation($username); + + return true; + } + + /** + * Validates a username and password agains external backend + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + public abstract function validateUserPassExternal($username, $password); + + /** + * return the displayname and email from the external Backend + * + * @param string $username + * @return array ('displayname' => string, 'email' => string) + */ + public function getAccountValues($username) { + + return array(); + } + + /** + * create an internal user, when user not exists + * + * @param string $username + */ + private function autoUserCreation($username) { + + /* search user in DB and do nothing, when user exists */ + $stmt = $this->pdo->prepare('SELECT username FROM '.$this->tableName.' WHERE username = ?'); + $stmt->execute(array($username)); + $result = $stmt->fetchAll(); + if (count($result) != 0) + return; + + /* get account values from backend */ + $values = $this->getAccountValues($username); + if (!isset($values['displayname']) OR strlen($values['displayname']) === 0) + $values['displayname'] = $username; + if (!isset($values['email']) OR strlen($values['email']) === 0) { + if(filter_var($username, FILTER_VALIDATE_EMAIL)) + $values['email'] = $username; + else + $values['email'] = 'unset-mail'; + } + + /* create user */ + $user = new \Baikal\Model\User(); + $user->set('username', $username); + $user->set('displayname', $values['displayname']); + $user->set('email', $values['email']); + $user->persist(); + } + +} diff --git a/sources/extra_files/app/Core/Frameworks/Baikal/Core/LDAPUserBindAuth.php b/sources/extra_files/app/Core/Frameworks/Baikal/Core/LDAPUserBindAuth.php new file mode 100644 index 0000000..d558a78 --- /dev/null +++ b/sources/extra_files/app/Core/Frameworks/Baikal/Core/LDAPUserBindAuth.php @@ -0,0 +1,79 @@ + + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class LDAPUserBindAuth extends AbstractExternalAuth { + + /** + * AccountValues for getAccountValues + * + * @var array ('displayname' => string, 'email' => string) + */ + private $accountValues; + + /** + * Validates a username and password over ldap + * + * @param string $username + * @param string $password + * @return bool + */ + public function validateUserPassExternal($username, $password) { + $config = Yaml::parseFile(PROJECT_PATH_CONFIG . "baikal.yaml"); + + /* create ldap connection */ + $conn = ldap_connect($config['system']['dav_ldap_uri']); + if (!$conn) + return false; + if (!ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3)) + return false; + + /* bind with user + * error_handler have to change, because a failed bind raises an error + * this raise a secuity issue because in the stack trace is the password of user readable + */ + $arr = explode('@', $username, 2); + $dn = str_replace('%n', $username, $config['system']['dav_ldap_dn_template']); + $dn = str_replace('%u', $arr[0], $dn); + if(isset($arr[1])) $dn = str_replace('%d', $arr[1], $dn); + + set_error_handler("\Baikal\Core\LDAPUserBindAuth::exception_error_handler"); + $bind = ldap_bind($conn, $dn, $password); + restore_error_handler(); + if (!$bind) { + ldap_close($conn); + return false; + } + + /* read displayname and email from user */ + $this->accountValues = array(); + $dav_ldap_displayname_attr = $config['system']['dav_ldap_displayname_attr']; + $dav_ldap_email_attr = $config['system']['dav_ldap_email_attr']; + $sr = ldap_read($conn, $dn, '(objectclass=*)', array($dav_ldap_displayname_attr, $dav_ldap_email_attr)); + $entry = ldap_get_entries($conn, $sr); + if (isset($entry[0][$dav_ldap_displayname_attr][0])) + $this->accountValues['displayname'] = $entry[0][$dav_ldap_displayname_attr][0]; + if (isset($entry[0][$dav_ldap_email_attr][0])) + $this->accountValues['email'] = $entry[0][$dav_ldap_email_attr][0]; + + /* close */ + ldap_close($conn); + return true; + } + + public function getAccountValues($username) { + + return $this->accountValues; + } + + # WorkAround error_handler in failed bind of LDAP + public static function exception_error_handler($errno, $errstr, $errfile, $errline) { + } +} diff --git a/sources/bin/upgrade.sh b/sources/extra_files/app/bin/upgrade.sh similarity index 85% rename from sources/bin/upgrade.sh rename to sources/extra_files/app/bin/upgrade.sh index 6468200..b77d96b 100755 --- a/sources/bin/upgrade.sh +++ b/sources/extra_files/app/bin/upgrade.sh @@ -41,15 +41,22 @@ if (!file_exists(PROJECT_PATH_ROOT . 'vendor/')) { } require PROJECT_PATH_ROOT . "vendor/autoload.php"; +use Symfony\Component\Yaml\Yaml; # Extend VersionUpgrade for cli usage class CLIUpgrade extends \BaikalAdmin\Controller\Install\VersionUpgrade { function run() { - $sBaikalVersion = BAIKAL_VERSION; - $sBaikalConfiguredVersion = BAIKAL_CONFIGURED_VERSION; + try { + $config = Yaml::parseFile(PROJECT_PATH_CONFIG . "baikal.yaml"); + } catch (\Exception $e) { + $this->output('Error reading baikal.yaml file : ' . $e->getMessage()); + } - if (BAIKAL_CONFIGURED_VERSION === BAIKAL_VERSION) { + $sBaikalVersion = BAIKAL_VERSION; + $sBaikalConfiguredVersion = $config['system']['configured_version']; + + if (isset($config['system']['configured_version']) && $sBaikalConfiguredVersion === BAIKAL_VERSION) { $this->output("Baïkal is already configured for version " . $sBaikalVersion); return true; } else { @@ -57,7 +64,7 @@ class CLIUpgrade extends \BaikalAdmin\Controller\Install\VersionUpgrade { } try { - $bSuccess = $this->upgrade(BAIKAL_CONFIGURED_VERSION, BAIKAL_VERSION); + $bSuccess = $this->upgrade($sBaikalConfiguredVersion, BAIKAL_VERSION); } catch (\Exception $e) { $bSuccess = false; $this->output("Uncaught exception during upgrade: " . (string)$e); @@ -88,11 +95,6 @@ class CLIUpgrade extends \BaikalAdmin\Controller\Install\VersionUpgrade { # Bootstrap BaikalAdmin \BaikalAdmin\Framework::bootstrap(); -if (!defined("BAIKAL_CONFIGURED_VERSION") || !defined("BAIKAL_ADMIN_PASSWORDHASH")) { - echo "Baïkal is not properly configured!\n"; - exit(1); -} - # Run the upgrade $oUpgrade = new CLIUpgrade(); if (!$oUpgrade->run()) { diff --git a/sources/patches/app-add-ldap-auth.patch b/sources/patches/app-add-ldap-auth.patch new file mode 100644 index 0000000..eb51c42 --- /dev/null +++ b/sources/patches/app-add-ldap-auth.patch @@ -0,0 +1,84 @@ +diff --git a/Core/Frameworks/Baikal/Core/Server.php b/Core/Frameworks/Baikal/Core/Server.php +index e96fe39..b90b49e 100644 +--- a/Core/Frameworks/Baikal/Core/Server.php ++++ b/Core/Frameworks/Baikal/Core/Server.php +@@ -133,6 +133,8 @@ class Server { + + if ($this->authType === 'Basic') { + $authBackend = new \Baikal\Core\PDOBasicAuth($this->pdo, $this->authRealm); ++ } elseif ($this->authType === 'LDAP-UserBind') { ++ $authBackend = new \Baikal\Core\LDAPUserBindAuth($this->pdo, $this->authRealm); + } else { + $authBackend = new \Sabre\DAV\Auth\Backend\PDO($this->pdo); + $authBackend->setRealm($this->authRealm); +diff --git a/Core/Frameworks/Baikal/Model/Config/Standard.php b/Core/Frameworks/Baikal/Model/Config/Standard.php +index 1ef5a51..32ec217 100644 +--- a/Core/Frameworks/Baikal/Model/Config/Standard.php ++++ b/Core/Frameworks/Baikal/Model/Config/Standard.php +@@ -51,6 +51,22 @@ class Standard extends \Baikal\Model\Config { + "type" => "string", + "comment" => "HTTP authentication type for WebDAV; default Digest" + ], ++ "dav_ldap_uri" => [ ++ "type" => "string", ++ "comment" => "URI to LDAP Server (for ldap-userbind auth); default ldapi:///" ++ ], ++ "dav_ldap_dn_template" => [ ++ "type" => "string", ++ "comment" => "User DN for bind; with replacments %n => username, %u => user part, %d => domain part of username" ++ ], ++ "dav_ldap_displayname_attr" => [ ++ "type" => "string", ++ "comment" => "LDAP-attribute for displayname; default cn" ++ ], ++ "dav_ldap_email_attr" => [ ++ "type" => "string", ++ "comment" => "LDAP-attribute for email; default mail" ++ ], + "admin_passwordhash" => [ + "type" => "string", + "comment" => "Baïkal Web admin password hash; Set via Baïkal Web Admin", +@@ -64,6 +80,10 @@ class Standard extends \Baikal\Model\Config { + "card_enabled" => true, + "cal_enabled" => true, + "dav_auth_type" => "Digest", ++ "dav_ldap_uri" => "ldapi:///", ++ "dav_ldap_dn_template" => "uid=%n,dc=example,dc=com", ++ "dav_ldap_displayname_attr" => "cn", ++ "dav_ldap_email_attr" => "mail", + "admin_passwordhash" => "", + "auth_realm" => "BaikalDAV", + "base_uri" => "" +@@ -103,7 +123,31 @@ class Standard extends \Baikal\Model\Config { + $oMorpho->add(new \Formal\Element\Listbox([ + "prop" => "dav_auth_type", + "label" => "WebDAV authentication type", +- "options" => ["Digest", "Basic"] ++ "options" => ["Digest", "Basic", "LDAP-UserBind"] ++ ])); ++ ++ $oMorpho->add(new \Formal\Element\Text([ ++ "prop" => "dav_ldap_uri", ++ "label" => "LDAP URI" ++ ])); ++ ++ $oMorpho->add(new \Formal\Element\Text([ ++ "prop" => "dav_ldap_dn_template", ++ "label" => "LDAP DN template", ++ "popover" => [ ++ "title" => "posible placeholder", ++ "content" => "%n - username
%u - user part of username , when it is an email address)
%d - domain part", ++ ] ++ ])); ++ ++ $oMorpho->add(new \Formal\Element\Text([ ++ "prop" => "dav_ldap_displayname_attr", ++ "label" => "LDAP attribute for DisplayName" ++ ])); ++ ++ $oMorpho->add(new \Formal\Element\Text([ ++ "prop" => "dav_ldap_email_attr", ++ "label" => "LDAP attribute for eMail" + ])); + + $oMorpho->add(new \Formal\Element\Password([