13 Commits

Author SHA1 Message Date
dd3c623bfc Merge pull request 'Seed OpenLDAP with bootstrap users including email addresses' (#8) from feature/ldap-email-bootstrap into master
All checks were successful
linter / quality (push) Successful in 1m34s
security / Dependency Audit (push) Successful in 1m24s
security / Static Analysis (push) Successful in 2m42s
tests / ci (8.4) (push) Successful in 1m23s
tests / ci (8.5) (push) Successful in 1m51s
Reviewed-on: #8
2026-03-06 10:48:31 +08:00
9f47e6e2c8 Seed OpenLDAP with bootstrap users including email addresses
All checks were successful
linter / quality (pull_request) Successful in 1m20s
security / Dependency Audit (pull_request) Successful in 2m22s
security / Static Analysis (pull_request) Successful in 1m42s
tests / ci (8.4) (pull_request) Successful in 1m25s
tests / ci (8.5) (pull_request) Successful in 1m24s
Add docker/openldap/bootstrap.ldif which creates an ou=people OU and
three test users (admin, approver, staff) each with a mail attribute,
matching the database seeder. Mount the LDIF into the openldap container
and add --copy-service so it is loaded on first boot.
2026-03-06 02:47:54 +00:00
fd53a28f03 Merge pull request 'Fix LDAP authentication failures' (#7) from worktree-ldap-issues into master
Some checks failed
linter / quality (push) Successful in 1m23s
security / Dependency Audit (push) Successful in 1m29s
security / Static Analysis (push) Successful in 1m42s
tests / ci (8.5) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
Reviewed-on: #7
2026-03-06 10:35:45 +08:00
bbcffe64b7 Fix LDAP authentication failures
All checks were successful
linter / quality (pull_request) Successful in 1m35s
security / Dependency Audit (pull_request) Successful in 1m19s
security / Static Analysis (pull_request) Successful in 1m32s
tests / ci (8.4) (pull_request) Successful in 2m36s
tests / ci (8.5) (pull_request) Successful in 1m37s
- Add missing LDAP env vars to .env.example (host, base DN, bind credentials)
- Use 'uid' instead of 'username' as the LDAP lookup attribute in Auth::attempt
- Override getLdapGuidColumn/getLdapDomainColumn in User model to match
  migration column names (ldap_guid/ldap_domain vs default guid/domain)
2026-03-06 02:34:46 +00:00
98abc637c8 Merge pull request 'Fix Bootstrap light/dark theming' (#6) from feature/bootstrap-theming into master
All checks were successful
linter / quality (push) Successful in 1m15s
security / Dependency Audit (push) Successful in 1m29s
security / Static Analysis (push) Successful in 1m25s
tests / ci (8.4) (push) Successful in 1m58s
tests / ci (8.5) (push) Successful in 1m28s
Reviewed-on: #6
2026-03-06 10:17:48 +08:00
236ba9558c Fix Bootstrap light/dark theming across all layouts
All checks were successful
linter / quality (pull_request) Successful in 1m52s
security / Dependency Audit (pull_request) Successful in 1m27s
security / Static Analysis (pull_request) Successful in 1m44s
tests / ci (8.4) (pull_request) Successful in 1m40s
tests / ci (8.5) (pull_request) Successful in 1m49s
- Make dark mode toggle reactive using Alpine x-data theme property
- Apply saved theme on page load via data-bs-theme attribute binding
- Remove hardcoded bg-light classes that broke dark mode styling
- Fix duplicate/broken Bootstrap bundle import in app.js
2026-03-06 02:16:45 +00:00
5f0b4218ae Merge pull request 'Add phpLDAPadmin for LDAP user management' (#5) from feature/phpldapadmin into master
All checks were successful
linter / quality (push) Successful in 1m20s
security / Dependency Audit (push) Successful in 1m30s
security / Static Analysis (push) Successful in 1m37s
tests / ci (8.4) (push) Successful in 1m27s
tests / ci (8.5) (push) Successful in 1m29s
Reviewed-on: #5
2026-03-06 09:43:41 +08:00
38b1dd0f4d Add phpLDAPadmin service for LDAP user management
All checks were successful
linter / quality (pull_request) Successful in 1m23s
security / Dependency Audit (pull_request) Successful in 1m20s
security / Static Analysis (pull_request) Successful in 1m26s
tests / ci (8.4) (pull_request) Successful in 1m37s
tests / ci (8.5) (pull_request) Successful in 1m30s
Accessible at localhost:8085 (configurable via FORWARD_PHPLDAPADMIN_PORT).
Login with cn=admin,dc=travel,dc=local and the LDAP admin password.
2026-03-06 01:42:03 +00:00
2b9b2fd32d Merge pull request 'Fix migration order for approval tables' (#4) from fix/migration-order into master
Some checks failed
linter / quality (push) Successful in 1m23s
security / Static Analysis (push) Has been cancelled
security / Dependency Audit (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
Reviewed-on: #4
2026-03-06 09:38:00 +08:00
95d953cf13 Merge pull request 'Consolidate Docker Compose into single compose.yaml' (#3) from fix/consolidate-docker-compose into master
All checks were successful
linter / quality (push) Successful in 1m32s
security / Dependency Audit (push) Successful in 1m22s
security / Static Analysis (push) Successful in 1m20s
tests / ci (8.4) (push) Successful in 1m31s
tests / ci (8.5) (push) Successful in 1m26s
Reviewed-on: #3
2026-03-06 09:13:12 +08:00
bbf8acef3c Consolidate Docker Compose into single compose.yaml
All checks were successful
linter / quality (pull_request) Successful in 1m31s
security / Dependency Audit (pull_request) Successful in 1m21s
security / Static Analysis (pull_request) Successful in 1m23s
tests / ci (8.4) (pull_request) Successful in 1m24s
tests / ci (8.5) (pull_request) Successful in 1m32s
Merge openldap service from docker-compose.yml into compose.yaml
and remove the duplicate file to fix the Docker Compose warning
about multiple config files.
2026-03-06 01:11:27 +00:00
d2f7812432 Merge pull request 'Add code security checks workflow' (#2) from feature/security-checks into master
All checks were successful
linter / quality (push) Successful in 1m30s
security / Dependency Audit (push) Successful in 1m26s
security / Static Analysis (push) Successful in 1m33s
tests / ci (8.4) (push) Successful in 1m30s
tests / ci (8.5) (push) Successful in 1m29s
Reviewed-on: #2
2026-03-05 13:54:25 +08:00
39fcb55904 Add security workflow with composer audit, npm audit, and PHPStan
All checks were successful
linter / quality (pull_request) Successful in 1m23s
security / Dependency Audit (pull_request) Successful in 1m21s
security / Static Analysis (pull_request) Successful in 1m32s
tests / ci (8.4) (pull_request) Successful in 1m42s
tests / ci (8.5) (pull_request) Successful in 1m31s
Adds a new GitHub Actions workflow that runs dependency vulnerability
checks for both PHP and Node packages, plus PHPStan static analysis
with Larastan. Includes a baseline for existing errors.
2026-03-05 05:51:21 +00:00
15 changed files with 463 additions and 146 deletions

View File

@@ -63,3 +63,10 @@ AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}" VITE_APP_NAME="${APP_NAME}"
LDAP_HOST=openldap
LDAP_USERNAME="cn=admin,dc=travel,dc=local"
LDAP_PASSWORD=adminpassword
LDAP_PORT=389
LDAP_BASE_DN="dc=travel,dc=local"
LDAP_LOGGING=true

67
.github/workflows/security.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: security
on:
push:
branches:
- develop
- main
- master
pull_request:
branches:
- develop
- main
- master
jobs:
audit:
name: Dependency Audit
runs-on: ubuntu-latest
environment: Testing
steps:
- uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Add Flux Credentials Loaded From ENV
run: composer config http-basic.composer.fluxui.dev "${{ secrets.FLUX_USERNAME }}" "${{ secrets.FLUX_LICENSE_KEY }}"
- name: Install PHP Dependencies
run: composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Install Node Dependencies
run: npm install
- name: Composer Audit
run: composer audit
- name: NPM Audit
run: npm audit --omit=dev
phpstan:
name: Static Analysis
runs-on: ubuntu-latest
environment: Testing
steps:
- uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
- name: Add Flux Credentials Loaded From ENV
run: composer config http-basic.composer.fluxui.dev "${{ secrets.FLUX_USERNAME }}" "${{ secrets.FLUX_LICENSE_KEY }}"
- name: Install Dependencies
run: composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Run PHPStan
run: vendor/bin/phpstan analyse --no-progress

View File

@@ -64,6 +64,16 @@ class User extends Authenticatable implements FilamentUser, LdapAuthenticatable
return $this->hasRole('administrator'); return $this->hasRole('administrator');
} }
public function getLdapGuidColumn(): string
{
return 'ldap_guid';
}
public function getLdapDomainColumn(): string
{
return 'ldap_domain';
}
public function emergencyContacts(): HasMany public function emergencyContacts(): HasMany
{ {
return $this->hasMany(EmergencyContact::class); return $this->hasMany(EmergencyContact::class);

View File

@@ -26,6 +26,7 @@ services:
- redis - redis
- selenium - selenium
- mailpit - mailpit
- openldap
mysql: mysql:
image: 'mysql:8.4' image: 'mysql:8.4'
ports: ports:
@@ -81,6 +82,52 @@ services:
- '${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025' - '${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025'
networks: networks:
- sail - sail
openldap:
image: 'osixia/openldap:1.5.0'
command: '--copy-service'
ports:
- '${FORWARD_LDAP_PORT:-389}:389'
- '${FORWARD_LDAPS_PORT:-636}:636'
environment:
LDAP_ORGANISATION: '${LDAP_ORGANISATION:-Travel App}'
LDAP_DOMAIN: '${LDAP_DOMAIN:-travel.local}'
LDAP_ADMIN_PASSWORD: '${LDAP_ADMIN_PASSWORD:-adminpassword}'
LDAP_CONFIG_PASSWORD: '${LDAP_CONFIG_PASSWORD:-configpassword}'
LDAP_READONLY_USER: 'true'
LDAP_READONLY_USER_USERNAME: '${LDAP_READONLY_USERNAME:-readonly}'
LDAP_READONLY_USER_PASSWORD: '${LDAP_READONLY_PASSWORD:-readonly}'
volumes:
- 'sail-ldap-data:/var/lib/ldap'
- 'sail-ldap-config:/etc/ldap/slapd.d'
- './docker/openldap/bootstrap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/bootstrap.ldif'
networks:
- sail
healthcheck:
test:
- CMD
- ldapsearch
- '-x'
- '-H'
- 'ldap://localhost'
- '-b'
- 'dc=travel,dc=local'
- '-D'
- 'cn=admin,dc=travel,dc=local'
- '-w'
- '${LDAP_ADMIN_PASSWORD:-adminpassword}'
retries: 3
timeout: 5s
phpldapadmin:
image: 'osixia/phpldapadmin:latest'
ports:
- '${FORWARD_PHPLDAPADMIN_PORT:-8085}:80'
environment:
PHPLDAPADMIN_LDAP_HOSTS: openldap
PHPLDAPADMIN_HTTPS: 'false'
networks:
- sail
depends_on:
- openldap
networks: networks:
sail: sail:
driver: bridge driver: bridge
@@ -89,3 +136,7 @@ volumes:
driver: local driver: local
sail-redis: sail-redis:
driver: local driver: local
sail-ldap-data:
driver: local
sail-ldap-config:
driver: local

View File

@@ -17,12 +17,14 @@
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.23", "fakerphp/faker": "^1.23",
"larastan/larastan": "^3.9",
"laravel/boost": "^2.0", "laravel/boost": "^2.0",
"laravel/pail": "^1.2.2", "laravel/pail": "^1.2.2",
"laravel/pint": "^1.24", "laravel/pint": "^1.24",
"laravel/sail": "^1.41", "laravel/sail": "^1.41",
"mockery/mockery": "^1.6", "mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6", "nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.3" "phpunit/phpunit": "^11.5.3"
}, },
"autoload": { "autoload": {

188
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "dee637c924fba6db2c11a4a299a491b6", "content-hash": "5a44c97e5f49f06d06ce0246f320edf2",
"packages": [ "packages": [
{ {
"name": "anourvalar/eloquent-serialize", "name": "anourvalar/eloquent-serialize",
@@ -8592,6 +8592,137 @@
}, },
"time": "2025-04-30T06:54:44+00:00" "time": "2025-04-30T06:54:44+00:00"
}, },
{
"name": "iamcal/sql-parser",
"version": "v0.7",
"source": {
"type": "git",
"url": "https://github.com/iamcal/SQLParser.git",
"reference": "610392f38de49a44dab08dc1659960a29874c4b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/iamcal/SQLParser/zipball/610392f38de49a44dab08dc1659960a29874c4b8",
"reference": "610392f38de49a44dab08dc1659960a29874c4b8",
"shasum": ""
},
"require-dev": {
"php-coveralls/php-coveralls": "^1.0",
"phpunit/phpunit": "^5|^6|^7|^8|^9"
},
"type": "library",
"autoload": {
"psr-4": {
"iamcal\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cal Henderson",
"email": "cal@iamcal.com"
}
],
"description": "MySQL schema parser",
"support": {
"issues": "https://github.com/iamcal/SQLParser/issues",
"source": "https://github.com/iamcal/SQLParser/tree/v0.7"
},
"time": "2026-01-28T22:20:33+00:00"
},
{
"name": "larastan/larastan",
"version": "v3.9.3",
"source": {
"type": "git",
"url": "https://github.com/larastan/larastan.git",
"reference": "64a52bcc5347c89fdf131cb59f96ebfbc8d1ad65"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/larastan/larastan/zipball/64a52bcc5347c89fdf131cb59f96ebfbc8d1ad65",
"reference": "64a52bcc5347c89fdf131cb59f96ebfbc8d1ad65",
"shasum": ""
},
"require": {
"ext-json": "*",
"iamcal/sql-parser": "^0.7.0",
"illuminate/console": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/container": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/contracts": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/database": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/http": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/pipeline": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/support": "^11.44.2 || ^12.4.1 || ^13",
"php": "^8.2",
"phpstan/phpstan": "^2.1.32"
},
"require-dev": {
"doctrine/coding-standard": "^13",
"laravel/framework": "^11.44.2 || ^12.7.2 || ^13",
"mockery/mockery": "^1.6.12",
"nikic/php-parser": "^5.4",
"orchestra/canvas": "^v9.2.2 || ^10.0.1 || ^11",
"orchestra/testbench-core": "^9.12.0 || ^10.1 || ^11",
"phpstan/phpstan-deprecation-rules": "^2.0.1",
"phpunit/phpunit": "^10.5.35 || ^11.5.15 || ^12.5.8"
},
"suggest": {
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench",
"phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically"
},
"type": "phpstan-extension",
"extra": {
"phpstan": {
"includes": [
"extension.neon"
]
},
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Larastan\\Larastan\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Can Vural",
"email": "can9119@gmail.com"
}
],
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel",
"keywords": [
"PHPStan",
"code analyse",
"code analysis",
"larastan",
"laravel",
"package",
"php",
"static analysis"
],
"support": {
"issues": "https://github.com/larastan/larastan/issues",
"source": "https://github.com/larastan/larastan/tree/v3.9.3"
},
"funding": [
{
"url": "https://github.com/canvural",
"type": "github"
}
],
"time": "2026-02-20T12:07:12+00:00"
},
{ {
"name": "laravel/boost", "name": "laravel/boost",
"version": "v2.2.2", "version": "v2.2.2",
@@ -9359,6 +9490,59 @@
}, },
"time": "2022-02-21T01:04:05+00:00" "time": "2022-02-21T01:04:05+00:00"
}, },
{
"name": "phpstan/phpstan",
"version": "2.1.40",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b",
"reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b",
"shasum": ""
},
"require": {
"php": "^7.4|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
}
],
"time": "2026-02-23T15:04:35+00:00"
},
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "11.0.12", "version": "11.0.12",
@@ -10989,5 +11173,5 @@
"php": "^8.2" "php": "^8.2"
}, },
"platform-dev": {}, "platform-dev": {},
"plugin-api-version": "2.9.0" "plugin-api-version": "2.6.0"
} }

