Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions .github/workflows/pull_request_frontend_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: Front End Tests On Pull Request

on:
pull_request:
types: [opened, reopened, edited, synchronize]
branches: ["main"]

jobs:

js-unit-tests:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Run Jest unit tests
run: yarn test:unit:ci
- name: Upload Jest coverage
uses: actions/upload-artifact@v4
if: always()
with:
name: jest-coverage
path: tests/js/coverage
retention-days: 5

e2e-tests:
runs-on: ubuntu-latest
env:
APP_ENV: testing
APP_DEBUG: true
APP_KEY: base64:4vh0op/S1dAsXKQ2bbdCfWRyCI9r8NNIdPXyZWt9PX4=
APP_URL: http://localhost:8001
DEV_EMAIL_TO: smarcet@gmail.com
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_DATABASE: idp_test
DB_USERNAME: root
DB_PASSWORD: 1qaz2wsx
REDIS_HOST: 127.0.0.1
REDIS_PORT: 6379
REDIS_DB: 0
REDIS_PASSWORD: 1qaz2wsx
REDIS_DATABASES: 16
SSL_ENABLED: false
SESSION_DRIVER: redis
SESSION_COOKIE_SECURE: false
PHP_VERSION: 8.3
OTEL_SDK_DISABLED: true
OTEL_SERVICE_ENABLED: false
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: 1qaz2wsx
MYSQL_DATABASE: idp_test
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Create Redis
uses: supercharge/redis-github-action@1.8.1
with:
redis-port: 6379
redis-password: 1qaz2wsx
- name: Check out repository code
uses: actions/checkout@v4
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: pdo_mysql, mbstring, exif, pcntl, bcmath, sockets, gettext, apcu
- name: Install PHP dependencies
uses: ramsey/composer-install@v3
env:
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.PAT }}"} }'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Build frontend assets
run: yarn build
- name: Prepare application
run: |
./update_doctrine.sh
php artisan doctrine:migrations:migrate --no-interaction
php artisan db:seed --force
php artisan idp:create-super-admin test@test.com '1Qaz2wsx!'
php artisan idp:create-raw-user e2e@test.com '1Qaz2wsx!'
- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium
- name: Start web server
run: php artisan serve --host=127.0.0.1 --port=8001 &
- name: Wait for server to be ready
run: |
for i in $(seq 1 20); do
curl -sf http://localhost:8001 > /dev/null 2>&1 && echo "Server ready" && exit 0
sleep 2
done
echo "Server did not start in time" && exit 1
- name: Run E2E tests
run: yarn test:e2e --reporter=list
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: tests/e2e/report
retention-days: 7
- name: Upload Playwright traces
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-traces
path: test-results/
retention-days: 7
3 changes: 3 additions & 0 deletions .github/workflows/pull_request_unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ jobs:
PHP_VERSION: 8.3
OTEL_SDK_DISABLED: true
OTEL_SERVICE_ENABLED: false
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
PLAYWRIGHT_WORKERS: 4
services:
mysql:
image: mysql:8.0
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ jobs:
PHP_VERSION: 8.3
OTEL_SDK_DISABLED: true
OTEL_SERVICE_ENABLED: false
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
services:
mysql:
image: mysql:8.0
Expand Down
129 changes: 129 additions & 0 deletions .github/workflows/push_frontend_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
name: Front End Tests On Push

on: push

jobs:

js-unit-tests:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Run Jest unit tests
run: yarn test:unit:ci
- name: Upload Jest coverage
uses: actions/upload-artifact@v4
if: always()
with:
name: jest-coverage
path: tests/js/coverage
retention-days: 5

