diff --git a/bin/quickstart.sh b/bin/quickstart.sh index f48ff50c..d63858f6 100755 --- a/bin/quickstart.sh +++ b/bin/quickstart.sh @@ -1,141 +1,131 @@ #!/bin/bash set -e function die() { echo "$1" exit 1 } rpm -qv docker-compose >/dev/null 2>&1 || \ test ! -z "$(which docker-compose 2>/dev/null)" || \ die "Is docker-compose installed?" test ! -z "$(grep 'systemd.unified_cgroup_hierarchy=0' /proc/cmdline)" || \ die "systemd containers only work with cgroupv1 (use 'grubby --update-kernel=ALL --args=\"systemd.unified_cgroup_hierarchy=0\"' and a reboot to fix)" base_dir=$(dirname $(dirname $0)) export DOCKER_BUILDKIT=0 docker-compose down --remove-orphans docker volume rm kolab_mariadb || : docker volume rm kolab_imap || : docker volume rm kolab_ldap || : # We can't use the following artisan commands because it will just block if redis is unavailable: # src/artisan octane:stop >/dev/null 2>&1 || : # src/artisan horizon:terminate >/dev/null 2>&1 || : # we therefore just kill all artisan processes running. pkill -9 -f artisan || : pkill -9 -f swoole || : bin/regen-certs docker-compose build coturn kolab mariadb meet pdns proxy redis haproxy docker-compose up -d coturn kolab mariadb meet pdns redis # Workaround until we have docker-compose --wait (https://github.com/docker/compose/pull/8777) function wait_for_container { container_id="$1" container_name="$(docker inspect "${container_id}" --format '{{ .Name }}')" echo "Waiting for container: ${container_name} [${container_id}]" waiting_done="false" while [[ "${waiting_done}" != "true" ]]; do container_state="$(docker inspect "${container_id}" --format '{{ .State.Status }}')" if [[ "${container_state}" == "running" ]]; then health_status="$(docker inspect "${container_id}" --format '{{ .State.Health.Status }}')" echo "${container_name}: container_state=${container_state}, health_status=${health_status}" if [[ ${health_status} == "healthy" ]]; then waiting_done="true" fi else echo "${container_name}: container_state=${container_state}" waiting_done="true" fi sleep 1; done; } if [ "$1" == "--nodev" ]; then echo "starting everything in containers" docker-compose -f docker-compose.build.yml build swoole docker-compose build webapp docker-compose up -d webapp proxy haproxy wait_for_container 'kolab-webapp' exit 0 fi echo "Starting the development environment" rpm -qv composer >/dev/null 2>&1 || \ test ! -z "$(which composer 2>/dev/null)" || \ die "Is composer installed?" rpm -qv npm >/dev/null 2>&1 || \ test ! -z "$(which npm 2>/dev/null)" || \ die "Is npm installed?" rpm -qv php >/dev/null 2>&1 || \ test ! -z "$(which php 2>/dev/null)" || \ die "Is php installed?" rpm -qv php-ldap >/dev/null 2>&1 || \ test ! -z "$(php --ini | grep ldap)" || \ die "Is php-ldap installed?" rpm -qv php-mysqlnd >/dev/null 2>&1 || \ test ! -z "$(php --ini | grep mysql)" || \ die "Is php-mysqlnd installed?" test ! -z "$(php --modules | grep swoole)" || \ die "Is swoole installed?" # Ensure the containers we depend on are fully started wait_for_container 'kolab' wait_for_container 'kolab-redis' pushd ${base_dir}/src/ rm -rf vendor/ composer.lock php -dmemory_limit=-1 $(which composer) install npm install find bootstrap/cache/ -type f ! -name ".gitignore" -delete ./artisan key:generate ./artisan clear-compiled ./artisan cache:clear ./artisan horizon:install -if [ ! -f storage/oauth-public.key -o ! -f storage/oauth-private.key ]; then - ./artisan passport:keys --force -fi - -cat >> .env << EOF -PASSPORT_PRIVATE_KEY="$(cat storage/oauth-private.key)" -PASSPORT_PUBLIC_KEY="$(cat storage/oauth-public.key)" -EOF - - if rpm -qv chromium 2>/dev/null; then chver=$(rpmquery --queryformat="%{VERSION}" chromium | awk -F'.' '{print $1}') ./artisan dusk:chrome-driver ${chver} fi if [ ! -f 'resources/countries.php' ]; then ./artisan data:countries fi npm run dev popd pushd ${base_dir}/src/ rm -rf database/database.sqlite ./artisan db:ping --wait php -dmemory_limit=512M ./artisan migrate:refresh --seed ./artisan data:import || : nohup ./artisan octane:start --host=$(grep OCTANE_HTTP_HOST .env | tail -n1 | sed "s/OCTANE_HTTP_HOST=//") > octane.out & nohup ./artisan horizon > horizon.out & popd docker-compose up --no-deps -d proxy haproxy diff --git a/config.demo/src/database/seeds/AppKeySeeder.php b/config.demo/src/database/seeds/AppKeySeeder.php new file mode 100644 index 00000000..4554bf5d --- /dev/null +++ b/config.demo/src/database/seeds/AppKeySeeder.php @@ -0,0 +1,62 @@ +generateRandomKey(); + $this->writeNewEnvironmentFileWith($key); + } + + /** + * Generate a random key for the application. + * + * @return string + */ + protected function generateRandomKey() + { + return 'base64:' . base64_encode( + Encrypter::generateKey(\config('app.cipher')) + ); + } + + /** + * Write a new environment file with the given key. + * + * @param string $key + * @return void + */ + protected function writeNewEnvironmentFileWith($key) + { + file_put_contents(\app()->environmentFilePath(), preg_replace( + $this->keyReplacementPattern(), + 'APP_KEY=' . $key, + file_get_contents(\app()->environmentFilePath()) + )); + } + + /** + * Get a regex pattern that will match env APP_KEY with any random key. + * + * @return string + */ + protected function keyReplacementPattern() + { + $escaped = preg_quote('=' . \config('app.key'), '/'); + return "/^APP_KEY{$escaped}/m"; + } +} + diff --git a/config.demo/src/database/seeds/DatabaseSeeder.php b/config.demo/src/database/seeds/DatabaseSeeder.php index cebfef72..bef5f2d2 100644 --- a/config.demo/src/database/seeds/DatabaseSeeder.php +++ b/config.demo/src/database/seeds/DatabaseSeeder.php @@ -1,32 +1,34 @@ call([ + Seeds\AppKeySeeder::class, + Seeds\PassportSeeder::class, Seeds\IP4NetSeeder::class, Seeds\TenantSeeder::class, Seeds\DiscountSeeder::class, Seeds\DomainSeeder::class, Seeds\SkuSeeder::class, Seeds\PackageSeeder::class, Seeds\PlanSeeder::class, Seeds\PowerDNSSeeder::class, Seeds\UserSeeder::class, Seeds\OauthClientSeeder::class, Seeds\ResourceSeeder::class, Seeds\SharedFolderSeeder::class, Seeds\MeetRoomSeeder::class, ]); } } diff --git a/config.demo/src/database/seeds/PassportSeeder.php b/config.demo/src/database/seeds/PassportSeeder.php new file mode 100644 index 00000000..4bc8aa1e --- /dev/null +++ b/config.demo/src/database/seeds/PassportSeeder.php @@ -0,0 +1,103 @@ +getPublicKey()); + file_put_contents($privateKey, (string) $key); + + $this->writeNewEnvironmentFileWith('PASSPORT_PRIVATE_KEY', 'passport.private_key', $key); + $this->writeNewEnvironmentFileWith('PASSPORT_PUBLIC_KEY', 'passport.public_key', (string) $key->getPublicKey()); + + //Create a password grant client for the webapp + $secret = $this->generateRandomKey(); + + $client = Passport::client()->forceFill([ + 'user_id' => null, + 'name' => "Kolab Password Grant Client", + 'secret' => $secret, + 'provider' => 'users', + 'redirect' => 'https://' . \config('app.website_domain'), + 'personal_access_client' => 0, + 'password_client' => 1, + 'revoked' => false, + ]); + $client->save(); + + $this->writeNewEnvironmentFileWith('PASSPORT_PROXY_OAUTH_CLIENT_ID', 'auth.proxy.client_id', $client->id); + $this->writeNewEnvironmentFileWith('PASSPORT_PROXY_OAUTH_CLIENT_SECRET', 'auth.proxy.client_secret', $secret); + } + + /** + * Generate a random key for the application. + * + * @return string + */ + protected function generateRandomKey() + { + return base64_encode( + Encrypter::generateKey(\config('app.cipher')) + ); + } + + /** + * Write a new environment file with the given key. + * + * @param string $key + * @param string $configKey + * @param string $value + * @return void + */ + protected function writeNewEnvironmentFileWith($key, $configKey, $value) + { + $path = \app()->environmentFilePath(); + $count = 0; + $line = "{$key}=\"{$value}\""; + $result = preg_replace( + $this->keyReplacementPattern($key, \config($configKey)), + $line, + file_get_contents($path), + -1, + $count + ); + //Append if it doesn't exist + if ($count == 0) { + $result = $result . "\n$line"; + } + file_put_contents($path, $result); + } + + /** + * Get a regex pattern that will match env APP_KEY with any random key. + * + * @return string + */ + protected function keyReplacementPattern($key, $value) + { + $escaped = preg_quote("={$value}", '/'); + return "/^{$key}{$escaped}/m"; + } +}