View File

@@ -1,125 +0,0 @@
services:
laravel.test:
build:
context: ./vendor/laravel/sail/runtimes/8.4
dockerfile: Dockerfile
args:
WWWGROUP: '${WWWGROUP}'
image: sail-8.4/app
extra_hosts:
- 'host.docker.internal:host-gateway'
ports:
- '${APP_PORT:-80}:80'
- '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
environment:
WWWUSER: '${WWWUSER}'
LARAVEL_SAIL: 1
XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
IGNITION_LOCAL_SITES_PATH: '${PWD}'
volumes:
- '.:/var/www/html'
networks:
- sail
depends_on:
- mysql
- redis
- mailpit
- openldap
mysql:
image: 'mysql/mysql-server:8.0'
ports:
- '${FORWARD_DB_PORT:-3306}:3306'
environment:
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: '${DB_DATABASE}'
MYSQL_USER: '${DB_USERNAME}'
MYSQL_PASSWORD: '${DB_PASSWORD}'
MYSQL_ALLOW_EMPTY_PASSWORD: 1
volumes:
- 'sail-mysql:/var/lib/mysql'
- './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
networks:
- sail
healthcheck:
test:
- CMD
- mysqladmin
- ping
- '-p${DB_PASSWORD}'
retries: 3
timeout: 5s
redis:
image: 'redis:alpine'
ports:
- '${FORWARD_REDIS_PORT:-6379}:6379'
volumes:
- 'sail-redis:/data'
networks:
- sail
healthcheck:
test:
- CMD
- redis-cli
- ping
retries: 3
timeout: 5s
mailpit:
image: 'axllent/mailpit:latest'
ports:
- '${FORWARD_MAILPIT_PORT:-1025}:1025'
- '${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025'
networks:
- sail
openldap:
image: 'osixia/openldap:1.5.0'
ports:
- '${FORWARD_LDAP_PORT:-389}:389'
- '${FORWARD_LDAPS_PORT:-636}:636'
environment:
LDAP_ORGANISATION: '${LDAP_ORGANISATION:-Travel App}'
LDAP_DOMAIN: '${LDAP_DOMAIN:-travel.local}'
LDAP_ADMIN_PASSWORD: '${LDAP_ADMIN_PASSWORD:-adminpassword}'
LDAP_CONFIG_PASSWORD: '${LDAP_CONFIG_PASSWORD:-configpassword}'
LDAP_READONLY_USER: 'true'
LDAP_READONLY_USER_USERNAME: '${LDAP_READONLY_USERNAME:-readonly}'
LDAP_READONLY_USER_PASSWORD: '${LDAP_READONLY_PASSWORD:-readonly}'
volumes:
- 'sail-ldap-data:/var/lib/ldap'
- 'sail-ldap-config:/etc/ldap/slapd.d'
networks:
- sail
healthcheck:
test:
- CMD
- ldapsearch
- '-x'
- '-H'
- 'ldap://localhost'
- '-b'
- 'dc=travel,dc=local'
- '-D'
- 'cn=admin,dc=travel,dc=local'
- '-w'
- '${LDAP_ADMIN_PASSWORD:-adminpassword}'
retries: 3
timeout: 5s
networks:
sail:
driver: bridge
volumes:
sail-mysql:
driver: local
sail-redis:
driver: local
sail-ldap-data:
driver: local
sail-ldap-config:
driver: local

