diff --git a/.github/jobs/baseinstall.sh b/.github/jobs/baseinstall.sh index ce7e90e613..32331313d3 100755 --- a/.github/jobs/baseinstall.sh +++ b/.github/jobs/baseinstall.sh @@ -11,7 +11,7 @@ export APP_ENV="${4:-prod}" # In the test environment, we need to use a different database [ "$APP_ENV" = "prod" ] && DATABASE_NAME=domjudge || DATABASE_NAME=domjudge_test -MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root} +MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-mysql_root_password} set -euxo pipefail @@ -29,8 +29,8 @@ cd .. section_end section_start "Set simple admin password" -echo "password" > ./etc/initial_admin_password.secret -echo "default login admin password password" > ~/.netrc +echo "admin_password" > ./etc/initial_admin_password.secret +echo "default login admin password admin_password" > ~/.netrc section_end section_start "Install domserver" @@ -67,31 +67,28 @@ password=${MYSQL_ROOT_PASSWORD} EOF cat ~/.my.cnf -mysql_root "CREATE DATABASE IF NOT EXISTS \`$DATABASE_NAME\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" -mysql_root "CREATE USER IF NOT EXISTS \`domjudge\`@'%' IDENTIFIED BY 'domjudge';" -mysql_root "GRANT SELECT, INSERT, UPDATE, DELETE ON \`$DATABASE_NAME\`.* TO 'domjudge'@'%';" -mysql_root "FLUSH PRIVILEGES;" +echo 'unused:sqlserver:$DATABASE_NAME:domjudge:djdb-pw:3306' > /opt/domjudge/domserver/etc/dbpasswords.secret + +/opt/domjudge/domserver/bin/dj_setup_database create-db-users # Show some MySQL debugging -mysql_root "show databases" -mysql_root "SELECT CURRENT_USER();" -mysql_root "SELECT USER();" -mysql_root "SELECT user,host FROM mysql.user" -mysql_root "SET GLOBAL max_allowed_packet=1073741824" -mysql_root "SHOW GLOBAL STATUS LIKE 'Connection_errors_%'" -mysql_root "SHOW VARIABLES LIKE '%_timeout'" -echo "unused:sqlserver:$DATABASE_NAME:domjudge:domjudge:3306" > /opt/domjudge/domserver/etc/dbpasswords.secret -mysql_user "SELECT CURRENT_USER();" -mysql_user "SELECT USER();" +mysql_log "show databases" +mysql_log "SELECT CURRENT_USER();" +mysql_log "SELECT USER();" +mysql_log "SELECT user,host FROM mysql.user" +mysql_log "SET GLOBAL max_allowed_packet=1073741824" +mysql_log "SHOW GLOBAL STATUS LIKE 'Connection_errors_%'" +mysql_log "SHOW VARIABLES LIKE '%_timeout'" section_end if [ "${db}" = "install" ]; then section_start "Install DOMjudge database" - /opt/domjudge/domserver/bin/dj_setup_database -uroot -p${MYSQL_ROOT_PASSWORD} bare-install + /opt/domjudge/domserver/bin/dj_setup_database bare-install section_end elif [ "${db}" = "upgrade" ]; then section_start "Upgrade DOMjudge database" - /opt/domjudge/domserver/bin/dj_setup_database -uroot -p${MYSQL_ROOT_PASSWORD} upgrade + /opt/domjudge/domserver/bin/dj_setup_database update-password + /opt/domjudge/domserver/bin/dj_setup_database upgrade section_end fi @@ -131,31 +128,31 @@ if [ "${db}" = "install" ]; then section_start "Install the example data" if [ "$version" = "unit" ]; then # Make sure admin has no team associated so we will not insert submissions during unit tests. - mysql_root "UPDATE user SET teamid=null WHERE userid=1;" $DATABASE_NAME + mysql_log "UPDATE user SET teamid=null WHERE userid=1;" $DATABASE_NAME fi - /opt/domjudge/domserver/bin/dj_setup_database -uroot -p${MYSQL_ROOT_PASSWORD} install-examples | tee -a "$ARTIFACTS/mysql.txt" + /opt/domjudge/domserver/bin/dj_setup_database install-examples | tee -a "$ARTIFACTS/mysql.txt" section_end fi section_start "Setup user" # We're using the admin user in all possible roles -mysql_root "DELETE FROM userrole WHERE userid=1;" $DATABASE_NAME +mysql_log "DELETE FROM userrole WHERE userid=1;" $DATABASE_NAME if [ "$version" = "team" ]; then # Add team to admin user - mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 3);" $DATABASE_NAME - mysql_root "UPDATE user SET teamid = 1 WHERE userid = 1;" $DATABASE_NAME + mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 3);" $DATABASE_NAME + mysql_log "UPDATE user SET teamid = 1 WHERE userid = 1;" $DATABASE_NAME elif [ "$version" = "jury" ]; then # Add jury to admin user - mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 2);" $DATABASE_NAME + mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 2);" $DATABASE_NAME elif [ "$version" = "balloon" ]; then # Add balloon to admin user - mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 4);" $DATABASE_NAME + mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 4);" $DATABASE_NAME elif [ "$version" = "admin" ]; then # Add admin to admin user - mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 1);" $DATABASE_NAME + mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 1);" $DATABASE_NAME elif [ "$version" = "all" ] || [ "$version" = "unit" ]; then - mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 1);" $DATABASE_NAME - mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 3);" $DATABASE_NAME - mysql_root "UPDATE user SET teamid = 1 WHERE userid = 1;" $DATABASE_NAME + mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 1);" $DATABASE_NAME + mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 3);" $DATABASE_NAME + mysql_log "UPDATE user SET teamid = 1 WHERE userid = 1;" $DATABASE_NAME fi section_end diff --git a/.github/jobs/ci_settings.sh b/.github/jobs/ci_settings.sh old mode 100644 new mode 100755 index 391996752b..b807178e67 --- a/.github/jobs/ci_settings.sh +++ b/.github/jobs/ci_settings.sh @@ -24,14 +24,9 @@ section_end_internal () { trace_on } -mysql_root () { +mysql_log () { # shellcheck disable=SC2086 - echo "$1" | mysql -uroot -proot ${2:-} | tee -a "$ARTIFACTS"/mysql.txt -} - -mysql_user () { - # shellcheck disable=SC2086 - echo "$1" | mysql -udomjudge -pdomjudge ${2:-} | tee -a "$ARTIFACTS"/mysql.txt + echo "$1" | mysql ${2:-} | tee -a "$ARTIFACTS"/mysql.txt } show_phpinfo() { diff --git a/.github/workflows/database-upgrade.yml b/.github/workflows/database-upgrade.yml index 47b6a14ed1..c0015ced2c 100644 --- a/.github/workflows/database-upgrade.yml +++ b/.github/workflows/database-upgrade.yml @@ -18,19 +18,17 @@ jobs: ports: - 3306:3306 env: - MYSQL_ROOT_PASSWORD: root - MYSQL_USER: domjudge - MYSQL_PASSWORD: domjudge + MYSQL_ROOT_PASSWORD: mysql_root_password options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - uses: actions/checkout@v4 - name: Import Database - run: mysql -hsqlserver -uroot -proot < .github/jobs/data/dj733.sql + run: mysql -hsqlserver -uroot -pmysql_root_password < .github/jobs/data/dj733.sql - name: Upgrade DOMjudge run: .github/jobs/baseinstall.sh default upgrade - name: Setting initial Admin Password run: echo "pass" > /opt/domjudge/domserver/etc/initial_admin_password.secret - name: Check for Errors in the Upgrade - run: mysql -hsqlserver -uroot -proot -e "SHOW TABLES FROM domjudge;" + run: mysql -hsqlserver -uroot -pmysql_root_password -e "SHOW TABLES FROM domjudge;" - name: Check for Errors in DOMjudge Webinterface run: .github/jobs/webstandard.sh none admin diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index e6921fa834..39b3a09914 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -19,9 +19,7 @@ jobs: ports: - 3306:3306 env: - MYSQL_ROOT_PASSWORD: root - MYSQL_USER: domjudge - MYSQL_PASSWORD: domjudge + MYSQL_ROOT_PASSWORD: mysql_root_password options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - uses: actions/checkout@v4 @@ -81,7 +79,7 @@ jobs: done - name: dump the db if: ${{ !cancelled() }} - run: mysqldump -uroot -proot --quick --max_allowed_packet=1024M domjudge > /tmp/db.sql + run: mysqldump -uroot -pmysql_root_password --quick --max_allowed_packet=1024M domjudge > /tmp/db.sql - name: Upload database dump for debugging if: ${{ !cancelled() }} uses: actions/upload-artifact@v4 diff --git a/.github/workflows/webstandard.yml b/.github/workflows/webstandard.yml index 23e0760cab..ca46ac7658 100644 --- a/.github/workflows/webstandard.yml +++ b/.github/workflows/webstandard.yml @@ -17,9 +17,7 @@ jobs: ports: - 3306:3306 env: - MYSQL_ROOT_PASSWORD: root - MYSQL_USER: domjudge - MYSQL_PASSWORD: domjudge + MYSQL_ROOT_PASSWORD: mysql_root_password options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3 strategy: matrix: diff --git a/sql/dj_setup_database.in b/sql/dj_setup_database.in index fc803939c3..f905bef194 100755 --- a/sql/dj_setup_database.in +++ b/sql/dj_setup_database.in @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # @configure_input@ # This script allows one to perform DOMjudge database setup actions. @@ -6,6 +6,8 @@ set -e +set -x + BINDIR="@domserver_bindir@" ETCDIR="@domserver_etcdir@" WEBAPPDIR="@domserver_webappdir@" @@ -52,30 +54,47 @@ not have to pass any of the options above. EOF } +urlencode() +{ + php -r "echo rawurlencode('$1');" +} + +# This is global variable to be able to return the output from +# mysql_options() below as an array, which is not possible otherwise. +declare -a _mysql_options + mysql_options() { + local user pass + _mysql_options=() + # shellcheck disable=SC2153 if [ -n "$DBUSER" ]; then - _user="-u $DBUSER" - else - _user="${DBA_USER:+-u ${DBA_USER}}" + _mysql_options+=('-u' "$DBUSER") + elif [ -n "$DBA_USER" ]; then + _mysql_options+=('-u' "$DBA_USER") fi # shellcheck disable=SC2153 if [ -n "$PASSWD" ]; then - _pass="-p$PASSWD" - else - [ -n "$PROMPT_PASSWD" ] && _pass="-p" - [ -n "$DBA_PASSWD" ] && _pass="-p$DBA_PASSWD" + _mysql_options+=("-p$PASSWD") + elif [ -n "$DBA_PASSWD" ]; then + _mysql_options+=("-p$DBA_PASSWD") + elif [ -n "$PROMPT_PASSWD" ]; then + _mysql_options+=('-p') fi - [ -z "$USE_SOCKET" ] && port="-P$DBPORT" - echo $_user ${_pass:+"$_pass"} -h "$DBHOST" ${port:+"$port"} + _mysql_options+=('-h' "$DBHOST") + + if [ -z "$USE_SOCKET" ]; then + _mysql_options+=("-P$DBPORT") + fi } # Wrapper around mysql command to allow setting options, user, etc. mysql() { - command mysql $(mysql_options) --silent --skip-column-names "$@" + mysql_options + command mysql "${_mysql_options[@]}" --silent --skip-column-names "$@" } # Quick shell hack to get a key from an INI file. @@ -126,12 +145,17 @@ symfony_console() fi if [ -n "$DBA_USER" ]; then + user=$(urlencode "${DBA_USER}") + host=$(urlencode "${domjudge_DBHOST}") + db=$(urlencode "${domjudge_DBNAME}") if [ -n "$DBA_PASSWD" ]; then - DATABASE_URL=mysql://${DBA_USER}:${DBA_PASSWD}@${domjudge_DBHOST}:${domjudge_DBPORT}/${domjudge_DBNAME} + DATABASE_URL="mysql://$user:$(urlencode "${DBA_PASSWD}")@$host:${domjudge_DBPORT}/$db" else - DATABASE_URL=mysql://${DBA_USER}@${domjudge_DBHOST}:${domjudge_DBPORT}/${domjudge_DBNAME} + DATABASE_URL="mysql://$user@$host:${domjudge_DBPORT}/$db" fi fi + else + DATABASE_URL="mysql://$DBUSER:$(urlencode "${DB_PASSWD}")@$host:${domjudge_DBPORT}/$db" fi if [ -n "$DATABASE_URL" ]; then @@ -236,7 +260,6 @@ remove_db_users() update_password() { - read_dbpasswords ( echo "ALTER USER '$domjudge_DBUSER'@'localhost' IDENTIFIED BY '$domjudge_PASSWD';" echo "FLUSH PRIVILEGES;" @@ -247,7 +270,7 @@ update_password() install_examples() { - DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-example-data + symfony_console domjudge:load-example-data "$EXAMPLEPROBDIR"/generate-contest-yaml ( cd "$EXAMPLEPROBDIR" && yes y | "$BINDIR"/import-contest ) } @@ -321,8 +344,8 @@ install-loadtest) read_dbpasswords create_db_users symfony_console doctrine:migrations:migrate -n - DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-default-data - DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-gatling-data + symfony_console domjudge:load-default-data + symfony_console domjudge:load-gatling-data ;; create-db-users) @@ -331,6 +354,7 @@ create-db-users) ;; update-password) + read_dbpasswords update_password ;; @@ -338,7 +362,7 @@ bare-install|install) read_dbpasswords create_db_users symfony_console doctrine:migrations:migrate -n - DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-default-data + symfony_console domjudge:load-default-data if [ "$1" = "install" ]; then install_examples verbose "SQL structure and default/example data installed." @@ -362,9 +386,8 @@ upgrade) fi symfony_console doctrine:migrations:migrate -n - DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-default-data - symfony_console domjudge:refresh-cache + symfony_console domjudge:load-default-data verbose "DOMjudge database upgrade completed." ;; diff --git a/webapp/config/load_db_secrets.php b/webapp/config/load_db_secrets.php index ea4ca11b83..c3a8b3dfb5 100644 --- a/webapp/config/load_db_secrets.php +++ b/webapp/config/load_db_secrets.php @@ -36,7 +36,11 @@ function get_db_url(): string break; } - return sprintf('mysql://%s:%s@%s:%d/%s?serverVersion=5.7.0', $user, $pass, $host, $port ?? 3306, $db); + return sprintf( + 'mysql://%s:%s@%s:%d/%s?serverVersion=5.7.0', + rawurlencode($user), rawurlencode($pass), rawurlencode($host), + $port ?? 3306, rawurlencode($db) + ); } function get_app_secret(): string diff --git a/webapp/config/packages/doctrine.yaml b/webapp/config/packages/doctrine.yaml index 89bf0223b3..b919a7bd43 100644 --- a/webapp/config/packages/doctrine.yaml +++ b/webapp/config/packages/doctrine.yaml @@ -7,7 +7,7 @@ doctrine: charset: utf8mb4 collate: utf8mb4_unicode_ci - url: '%env(resolve:DATABASE_URL)%' + url: '%env(DATABASE_URL)%' profiling_collect_backtrace: '%kernel.debug%' types: tinyint: App\Doctrine\DBAL\Types\TinyIntType