e2e-tests:
runs-on: ubuntu-latest
env:
APP_ENV: testing
APP_DEBUG: true
APP_KEY: base64:4vh0op/S1dAsXKQ2bbdCfWRyCI9r8NNIdPXyZWt9PX4=
APP_URL: http://localhost:8001
DEV_EMAIL_TO: smarcet@gmail.com
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_DATABASE: idp_test
DB_USERNAME: root
DB_PASSWORD: 1qaz2wsx
REDIS_HOST: 127.0.0.1
REDIS_PORT: 6379
REDIS_DB: 0
REDIS_PASSWORD: 1qaz2wsx
REDIS_DATABASES: 16
SSL_ENABLED: false
SESSION_DRIVER: redis
SESSION_COOKIE_SECURE: false
PHP_VERSION: 8.3
OTEL_SDK_DISABLED: true
OTEL_SERVICE_ENABLED: false
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
PLAYWRIGHT_WORKERS: 4
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: 1qaz2wsx
MYSQL_DATABASE: idp_test
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Create Redis
uses: supercharge/redis-github-action@1.8.1
with:
redis-port: 6379
redis-password: 1qaz2wsx
- name: Check out repository code
uses: actions/checkout@v4
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: pdo_mysql, mbstring, exif, pcntl, bcmath, sockets, gettext, apcu
- name: Install PHP dependencies
uses: ramsey/composer-install@v3
env:
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.PAT }}"} }'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Build frontend assets
run: yarn build
- name: Prepare application
run: |
./update_doctrine.sh
php artisan doctrine:migrations:migrate --no-interaction
php artisan db:seed --force
php artisan idp:create-super-admin test@test.com '1Qaz2wsx!'
php artisan idp:create-raw-user e2e@test.com '1Qaz2wsx!'
- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium
- name: Start web server
run: php artisan serve --host=127.0.0.1 --port=8001 &
- name: Wait for server to be ready
run: |
for i in $(seq 1 20); do
curl -sf http://localhost:8001 > /dev/null 2>&1 && echo "Server ready" && exit 0
sleep 2
done
echo "Server did not start in time" && exit 1
- name: Run E2E tests
run: yarn test:e2e --reporter=list
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: tests/e2e/report
retention-days: 7
- name: Upload Playwright traces
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-traces
path: test-results/
retention-days: 7
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,11 @@ model.sql
/.phpunit.cache/
docker-compose/mysql/model/*.sql
public/assets/*.map
public/assets/css/*.map
public/assets/css/*.map
# Playwright
/tests/e2e/report/

# Jest
/tests/js/coverage/
/playwright-report/
/.playwright-out/
57 changes: 57 additions & 0 deletions app/Console/Commands/CreateRawUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php namespace App\Console\Commands;
/**
* Copyright 2026 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

use Auth\User;
use Illuminate\Console\Command;
use LaravelDoctrine\ORM\Facades\EntityManager;

/**
* Class CreateRawUser
*
* Creates a plain, verified user with no group memberships.
* Because this user belongs to none of the groups listed in
* two_factor.enforced_groups, MFA is never required — making it
* safe to use in automated E2E tests that must complete the full
* login → redirect flow without a 2FA challenge.
*
* @package App\Console\Commands
*/
class CreateRawUser extends Command
{
protected $signature = 'idp:create-raw-user {email} {password}';

protected $description = 'Create a plain verified user with no group memberships (useful for E2E tests)';

public function handle()
{
$email = trim($this->argument('email'));
$password = trim($this->argument('password'));

$user = EntityManager::getRepository(User::class)->findOneBy(['email' => $email]);
if (is_null($user)) {
$user = new User();
$user->setEmail($email);
$user->verifyEmail();
$user->setPassword($password);
$user->setFirstName($email);
$user->setLastName($email);
$user->setIdentifier($email);
EntityManager::persist($user);
EntityManager::flush();
$this->info("Created user: {$email}");
} else {
$this->info("User already exists: {$email}");
}
}
}
1 change: 1 addition & 0 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Kernel extends ConsoleKernel
Commands\CleanOAuth2StaleData::class,
Commands\CleanOpenIdStaleData::class,
Commands\CreateSuperAdmin::class,
Commands\CreateRawUser::class,
Commands\SpammerProcess\RebuildUserSpammerEstimator::class,
Commands\SpammerProcess\UserSpammerProcessor::class,
];
Expand Down
17 changes: 10 additions & 7 deletions app/Http/Controllers/Auth/RegisterController.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,18 @@ public function showRegistrationForm(LaravelRequest $request)
protected function validator(array $data)
{
$rules = [
'first_name' => 'required|string|max:100',
'last_name' => 'required|string|max:100',
'country_iso_code' => 'required|string|country_iso_alpha2_code',
'email' => 'required|string|email|max:255',
'password' => 'required|string|confirmed|password_policy',
'cf-turnstile-response' => ['required', new Turnstile()],
'first_name' => 'required|string|max:100',
'last_name' => 'required|string|max:100',
'country_iso_code' => 'required|string|country_iso_alpha2_code',
'email' => 'required|string|email|max:255',
'password' => 'required|string|confirmed|password_policy',
];

if(!empty(Config::get("app.code_of_conduct_link", null))){
if (!empty(Config::get("services.turnstile.secret", null))) {
$rules['cf-turnstile-response'] = ['required', new Turnstile()];
}

if (!empty(Config::get("app.code_of_conduct_link", null))) {
$rules['agree_code_of_conduct'] = 'required|string|in:true';
}

Expand Down
Loading
Loading