View File

@@ -0,0 +1,46 @@
# People OU
dn: ou=people,dc=travel,dc=local
objectClass: organizationalUnit
ou: people
# Administrator
dn: uid=admin,ou=people,dc=travel,dc=local
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
cn: Administrator
sn: Administrator
uid: admin
mail: admin@travel.local
uidNumber: 1000
gidNumber: 1000
homeDirectory: /home/admin
userPassword: password
# Travel Approver
dn: uid=approver,ou=people,dc=travel,dc=local
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
cn: Travel Approver
sn: Approver
uid: approver
mail: approver@travel.local
uidNumber: 1001
gidNumber: 1000
homeDirectory: /home/approver
userPassword: password
# Staff Member
dn: uid=staff,ou=people,dc=travel,dc=local
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
cn: Staff Member
sn: Member
uid: staff
mail: staff@travel.local
uidNumber: 1002
gidNumber: 1000
homeDirectory: /home/staff
userPassword: password

67
phpstan-baseline.neon Normal file
View File

@@ -0,0 +1,67 @@
parameters:
ignoreErrors:
-
message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$email\.$#'
identifier: property.notFound
count: 1
path: app/Jobs/SendApprovalDecisionEmail.php
-
message: '#^Cannot access property \$value on string\.$#'
identifier: property.nonObject
count: 1
path: app/Mail/ApprovalDecisionMail.php
-
message: '#^Result of && is always false\.$#'
identifier: booleanAnd.alwaysFalse
count: 2
path: app/Policies/TravelRequestPolicy.php
-
message: '#^Strict comparison using \=\=\= between string and App\\Enums\\TravelStatus\:\:Draft will always evaluate to false\.$#'
identifier: identical.alwaysFalse
count: 1
path: app/Policies/TravelRequestPolicy.php
-
message: '#^Strict comparison using \=\=\= between string and App\\Enums\\TravelStatus\:\:Pending will always evaluate to false\.$#'
identifier: identical.alwaysFalse
count: 1
path: app/Policies/TravelRequestPolicy.php
-
message: '#^Parameter \#1 \$action of method Filament\\Panel\:\:login\(\) expects array\<class\-string, string\>\|Closure\|string\|null, false given\.$#'
identifier: argument.type
count: 1
path: app/Providers/Filament/AdminPanelProvider.php
-
message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#'
identifier: property.notFound
count: 1
path: app/Services/ApprovalService.php
-
message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$role\.$#'
identifier: property.notFound
count: 1
path: app/Services/ApprovalService.php
-
message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:steps\(\)\.$#'
identifier: method.notFound
count: 1
path: app/Services/ApprovalService.php
-
message: '#^Parameter \#1 \$travelRequest of job class App\\Jobs\\SendApprovalDecisionEmail constructor expects App\\Models\\TravelRequest in App\\Jobs\\SendApprovalDecisionEmail\:\:dispatch\(\), Illuminate\\Database\\Eloquent\\Model\|null given\.$#'
identifier: argument.type
count: 2
path: app/Services/ApprovalService.php
-
message: '#^Parameter \#1 \$travelRequest of job class App\\Jobs\\SendApprovalRequestEmail constructor expects App\\Models\\TravelRequest in App\\Jobs\\SendApprovalRequestEmail\:\:dispatch\(\), Illuminate\\Database\\Eloquent\\Model\|null given\.$#'
identifier: argument.type
count: 1
path: app/Services/ApprovalService.php

9
phpstan.neon Normal file
View File

@@ -0,0 +1,9 @@
includes:
- vendor/larastan/larastan/extension.neon
- phpstan-baseline.neon
parameters:
paths:
- app/
level: 5

View File

@@ -1,4 +1,3 @@
import * as bootstrap from 'bootstrap'; import * as bootstrap from 'bootstrap';
import './../../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js';
window.bootstrap = bootstrap; window.bootstrap = bootstrap;

View File

@@ -1,8 +1,9 @@
<!doctype html> <!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" x-data x-init=" <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"
const saved = localStorage.getItem('theme'); x-data="{ theme: localStorage.getItem('theme') || 'light' }"
if (saved) { document.documentElement.setAttribute('data-bs-theme', saved); } x-init="$watch('theme', val => { document.documentElement.setAttribute('data-bs-theme', val); localStorage.setItem('theme', val); }); document.documentElement.setAttribute('data-bs-theme', theme);"
"> :data-bs-theme="theme"
>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -43,16 +44,11 @@
<li class="nav-item me-2"> <li class="nav-item me-2">
<button <button
class="btn btn-sm btn-outline-light" class="btn btn-sm btn-outline-light"
x-on:click=" x-on:click="theme = theme === 'dark' ? 'light' : 'dark'"
const current = document.documentElement.getAttribute('data-bs-theme');
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-bs-theme', next);
localStorage.setItem('theme', next);
"
title="Toggle dark/light mode" title="Toggle dark/light mode"
> >
<span x-show="document.documentElement.getAttribute('data-bs-theme') !== 'dark'">🌙</span> <span x-show="theme !== 'dark'">🌙</span>
<span x-show="document.documentElement.getAttribute('data-bs-theme') === 'dark'">☀️</span> <span x-show="theme === 'dark'">☀️</span>
</button> </button>
</li> </li>
@auth @auth

View File

@@ -1,5 +1,9 @@
<!doctype html> <!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"
x-data="{ theme: localStorage.getItem('theme') || 'light' }"
x-init="document.documentElement.setAttribute('data-bs-theme', theme);"
:data-bs-theme="theme"
>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -8,7 +12,7 @@
@vite(['resources/css/app.css', 'resources/js/app.js']) @vite(['resources/css/app.css', 'resources/js/app.js'])
@livewireStyles @livewireStyles
</head> </head>
<body class="bg-light"> <body>
{{ $slot }} {{ $slot }}
@livewireScripts @livewireScripts
</body> </body>

View File

@@ -18,7 +18,7 @@ new #[Layout('components.layouts.guest')] class extends Component {
{ {
$this->validate(); $this->validate();
if (Auth::attempt(['username' => $this->username, 'password' => $this->password], $this->rememberMe)) { if (Auth::attempt(['uid' => $this->username, 'password' => $this->password], $this->rememberMe)) {
session()->regenerate(); session()->regenerate();
$this->redirectIntended(route('dashboard'), navigate: true); $this->redirectIntended(route('dashboard'), navigate: true);
return; return;
@@ -29,7 +29,7 @@ new #[Layout('components.layouts.guest')] class extends Component {
} }
?> ?>
<div class="min-vh-100 d-flex align-items-center justify-content-center bg-light"> <div class="min-vh-100 d-flex align-items-center justify-content-center">
<div class="card shadow-sm" style="width: 100%; max-width: 420px;"> <div class="card shadow-sm" style="width: 100%; max-width: 420px;">
<div class="card-body p-4"> <div class="card-body p-4">
<div class="text-center mb-4"> <div class="text-center mb-4">

View File

@@ -318,7 +318,7 @@ new #[Layout('components.layouts.app')] class extends Component {
@error('journeys') <div class="alert alert-danger">{{ $message }}</div> @enderror @error('journeys') <div class="alert alert-danger">{{ $message }}</div> @enderror
@foreach ($journeys as $i => $journey) @foreach ($journeys as $i => $journey)
<div class="border rounded p-3 mb-3 bg-light" wire:key="journey-{{ $i }}"> <div class="border rounded p-3 mb-3" wire:key="journey-{{ $i }}">
<div class="d-flex justify-content-between align-items-center mb-2"> <div class="d-flex justify-content-between align-items-center mb-2">
<strong class="small">Journey {{ $i + 1 }}</strong> <strong class="small">Journey {{ $i + 1 }}</strong>
@if (count($journeys) > 1) @if (count($journeys) > 1)
@@ -413,7 +413,7 @@ new #[Layout('components.layouts.app')] class extends Component {
</div> </div>
<div class="card-body"> <div class="card-body">
@foreach ($costCodes as $i => $code) @foreach ($costCodes as $i => $code)
<div class="border rounded p-3 mb-3 bg-light" wire:key="code-{{ $i }}"> <div class="border rounded p-3 mb-3" wire:key="code-{{ $i }}">
<div class="d-flex justify-content-between align-items-center mb-2"> <div class="d-flex justify-content-between align-items-center mb-2">
<strong class="small">Cost Code {{ $i + 1 }}</strong> <strong class="small">Cost Code {{ $i + 1 }}</strong>
@if (count($costCodes) > 1) @if (count($costCodes) > 1)