mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-05-04 18:08:46 +03:00
Compare commits
2 Commits
developmen
...
MilnerMart
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
844c79ae72 | ||
|
|
f3c1fad50a |
@@ -1,2 +0,0 @@
|
|||||||
Please find our community rules on our website here:
|
|
||||||
https://www.bookstackapp.com/about/community-rules/
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: [ssddanbrown]
|
|
||||||
ko_fi: ssddanbrown
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Community Forum Support
|
|
||||||
url: https://community.bookstackapp.com
|
|
||||||
about: Get support by talking with the BookStack team & community.
|
|
||||||
|
|
||||||
- name: Debugging & Common Issues
|
|
||||||
url: https://www.bookstackapp.com/docs/admin/debugging/
|
|
||||||
about: Find details on how to debug issues and view common issues with their resolutions.
|
|
||||||
|
|
||||||
- name: Official Support Plans
|
|
||||||
url: https://www.bookstackapp.com/support/
|
|
||||||
about: View our official support plans that offer assured support for business.
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
## Details
|
|
||||||
|
|
||||||
<!-- Write details of your pull request in here -->
|
|
||||||
<!-- Include references to any relevant issues/discussions -->
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!-- Put an 'x' in between the brackets below to confirm these elements -->
|
|
||||||
|
|
||||||
- [ ] I have read the [BookStack community rules](https://www.bookstackapp.com/about/community-rules/).
|
|
||||||
- [ ] This PR does not feature significant use of LLM/AI generation as per the community rules above.
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
name: Crowdin Action
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ development ]
|
|
||||||
paths:
|
|
||||||
- 'lang/**.php'
|
|
||||||
schedule:
|
|
||||||
- cron: '30 4 * * *'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
synchronize-with-crowdin:
|
|
||||||
runs-on: docker
|
|
||||||
container:
|
|
||||||
image: docker.io/library/node:24-trixie
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: https://code.forgejo.org/actions/checkout@v6
|
|
||||||
|
|
||||||
- name: crowdin action
|
|
||||||
uses: https://github.com/crowdin/github-action@v2
|
|
||||||
with:
|
|
||||||
upload_sources: true
|
|
||||||
upload_translations: false
|
|
||||||
download_translations: true
|
|
||||||
localization_branch_name: l10n_development
|
|
||||||
create_pull_request: false
|
|
||||||
github_base_url: codeberg.org
|
|
||||||
env:
|
|
||||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
|
||||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
|
||||||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,8 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Open Issues Here Instead
|
- name: Discord Chat Support
|
||||||
url: https://codeberg.org/bookstack/bookstack/issues
|
url: https://discord.gg/ztkBqR2
|
||||||
about: This project has migrated to Codeberg, please open issues there instead.
|
about: Realtime support & chat with the BookStack community and the team.
|
||||||
|
|
||||||
- name: Debugging & Common Issues
|
- name: Debugging & Common Issues
|
||||||
url: https://www.bookstackapp.com/docs/admin/debugging/
|
url: https://www.bookstackapp.com/docs/admin/debugging/
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Have you searched for an existing open/closed issue?
|
label: Have you searched for an existing open/closed issue?
|
||||||
description: |
|
description: |
|
||||||
To help us keep these issues under control, please ensure you have first [searched our issue list](https://codeberg.org/bookstack/bookstack/issues) for any existing issues that cover the fundamental benefit/goal of your request.
|
To help us keep these issues under control, please ensure you have first [searched our issue list](https://github.com/BookStackApp/BookStack/issues?q=is%3Aissue) for any existing issues that cover the fundamental benefit/goal of your request.
|
||||||
options:
|
options:
|
||||||
- label: I have searched for existing issues and none cover my fundamental request
|
- label: I have searched for existing issues and none cover my fundamental request
|
||||||
required: true
|
required: true
|
||||||
@@ -15,11 +15,11 @@ body:
|
|||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: searchissue
|
id: searchissue
|
||||||
attributes:
|
attributes:
|
||||||
label: Searched Existing Issues
|
label: Searched GitHub Issues
|
||||||
description: |
|
description: |
|
||||||
I have searched for the issue and potential resolutions within the [project's issue list](https://codeberg.org/bookstack/bookstack/issues)
|
I have searched for the issue and potential resolutions within the [project's GitHub issue list](https://github.com/BookStackApp/BookStack/issues)
|
||||||
options:
|
options:
|
||||||
- label: I have searched for the issue.
|
- label: I have searched GitHub for the issue.
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: scenario
|
id: scenario
|
||||||
12
.forgejo/SECURITY.md → .github/SECURITY.md
vendored
12
.forgejo/SECURITY.md → .github/SECURITY.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
Only the [latest version](https://codeberg.org/bookstack/bookstack/releases) of BookStack is supported.
|
Only the [latest version](https://github.com/BookStackApp/BookStack/releases) of BookStack is supported.
|
||||||
We generally don't support older versions of BookStack due to maintenance effort and
|
We generally don't support older versions of BookStack due to maintenance effort and
|
||||||
since we aim to provide a fairly stable upgrade path for new versions.
|
since we aim to provide a fairly stable upgrade path for new versions.
|
||||||
|
|
||||||
@@ -12,14 +12,16 @@ If you'd like to be notified of new potential security concerns you can [sign-up
|
|||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
If you've found an issue that likely has no impact to existing users (For example, an issue only in the development branch)
|
If you've found an issue that likely has no impact to existing users (For example, in a development-only branch)
|
||||||
feel free to raise it via a standard Codeberg bug report issue.
|
feel free to raise it via a standard GitHub bug report issue.
|
||||||
|
|
||||||
If the issue could have a security impact to BookStack instances,
|
If the issue could have a security impact to BookStack instances,
|
||||||
please directly contact the lead maintainer via email Dan Brown using the [details found here](https://www.bookstackapp.com/links/contact/).
|
please directly contact the lead maintainer [@ssddanbrown](https://github.com/ssddanbrown).
|
||||||
|
You will need to log in to be able to see the email address on the [GitHub profile page](https://github.com/ssddanbrown).
|
||||||
|
Alternatively you can send a DM via Mastodon to [@danb@fosstodon.org](https://fosstodon.org/@danb).
|
||||||
|
|
||||||
Please be patient while the vulnerability is being reviewed. Deploying the fix to address the vulnerability
|
Please be patient while the vulnerability is being reviewed. Deploying the fix to address the vulnerability
|
||||||
can often take a little time due to the amount of preparation required, to ensure the vulnerability has
|
can often take a little time due to the amount of preparation required, to ensure the vulnerability has
|
||||||
been covered, and to create the content required to adequately notify the user-base.
|
been covered, and to create the content required to adequately notify the user-base.
|
||||||
|
|
||||||
Thank you for keeping BookStack instances safe!
|
Thank you for keeping BookStack instances safe!
|
||||||
13
.github/pull_request_template.md
vendored
13
.github/pull_request_template.md
vendored
@@ -1,10 +1,11 @@
|
|||||||
**Warning:**
|
## Details
|
||||||
|
|
||||||
This project has migrated to Codeberg:
|
<!-- Write details of your pull request in here -->
|
||||||
https://codeberg.org/bookstack/bookstack
|
<!-- Include references to any relevant issues/discussions -->
|
||||||
|
|
||||||
Please open pull requests here instead.
|
## Checklist
|
||||||
|
|
||||||
ANY PULL REQUESTS OPENED HERE WILL BE CLOSED WITHOUT COMMENT OR MERGE.
|
<!-- Put an 'x' in between the brackets below to confirm these elements -->
|
||||||
|
|
||||||
---
|
- [ ] I have read the [BookStack community rules](https://www.bookstackapp.com/about/community-rules/).
|
||||||
|
- [ ] This PR does not feature significant use of LLM/AI generation as per the community rules above.
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
name: analyse-php
|
name: analyse-php
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- '**.php'
|
- '**.php'
|
||||||
@@ -12,16 +11,14 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
||||||
runs-on: docker
|
runs-on: ubuntu-24.04
|
||||||
container:
|
|
||||||
image: docker.io/library/node:24-trixie
|
|
||||||
steps:
|
steps:
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: https://github.com/shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.5
|
php-version: 8.3
|
||||||
extensions: gd, mbstring, json, curl, xml, mysql, ldap
|
extensions: gd, mbstring, json, curl, xml, mysql, ldap
|
||||||
|
|
||||||
- name: Get Composer Cache Directory
|
- name: Get Composer Cache Directory
|
||||||
@@ -30,16 +27,14 @@ jobs:
|
|||||||
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Cache composer packages
|
- name: Cache composer packages
|
||||||
uses: https://code.forgejo.org/actions/cache@v5
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-composer-8.5
|
key: ${{ runner.os }}-composer-8.3
|
||||||
restore-keys: ${{ runner.os }}-composer-
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
|
|
||||||
- name: Install composer dependencies
|
- name: Install composer dependencies
|
||||||
run: composer install --prefer-dist --no-interaction --ansi
|
run: composer install --prefer-dist --no-interaction --ansi
|
||||||
env:
|
|
||||||
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.GH_TOKEN }}"}}'
|
|
||||||
|
|
||||||
- name: Run static analysis check
|
- name: Run static analysis check
|
||||||
run: composer check-static
|
run: composer check-static
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
name: lint-js
|
name: lint-js
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- '**.js'
|
- '**.js'
|
||||||
@@ -14,11 +13,9 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
||||||
runs-on: docker
|
runs-on: ubuntu-24.04
|
||||||
container:
|
|
||||||
image: docker.io/library/node:24-trixie
|
|
||||||
steps:
|
steps:
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install NPM deps
|
- name: Install NPM deps
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
name: lint-php
|
name: lint-php
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- '**.php'
|
- '**.php'
|
||||||
@@ -12,16 +11,14 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
||||||
runs-on: docker
|
runs-on: ubuntu-24.04
|
||||||
container:
|
|
||||||
image: docker.io/library/node:24-trixie
|
|
||||||
steps:
|
steps:
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: https://github.com/shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.5
|
php-version: 8.3
|
||||||
tools: phpcs
|
tools: phpcs
|
||||||
|
|
||||||
- name: Run formatting check
|
- name: Run formatting check
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
name: test-js
|
name: test-js
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- '**.js'
|
- '**.js'
|
||||||
@@ -16,11 +15,9 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
||||||
runs-on: docker
|
runs-on: ubuntu-24.04
|
||||||
container:
|
|
||||||
image: docker.io/library/node:24-trixie
|
|
||||||
steps:
|
steps:
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install NPM deps
|
- name: Install NPM deps
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
name: test-migrations
|
name: test-migrations
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- '**.php'
|
- '**.php'
|
||||||
@@ -14,25 +13,15 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
||||||
runs-on: docker
|
runs-on: ubuntu-24.04
|
||||||
container:
|
|
||||||
image: docker.io/library/node:24-trixie
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php: ['8.2', '8.3', '8.4', '8.5']
|
php: ['8.2', '8.3', '8.4', '8.5']
|
||||||
services:
|
|
||||||
mysql:
|
|
||||||
image: docker.io/library/mariadb:12.2.2-noble
|
|
||||||
env:
|
|
||||||
MARIADB_USER: bookstack-test
|
|
||||||
MARIADB_PASSWORD: bookstack-test
|
|
||||||
MARIADB_DATABASE: bookstack-test
|
|
||||||
MARIADB_ROOT_PASSWORD: password
|
|
||||||
steps:
|
steps:
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: https://github.com/shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
extensions: gd, mbstring, json, curl, xml, mysql, ldap
|
extensions: gd, mbstring, json, curl, xml, mysql, ldap
|
||||||
@@ -43,31 +32,34 @@ jobs:
|
|||||||
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Cache composer packages
|
- name: Cache composer packages
|
||||||
uses: https://code.forgejo.org/actions/cache@v5
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-composer-${{ matrix.php }}
|
key: ${{ runner.os }}-composer-${{ matrix.php }}
|
||||||
restore-keys: ${{ runner.os }}-composer-
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
|
|
||||||
|
- name: Start MySQL
|
||||||
|
run: |
|
||||||
|
sudo systemctl start mysql
|
||||||
|
|
||||||
|
- name: Create database & user
|
||||||
|
run: |
|
||||||
|
mysql -uroot -proot -e 'CREATE DATABASE IF NOT EXISTS `bookstack-test`;'
|
||||||
|
mysql -uroot -proot -e "CREATE USER 'bookstack-test'@'localhost' IDENTIFIED WITH mysql_native_password BY 'bookstack-test';"
|
||||||
|
mysql -uroot -proot -e "GRANT ALL ON \`bookstack-test\`.* TO 'bookstack-test'@'localhost';"
|
||||||
|
mysql -uroot -proot -e 'FLUSH PRIVILEGES;'
|
||||||
|
|
||||||
- name: Install composer dependencies
|
- name: Install composer dependencies
|
||||||
run: composer install --prefer-dist --no-interaction --ansi
|
run: composer install --prefer-dist --no-interaction --ansi
|
||||||
env:
|
|
||||||
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.GH_TOKEN }}"}}'
|
|
||||||
|
|
||||||
- name: Start migration test
|
- name: Start migration test
|
||||||
env:
|
|
||||||
TEST_DATABASE_URL: 'mysql://bookstack-test:bookstack-test@mysql/bookstack-test'
|
|
||||||
run: |
|
run: |
|
||||||
php${{ matrix.php }} artisan migrate --force -n --database=mysql_testing
|
php${{ matrix.php }} artisan migrate --force -n --database=mysql_testing
|
||||||
|
|
||||||
- name: Start migration:rollback test
|
- name: Start migration:rollback test
|
||||||
env:
|
|
||||||
TEST_DATABASE_URL: 'mysql://bookstack-test:bookstack-test@mysql/bookstack-test'
|
|
||||||
run: |
|
run: |
|
||||||
php${{ matrix.php }} artisan migrate:rollback --force -n --database=mysql_testing
|
php${{ matrix.php }} artisan migrate:rollback --force -n --database=mysql_testing
|
||||||
|
|
||||||
- name: Start migration rerun test
|
- name: Start migration rerun test
|
||||||
env:
|
|
||||||
TEST_DATABASE_URL: 'mysql://bookstack-test:bookstack-test@mysql/bookstack-test'
|
|
||||||
run: |
|
run: |
|
||||||
php${{ matrix.php }} artisan migrate --force -n --database=mysql_testing
|
php${{ matrix.php }} artisan migrate --force -n --database=mysql_testing
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
name: test-php
|
name: test-php
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- '**.php'
|
- '**.php'
|
||||||
@@ -14,25 +13,15 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
if: ${{ github.ref != 'refs/heads/l10n_development' }}
|
||||||
runs-on: docker
|
runs-on: ubuntu-24.04
|
||||||
container:
|
|
||||||
image: docker.io/library/node:24-trixie
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php: ['8.2', '8.3', '8.4', '8.5']
|
php: ['8.2', '8.3', '8.4', '8.5']
|
||||||
services:
|
|
||||||
mysql:
|
|
||||||
image: docker.io/library/mariadb:12.2.2-noble
|
|
||||||
env:
|
|
||||||
MARIADB_USER: bookstack-test
|
|
||||||
MARIADB_PASSWORD: bookstack-test
|
|
||||||
MARIADB_DATABASE: bookstack-test
|
|
||||||
MARIADB_ROOT_PASSWORD: password
|
|
||||||
steps:
|
steps:
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: https://github.com/shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
extensions: gd, mbstring, json, curl, xml, mysql, ldap, gmp
|
extensions: gd, mbstring, json, curl, xml, mysql, ldap, gmp
|
||||||
@@ -43,25 +32,30 @@ jobs:
|
|||||||
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Cache composer packages
|
- name: Cache composer packages
|
||||||
uses: https://code.forgejo.org/actions/cache@v5
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-composer-${{ matrix.php }}
|
key: ${{ runner.os }}-composer-${{ matrix.php }}
|
||||||
restore-keys: ${{ runner.os }}-composer-
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
|
|
||||||
|
- name: Start Database
|
||||||
|
run: |
|
||||||
|
sudo systemctl start mysql
|
||||||
|
|
||||||
|
- name: Setup Database
|
||||||
|
run: |
|
||||||
|
mysql -uroot -proot -e 'CREATE DATABASE IF NOT EXISTS `bookstack-test`;'
|
||||||
|
mysql -uroot -proot -e "CREATE USER 'bookstack-test'@'localhost' IDENTIFIED WITH mysql_native_password BY 'bookstack-test';"
|
||||||
|
mysql -uroot -proot -e "GRANT ALL ON \`bookstack-test\`.* TO 'bookstack-test'@'localhost';"
|
||||||
|
mysql -uroot -proot -e 'FLUSH PRIVILEGES;'
|
||||||
|
|
||||||
- name: Install composer dependencies
|
- name: Install composer dependencies
|
||||||
run: composer install --prefer-dist --no-interaction --ansi
|
run: composer install --prefer-dist --no-interaction --ansi
|
||||||
env:
|
|
||||||
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.GH_TOKEN }}"}}'
|
|
||||||
|
|
||||||
- name: Migrate and seed the database
|
- name: Migrate and seed the database
|
||||||
env:
|
|
||||||
TEST_DATABASE_URL: 'mysql://bookstack-test:bookstack-test@mysql/bookstack-test'
|
|
||||||
run: |
|
run: |
|
||||||
php${{ matrix.php }} artisan migrate --force -n --database=mysql_testing
|
php${{ matrix.php }} artisan migrate --force -n --database=mysql_testing
|
||||||
php${{ matrix.php }} artisan db:seed --force -n --class=DummyContentSeeder --database=mysql_testing
|
php${{ matrix.php }} artisan db:seed --force -n --class=DummyContentSeeder --database=mysql_testing
|
||||||
|
|
||||||
- name: Run PHP tests
|
- name: Run PHP tests
|
||||||
env:
|
|
||||||
TEST_DATABASE_URL: 'mysql://bookstack-test:bookstack-test@mysql/bookstack-test'
|
|
||||||
run: php${{ matrix.php }} ./vendor/bin/phpunit
|
run: php${{ matrix.php }} ./vendor/bin/phpunit
|
||||||
@@ -55,6 +55,7 @@ class OidcController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$this->throwIfAuthorizationError($request);
|
||||||
$this->oidcService->processAuthorizeResponse($request->query('code'));
|
$this->oidcService->processAuthorizeResponse($request->query('code'));
|
||||||
} catch (OidcException $oidcException) {
|
} catch (OidcException $oidcException) {
|
||||||
$this->showErrorNotification($oidcException->getMessage());
|
$this->showErrorNotification($oidcException->getMessage());
|
||||||
@@ -72,4 +73,23 @@ class OidcController extends Controller
|
|||||||
{
|
{
|
||||||
return redirect($this->oidcService->logout());
|
return redirect($this->oidcService->logout());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @throws OidcException
|
||||||
|
*/
|
||||||
|
private function throwIfAuthorizationError(Request $request): void
|
||||||
|
{
|
||||||
|
$errorCode = $request->query('error');
|
||||||
|
if (!$errorCode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$errorDescription = $request->query('error_description');
|
||||||
|
if ($errorDescription) {
|
||||||
|
throw new OidcException($errorDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new OidcException(trans('errors.oidc_fail_authed', ['system' => config('oidc.name')]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
app/Access/Oidc/OidcHttpBasicWithClientIdOptionProvider.php
Normal file
27
app/Access/Oidc/OidcHttpBasicWithClientIdOptionProvider.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Access\Oidc;
|
||||||
|
|
||||||
|
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option provider that sends credentials via HTTP Basic Auth header
|
||||||
|
* and also includes client_id in the request body.
|
||||||
|
*/
|
||||||
|
class OidcHttpBasicWithClientIdOptionProvider extends HttpBasicAuthOptionProvider
|
||||||
|
{
|
||||||
|
public function getAccessTokenOptions($method, array $params)
|
||||||
|
{
|
||||||
|
$clientId = $params['client_id'] ?? null;
|
||||||
|
|
||||||
|
$options = parent::getAccessTokenOptions($method, $params);
|
||||||
|
|
||||||
|
if ($clientId) {
|
||||||
|
parse_str($options['body'] ?? '', $body);
|
||||||
|
$body['client_id'] = $clientId;
|
||||||
|
$options['body'] = http_build_query($body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,10 +9,10 @@ class OidcIdToken extends OidcJwtWithClaims implements ProvidesClaims
|
|||||||
*
|
*
|
||||||
* @throws OidcInvalidTokenException
|
* @throws OidcInvalidTokenException
|
||||||
*/
|
*/
|
||||||
public function validate(string $clientId): bool
|
public function validate(OidcProviderSettings $settings): bool
|
||||||
{
|
{
|
||||||
parent::validateCommonTokenDetails($clientId);
|
parent::validateCommonTokenDetails($settings);
|
||||||
$this->validateTokenClaims($clientId);
|
$this->validateTokenClaims($settings->clientId);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ class OidcJwtWithClaims implements ProvidesClaims
|
|||||||
protected string $signature;
|
protected string $signature;
|
||||||
protected string $issuer;
|
protected string $issuer;
|
||||||
protected array $tokenParts = [];
|
protected array $tokenParts = [];
|
||||||
|
protected array $acceptedSignatures = [self::hs256Signature, self::rs256Signature];
|
||||||
|
private const hs256Signature = 'HS256'
|
||||||
|
, rs256Signature = 'RS256';
|
||||||
/**
|
/**
|
||||||
* @var array[]|string[]
|
* @var array[]|string[]
|
||||||
*/
|
*/
|
||||||
@@ -59,11 +61,11 @@ class OidcJwtWithClaims implements ProvidesClaims
|
|||||||
*
|
*
|
||||||
* @throws OidcInvalidTokenException
|
* @throws OidcInvalidTokenException
|
||||||
*/
|
*/
|
||||||
public function validateCommonTokenDetails(string $clientId): bool
|
public function validateCommonTokenDetails(OidcProviderSettings $settings): bool
|
||||||
{
|
{
|
||||||
$this->validateTokenStructure();
|
$this->validateTokenStructure();
|
||||||
$this->validateTokenSignature();
|
$this->validateTokenSignature($settings);
|
||||||
$this->validateCommonClaims($clientId);
|
$this->validateCommonClaims($settings->clientId);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -102,12 +104,12 @@ class OidcJwtWithClaims implements ProvidesClaims
|
|||||||
protected function validateTokenStructure(): void
|
protected function validateTokenStructure(): void
|
||||||
{
|
{
|
||||||
foreach (['header', 'payload'] as $prop) {
|
foreach (['header', 'payload'] as $prop) {
|
||||||
if (empty($this->$prop)) {
|
if (empty($this->$prop) || !is_array($this->$prop)) {
|
||||||
throw new OidcInvalidTokenException("Could not parse out a valid {$prop} within the provided token");
|
throw new OidcInvalidTokenException("Could not parse out a valid {$prop} within the provided token");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($this->signature)) {
|
if (empty($this->signature) || !is_string($this->signature)) {
|
||||||
throw new OidcInvalidTokenException('Could not parse out a valid signature within the provided token');
|
throw new OidcInvalidTokenException('Could not parse out a valid signature within the provided token');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,31 +119,42 @@ class OidcJwtWithClaims implements ProvidesClaims
|
|||||||
*
|
*
|
||||||
* @throws OidcInvalidTokenException
|
* @throws OidcInvalidTokenException
|
||||||
*/
|
*/
|
||||||
protected function validateTokenSignature(): void
|
protected function validateTokenSignature(OidcProviderSettings $settings): void {
|
||||||
{
|
$validSignatures = implode(', ',$this->acceptedSignatures);
|
||||||
if ($this->header['alg'] !== 'RS256') {
|
switch ($this->header['alg']) {
|
||||||
throw new OidcInvalidTokenException("Only RS256 signature validation is supported. Token reports using {$this->header['alg']}");
|
case self::rs256Signature:
|
||||||
|
$parsedKeys = array_map(function ($key) {
|
||||||
|
try {
|
||||||
|
return new OidcJwtSigningKey($key);
|
||||||
|
} catch (OidcInvalidKeyException $e) {
|
||||||
|
throw new OidcInvalidTokenException('Failed to read signing key with error: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}, $this->keys);
|
||||||
|
|
||||||
|
$parsedKeys = array_filter($parsedKeys);
|
||||||
|
|
||||||
|
$contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1];
|
||||||
|
/** @var OidcJwtSigningKey $parsedKey */
|
||||||
|
foreach ($parsedKeys as $parsedKey) {
|
||||||
|
if ($parsedKey->verify($contentToSign, $this->signature)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new OidcInvalidTokenException('Token signature could not be validated using the provided keys');
|
||||||
|
case self::hs256Signature:
|
||||||
|
$secret = $settings->clientSecret;
|
||||||
|
$contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1];
|
||||||
|
$expectedSignature = hash_hmac('sha256', $contentToSign, $secret, true);
|
||||||
|
|
||||||
|
if (hash_equals($expectedSignature, $this->signature)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new OidcInvalidTokenException('Token signature could not be validated using the provided secret');
|
||||||
|
default:
|
||||||
|
throw new OidcInvalidTokenException("Only $validSignatures signatures validation are supported. Token reports using {$this->header['alg']}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$parsedKeys = array_map(function ($key) {
|
|
||||||
try {
|
|
||||||
return new OidcJwtSigningKey($key);
|
|
||||||
} catch (OidcInvalidKeyException $e) {
|
|
||||||
throw new OidcInvalidTokenException('Failed to read signing key with error: ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
}, $this->keys);
|
|
||||||
|
|
||||||
$parsedKeys = array_filter($parsedKeys);
|
|
||||||
|
|
||||||
$contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1];
|
|
||||||
/** @var OidcJwtSigningKey $parsedKey */
|
|
||||||
foreach ($parsedKeys as $parsedKey) {
|
|
||||||
if ($parsedKey->verify($contentToSign, $this->signature)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new OidcInvalidTokenException('Token signature could not be validated using the provided keys');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class OidcProviderSettings
|
|||||||
{
|
{
|
||||||
$this->validateInitial();
|
$this->validateInitial();
|
||||||
|
|
||||||
$required = ['keys', 'tokenEndpoint', 'authorizationEndpoint'];
|
$required = ['tokenEndpoint', 'authorizationEndpoint'];
|
||||||
foreach ($required as $prop) {
|
foreach ($required as $prop) {
|
||||||
if (empty($this->$prop)) {
|
if (empty($this->$prop)) {
|
||||||
throw new InvalidArgumentException("Missing required configuration \"{$prop}\" value");
|
throw new InvalidArgumentException("Missing required configuration \"{$prop}\" value");
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ use BookStack\Theming\ThemeEvents;
|
|||||||
use BookStack\Uploads\UserAvatars;
|
use BookStack\Uploads\UserAvatars;
|
||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
|
|
||||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,7 +139,7 @@ class OidcService
|
|||||||
'redirectUri' => url('/oidc/callback'),
|
'redirectUri' => url('/oidc/callback'),
|
||||||
], [
|
], [
|
||||||
'httpClient' => $this->http->buildClient(5),
|
'httpClient' => $this->http->buildClient(5),
|
||||||
'optionProvider' => new HttpBasicAuthOptionProvider(),
|
'optionProvider' => new OidcHttpBasicWithClientIdOptionProvider(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
foreach ($this->getAdditionalScopes() as $scope) {
|
foreach ($this->getAdditionalScopes() as $scope) {
|
||||||
@@ -199,7 +198,7 @@ class OidcService
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$idToken->validate($settings->clientId);
|
$idToken->validate($settings);
|
||||||
} catch (OidcInvalidTokenException $exception) {
|
} catch (OidcInvalidTokenException $exception) {
|
||||||
throw new OidcException("ID token validation failed with error: {$exception->getMessage()}");
|
throw new OidcException("ID token validation failed with error: {$exception->getMessage()}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class PwaManifestBuilder
|
|||||||
// does not start a session, so we won't have current user context.
|
// does not start a session, so we won't have current user context.
|
||||||
// This was attempted but removed since manifest calls could affect user session
|
// This was attempted but removed since manifest calls could affect user session
|
||||||
// history tracking and back redirection.
|
// history tracking and back redirection.
|
||||||
// Context: https://codeberg.org/bookstack/bookstack/issues/4649
|
// Context: https://github.com/BookStackApp/BookStack/issues/4649
|
||||||
$darkMode = (bool) setting()->getForCurrentUser('dark-mode-enabled');
|
$darkMode = (bool) setting()->getForCurrentUser('dark-mode-enabled');
|
||||||
$appName = setting('app-name');
|
$appName = setting('app-name');
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ return [
|
|||||||
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
|
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
|
||||||
* Symbol, ZapfDingbats.
|
* Symbol, ZapfDingbats.
|
||||||
*/
|
*/
|
||||||
'font_dir' => storage_path('fonts/dompdf'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
|
'font_dir' => storage_path('fonts/'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The location of the DOMPDF font cache directory.
|
* The location of the DOMPDF font cache directory.
|
||||||
@@ -78,7 +78,7 @@ return [
|
|||||||
*
|
*
|
||||||
* Note: This directory must exist and be writable by the webserver process.
|
* Note: This directory must exist and be writable by the webserver process.
|
||||||
*/
|
*/
|
||||||
'font_cache' => storage_path('fonts/dompdf/cache'),
|
'font_cache' => storage_path('fonts/'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The location of a temporary directory.
|
* The location of a temporary directory.
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ namespace BookStack\Exports;
|
|||||||
|
|
||||||
use BookStack\Exceptions\PdfExportException;
|
use BookStack\Exceptions\PdfExportException;
|
||||||
use Dompdf\Dompdf;
|
use Dompdf\Dompdf;
|
||||||
use FontLib\Font;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Knp\Snappy\Pdf as SnappyPdf;
|
use Knp\Snappy\Pdf as SnappyPdf;
|
||||||
use Symfony\Component\Process\Exception\ProcessTimedOutException;
|
use Symfony\Component\Process\Exception\ProcessTimedOutException;
|
||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\Process\Process;
|
||||||
@@ -62,65 +60,12 @@ class PdfGenerator
|
|||||||
$domPdf = new Dompdf($options);
|
$domPdf = new Dompdf($options);
|
||||||
$domPdf->setBasePath(base_path('public'));
|
$domPdf->setBasePath(base_path('public'));
|
||||||
|
|
||||||
$fontMetrics = $domPdf->getFontMetrics();
|
|
||||||
$userFontfamilies = $this->getUserDomPdfFontFamilies();
|
|
||||||
foreach ($userFontfamilies as $fontFamily => $fonts) {
|
|
||||||
try {
|
|
||||||
$fontMetrics->setFontFamily($fontFamily, $fonts);
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
$expectedPath = storage_path('fonts/dompdf');
|
|
||||||
throw new PdfExportException("Failed to create required font data in {$expectedPath}, Ensure all content in this location is writable by the web server");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$domPdf->loadHTML($this->convertEntities($html));
|
$domPdf->loadHTML($this->convertEntities($html));
|
||||||
$domPdf->render();
|
$domPdf->render();
|
||||||
|
|
||||||
return (string) $domPdf->output();
|
return (string) $domPdf->output();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, array<string, string>>
|
|
||||||
*/
|
|
||||||
protected function getUserDomPdfFontFamilies(): array
|
|
||||||
{
|
|
||||||
$fontStore = storage_path('fonts/dompdf');
|
|
||||||
if (!is_dir($fontStore)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$fontFamilies = [];
|
|
||||||
$fontFiles = glob($fontStore . DIRECTORY_SEPARATOR . '*.ttf');
|
|
||||||
foreach ($fontFiles as $fontFile) {
|
|
||||||
$fontFileName = basename($fontFile, '.ttf');
|
|
||||||
$expectedUfm = $fontStore . DIRECTORY_SEPARATOR . $fontFileName . '.ufm';
|
|
||||||
if (!file_exists($expectedUfm)) {
|
|
||||||
$font = Font::load($fontFile);
|
|
||||||
$font->parse();
|
|
||||||
try {
|
|
||||||
$font->saveAdobeFontMetrics($expectedUfm);
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
throw new PdfExportException("Failed to create required font data at $expectedUfm, Ensure this location is writable by the web server");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$nameParts = explode('-', $fontFileName);
|
|
||||||
if (count($nameParts) === 1 || $nameParts[1] === 'Regular') {
|
|
||||||
$nameParts[1] = 'Normal';
|
|
||||||
}
|
|
||||||
|
|
||||||
$family = trim(strtolower(preg_replace('/([A-Z])/', ' $1', $nameParts[0])));
|
|
||||||
$variation = Str::snake($nameParts[1]);
|
|
||||||
if (!isset($fontFamilies[$family])) {
|
|
||||||
$fontFamilies[$family] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$fontFamilies[$family][$variation] = $fontStore . DIRECTORY_SEPARATOR . $fontFileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $fontFamilies;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws PdfExportException
|
* @throws PdfExportException
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
project_id: "377219"
|
project_id: "377219"
|
||||||
project_identifier: bookstack
|
project_identifier: bookstack
|
||||||
api_token_env: CROWDIN_PERSONAL_TOKEN
|
|
||||||
|
|
||||||
base_path: .
|
base_path: .
|
||||||
preserve_hierarchy: false
|
preserve_hierarchy: false
|
||||||
pull_request_title: Updated translations with latest Crowdin changes
|
pull_request_title: Updated translations with latest Crowdin changes
|
||||||
pull_request_labels:
|
pull_request_labels:
|
||||||
- "Translations"
|
- ":earth_africa: Translations"
|
||||||
|
|
||||||
files:
|
files:
|
||||||
- source: /lang/en/*.php
|
- source: /lang/en/*.php
|
||||||
translation: /lang/%two_letters_code%/%original_file_name%
|
translation: /lang/%two_letters_code%/%original_file_name%
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ ARG BRANCH=development
|
|||||||
|
|
||||||
# Download BookStack & install PHP deps
|
# Download BookStack & install PHP deps
|
||||||
RUN mkdir -p /var/www && \
|
RUN mkdir -p /var/www && \
|
||||||
git clone https://codeberg.org/bookstack/bookstack.git --branch "$BRANCH" --single-branch /var/www/bookstack && \
|
git clone https://github.com/bookstackapp/bookstack.git --branch "$BRANCH" --single-branch /var/www/bookstack && \
|
||||||
cd /var/www/bookstack && \
|
cd /var/www/bookstack && \
|
||||||
wget https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer -O - -q | php -- --quiet --filename=composer && \
|
wget https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer -O - -q | php -- --quiet --filename=composer && \
|
||||||
php composer install
|
php composer install
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ Theme::registerCommand(new SayHelloCommand());
|
|||||||
|
|
||||||
## Available Events
|
## Available Events
|
||||||
|
|
||||||
All available events dispatched by BookStack are exposed as static properties on the `\BookStack\Theming\ThemeEvents` class, which can be found within the file `app/Theming/ThemeEvents.php` relative to your root BookStack folder. Alternatively, the events for the latest release can be [seen on Codeberg here](https://codeberg.org/bookstack/bookstack/src/branch/release/app/Theming/ThemeEvents.php).
|
All available events dispatched by BookStack are exposed as static properties on the `\BookStack\Theming\ThemeEvents` class, which can be found within the file `app/Theming/ThemeEvents.php` relative to your root BookStack folder. Alternatively, the events for the latest release can be [seen on GitHub here](https://github.com/BookStackApp/BookStack/blob/release/app/Theming/ThemeEvents.php).
|
||||||
|
|
||||||
The comments above each constant with the `ThemeEvents.php` file describe the dispatch conditions of the event, in addition to the arguments the action will receive. The comments may also describe any ways the return value of the action may be used.
|
The comments above each constant with the `ThemeEvents.php` file describe the dispatch conditions of the event, in addition to the arguments the action will receive. The comments may also describe any ways the return value of the action may be used.
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ Feature releases are generally larger, bringing new features in addition to fixe
|
|||||||
|
|
||||||
### Release Planning Process
|
### Release Planning Process
|
||||||
|
|
||||||
Each BookStack release will have a [milestone](https://codeberg.org/bookstack/bookstack/milestones) created with issues & pull requests assigned to it to define what will be in that release. Milestones are built up then worked through until complete at which point, after some testing and documentation updates, the release will be deployed.
|
Each BookStack release will have a [milestone](https://github.com/BookStackApp/BookStack/milestones) created with issues & pull requests assigned to it to define what will be in that release. Milestones are built up then worked through until complete at which point, after some testing and documentation updates, the release will be deployed.
|
||||||
|
|
||||||
### Release Announcements
|
### Release Announcements
|
||||||
|
|
||||||
Feature releases, and some patch releases, will be accompanied by a post on the [BookStack blog](https://www.bookstackapp.com/blog/) which will provide additional detail on features, changes & updates otherwise the [Codeberg release page](https://codeberg.org/bookstack/bookstack/releases) will show a list of changes. You can sign up to be alerted to new BookStack blog posts (once per week maximum) [at this link](https://updates.bookstackapp.com/signup/bookstack-news-and-updates).
|
Feature releases, and some patch releases, will be accompanied by a post on the [BookStack blog](https://www.bookstackapp.com/blog/) which will provide additional detail on features, changes & updates otherwise the [GitHub release page](https://github.com/BookStackApp/BookStack/releases) will show a list of changes. You can sign up to be alerted to new BookStack blog posts (once per week maximum) [at this link](https://updates.bookstackapp.com/signup/bookstack-news-and-updates).
|
||||||
|
|
||||||
### Release Technical Process
|
### Release Technical Process
|
||||||
|
|
||||||
Deploying a release, at a high level, simply involves merging the development branch into the release branch before then building & committing any release-only assets.
|
Deploying a release, at a high level, simply involves merging the development branch into the release branch before then building & committing any release-only assets.
|
||||||
A helper script [can be found in our](https://codeberg.org/bookstack/devops/src/branch/main/meta-scripts/bookstack-release-steps) devops repo which provides the steps and commands for deploying a new release.
|
A helper script [can be found in our](https://github.com/BookStackApp/devops/blob/main/meta-scripts/bookstack-release-steps) devops repo which provides the steps and commands for deploying a new release.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Warning: This API is currently in development and may change without notice.**
|
**Warning: This API is currently in development and may change without notice.**
|
||||||
|
|
||||||
Feedback is very much welcomed via this issue: https://codeberg.org/bookstack/bookstack/issues/5937
|
Feedback is very much welcomed via this issue: https://github.com/BookStackApp/BookStack/issues/5937
|
||||||
|
|
||||||
This document covers the JavaScript API for the (newer Lexical-based) WYSIWYG editor.
|
This document covers the JavaScript API for the (newer Lexical-based) WYSIWYG editor.
|
||||||
This API is built and designed to abstract the internals of the editor away
|
This API is built and designed to abstract the internals of the editor away
|
||||||
|
|||||||
28
readme.md
28
readme.md
@@ -1,15 +1,17 @@
|
|||||||
# BookStack
|
# BookStack
|
||||||
|
|
||||||
[](https://codeberg.org/bookstack/bookstack/releases/latest)
|
[](https://github.com/BookStackApp/BookStack/releases/latest)
|
||||||
[](https://codeberg.org/bookstack/bookstack/src/branch/development/LICENSE)
|
[](https://github.com/BookStackApp/BookStack/blob/development/LICENSE)
|
||||||
[](https://crowdin.com/project/bookstack)
|
[](https://crowdin.com/project/bookstack)
|
||||||
[](https://codeberg.org/bookstack/bookstack/actions?workflow=test-php.yml)
|
[](https://github.com/BookStackApp/BookStack/actions)
|
||||||
[](https://codeberg.org/bookstack/bookstack/actions?workflow=lint-php.yml)
|
[](https://github.com/BookStackApp/BookStack/actions)
|
||||||
[](https://source.bookstackapp.com/php-stats/index.html)
|
[](https://source.bookstackapp.com/php-stats/index.html)
|
||||||
<br>
|
<br>
|
||||||
[](https://source.bookstackapp.com/)
|
[](https://source.bookstackapp.com/)
|
||||||
|
[](https://gh-stats.bookstackapp.com/)
|
||||||
[](https://community.bookstackapp.com/)
|
[](https://community.bookstackapp.com/)
|
||||||
[](https://www.bookstackapp.com/links/mastodon)
|
[](https://www.bookstackapp.com/links/mastodon)
|
||||||
|
[](https://www.bookstackapp.com/links/discord)
|
||||||
<br>
|
<br>
|
||||||
[](https://foss.video/c/bookstack)
|
[](https://foss.video/c/bookstack)
|
||||||
[](https://www.youtube.com/bookstackapp)
|
[](https://www.youtube.com/bookstackapp)
|
||||||
@@ -21,7 +23,7 @@ A platform for storing and organising information and documentation. Details for
|
|||||||
* [Demo Instance](https://demo.bookstackapp.com)
|
* [Demo Instance](https://demo.bookstackapp.com)
|
||||||
* [Screenshots](https://www.bookstackapp.com/#screenshots)
|
* [Screenshots](https://www.bookstackapp.com/#screenshots)
|
||||||
* [BookStack Blog](https://www.bookstackapp.com/blog)
|
* [BookStack Blog](https://www.bookstackapp.com/blog)
|
||||||
* [Issue List](https://codeberg.org/bookstack/bookstack/issues)
|
* [Issue List](https://github.com/BookStackApp/BookStack/issues)
|
||||||
* [Community Discussions](https://community.bookstackapp.com/)
|
* [Community Discussions](https://community.bookstackapp.com/)
|
||||||
* [Support Options](https://www.bookstackapp.com/support/)
|
* [Support Options](https://www.bookstackapp.com/support/)
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ Big thanks to these companies for supporting the project.
|
|||||||
<td align="center"><a href="https://www.stellarhosted.com/bookstack/" target="_blank">
|
<td align="center"><a href="https://www.stellarhosted.com/bookstack/" target="_blank">
|
||||||
<img width="240" src="https://www.bookstackapp.com/images/sponsors/stellarhosted.png" alt="Stellar Hosted">
|
<img width="240" src="https://www.bookstackapp.com/images/sponsors/stellarhosted.png" alt="Stellar Hosted">
|
||||||
</a></td>
|
</a></td>
|
||||||
<td align="center" style="text-align: center"><a href="https://nws.netways.de" target="_blank">
|
<td align="center" style="text-align: center"><a href="https://nws.netways.de/apps/bookstack/" target="_blank">
|
||||||
<img width="240" src="https://www.bookstackapp.com/images/sponsors/netways.png" alt="NETWAYS Web Services">
|
<img width="240" src="https://www.bookstackapp.com/images/sponsors/netways.png" alt="NETWAYS Web Services">
|
||||||
</a></td>
|
</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -111,13 +113,13 @@ Translations for text within BookStack are managed through the [BookStack projec
|
|||||||
|
|
||||||
Please use [Crowdin](https://crowdin.com/project/bookstack) to contribute translations instead of opening a pull request. The translations within the working codebase can be out-of-date, and merging via code can cause conflicts & sync issues. If for some reason you can't use Crowdin feel free to open an issue to discuss alternative options.
|
Please use [Crowdin](https://crowdin.com/project/bookstack) to contribute translations instead of opening a pull request. The translations within the working codebase can be out-of-date, and merging via code can cause conflicts & sync issues. If for some reason you can't use Crowdin feel free to open an issue to discuss alternative options.
|
||||||
|
|
||||||
If you'd like a new language to be added to Crowdin, for you to be able to provide translations for, please [open a new issue here](https://codeberg.org/bookstack/bookstack/issues/new?template=.forgejo%2fISSUE_TEMPLATE%2flanguage_request.yml).
|
If you'd like a new language to be added to Crowdin, for you to be able to provide translations for, please [open a new issue here](https://github.com/BookStackApp/BookStack/issues/new?template=language_request.yml).
|
||||||
|
|
||||||
Please note, translations in BookStack are provided to the "Crowdin Global Translation Memory" which helps BookStack and other projects with finding translations. If you are not happy with contributing to this then providing translations to BookStack, even manually via code, is not advised.
|
Please note, translations in BookStack are provided to the "Crowdin Global Translation Memory" which helps BookStack and other projects with finding translations. If you are not happy with contributing to this then providing translations to BookStack, even manually via GitHub, is not advised.
|
||||||
|
|
||||||
## 🎁 Contributing, Issues & Pull Requests
|
## 🎁 Contributing, Issues & Pull Requests
|
||||||
|
|
||||||
Feel free to [create issues](https://codeberg.org/bookstack/bookstack/issues/new/choose) to request new features or to report bugs & problems. Just please follow the template given when creating the issue.
|
Feel free to [create issues](https://github.com/BookStackApp/BookStack/issues/new/choose) to request new features or to report bugs & problems. Just please follow the template given when creating the issue.
|
||||||
|
|
||||||
Pull requests are welcome but, unless it's a small tweak, it may be best to open the pull request early or create an issue for your intended change to discuss how it will fit into the project and plan out the merge. Just because a feature request exists, or is tagged, does not mean that feature would be accepted into the core project.
|
Pull requests are welcome but, unless it's a small tweak, it may be best to open the pull request early or create an issue for your intended change to discuss how it will fit into the project and plan out the merge. Just because a feature request exists, or is tagged, does not mean that feature would be accepted into the core project.
|
||||||
|
|
||||||
@@ -132,7 +134,7 @@ Security information for administering a BookStack instance can be found on the
|
|||||||
|
|
||||||
If you'd like to be notified of new potential security concerns you can [sign-up to the BookStack security mailing list](https://updates.bookstackapp.com/signup/bookstack-security-updates).
|
If you'd like to be notified of new potential security concerns you can [sign-up to the BookStack security mailing list](https://updates.bookstackapp.com/signup/bookstack-security-updates).
|
||||||
|
|
||||||
If you would like to report a security concern, details of doing so [can be found here](.forgejo/SECURITY.md).
|
If you would like to report a security concern, details of doing so [can be found here](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md).
|
||||||
|
|
||||||
## ♿ Accessibility
|
## ♿ Accessibility
|
||||||
|
|
||||||
@@ -140,18 +142,18 @@ We want BookStack to remain accessible to as many people as possible. We aim for
|
|||||||
|
|
||||||
## 🖥️ Website, Docs & Blog
|
## 🖥️ Website, Docs & Blog
|
||||||
|
|
||||||
The website which contains the project docs & blog can be found in the [bookstack/website](https://codeberg.org/bookstack/website) repo.
|
The website which contains the project docs & blog can be found in the [BookStackApp/website](https://codeberg.org/bookstack/website) repo.
|
||||||
|
|
||||||
## ⚖️ License
|
## ⚖️ License
|
||||||
|
|
||||||
The BookStack source is provided under the [MIT License](https://codeberg.org/bookstack/bookstack/src/branch/development/LICENSE).
|
The BookStack source is provided under the [MIT License](https://github.com/BookStackApp/BookStack/blob/development/LICENSE).
|
||||||
|
|
||||||
The libraries used by, and included with, BookStack are provided under their own licenses and copyright.
|
The libraries used by, and included with, BookStack are provided under their own licenses and copyright.
|
||||||
The licenses for many of our core dependencies can be found in the attribution list below, but this is not an exhaustive list of all projects used within BookStack.
|
The licenses for many of our core dependencies can be found in the attribution list below, but this is not an exhaustive list of all projects used within BookStack.
|
||||||
|
|
||||||
## 👪 Attribution
|
## 👪 Attribution
|
||||||
|
|
||||||
The great people that have worked to build and improve BookStack can [be seen here](https://codeberg.org/bookstack/bookstack/activity/contributors). The wonderful people that have provided translations, either through GitHub, Codeberg or via Crowdin [can be seen here](https://codeberg.org/bookstack/bookstack/src/branch/development/.github/translators.txt).
|
The great people that have worked to build and improve BookStack can [be seen here](https://github.com/BookStackApp/BookStack/graphs/contributors). The wonderful people that have provided translations, either through GitHub or via Crowdin [can be seen here](https://github.com/BookStackApp/BookStack/blob/development/.github/translators.txt).
|
||||||
|
|
||||||
Below are the great open-source projects used to help build BookStack.
|
Below are the great open-source projects used to help build BookStack.
|
||||||
Note: This is not an exhaustive list of all libraries and projects that would be used in an active BookStack instance.
|
Note: This is not an exhaustive list of all libraries and projects that would be used in an active BookStack instance.
|
||||||
|
|||||||
@@ -14,11 +14,11 @@
|
|||||||
HTTP POST calls upon events occurring in BookStack.
|
HTTP POST calls upon events occurring in BookStack.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://codeberg.org/bookstack/bookstack/src/branch/development/dev/docs/visual-theme-system.md" target="_blank" rel="noopener noreferrer">Visual Theme System</a> -
|
<a href="https://github.com/BookStackApp/BookStack/blob/development/dev/docs/visual-theme-system.md" target="_blank" rel="noopener noreferrer">Visual Theme System</a> -
|
||||||
Methods to override views, translations and icons within BookStack.
|
Methods to override views, translations and icons within BookStack.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://codeberg.org/bookstack/bookstack/src/branch/development/dev/docs/logical-theme-system.md" target="_blank" rel="noopener noreferrer">Logical Theme System</a> -
|
<a href="https://github.com/BookStackApp/BookStack/blob/development/dev/docs/logical-theme-system.md" target="_blank" rel="noopener noreferrer">Logical Theme System</a> -
|
||||||
Methods to extend back-end functionality within BookStack.
|
Methods to extend back-end functionality within BookStack.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -113,13 +113,13 @@
|
|||||||
<a href="https://www.bookstackapp.com/docs/admin/debugging/" target="_blank">Review BookStack debugging documentation »</a>
|
<a href="https://www.bookstackapp.com/docs/admin/debugging/" target="_blank">Review BookStack debugging documentation »</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://codeberg.org/bookstack/bookstack/releases" target="_blank">Ensure your instance is up-to-date »</a>
|
<a href="https://github.com/BookStackApp/BookStack/releases" target="_blank">Ensure your instance is up-to-date »</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://codeberg.org/bookstack/bookstack/issues?q={{ urlencode($error) }}" target="_blank">Search for the issue on GitHub »</a>
|
<a href="https://github.com/BookStackApp/BookStack/issues?q=is%3Aissue+{{ urlencode($error) }}" target="_blank">Search for the issue on GitHub »</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://community.bookstackapp.com" target="_blank">Ask for help in our community forums »</a>
|
<a href="https://discord.gg/ztkBqR2" target="_blank">Ask for help via Discord »</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://duckduckgo.com/?q={{urlencode("BookStack {$error}")}}" target="_blank">Search the error message »</a>
|
<a href="https://duckduckgo.com/?q={{urlencode("BookStack {$error}")}}" target="_blank">Search the error message »</a>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
<h5 class="mt-xl">{{ trans('settings.system_version') }}</h5>
|
<h5 class="mt-xl">{{ trans('settings.system_version') }}</h5>
|
||||||
<div class="py-xs">
|
<div class="py-xs">
|
||||||
<a target="_blank" rel="noopener noreferrer" href="https://codeberg.org/bookstack/bookstack/releases">
|
<a target="_blank" rel="noopener noreferrer" href="https://github.com/BookStackApp/BookStack/releases">
|
||||||
BookStack @if(!str_starts_with($version, 'v')) version @endif {{ $version }}
|
BookStack @if(!str_starts_with($version, 'v')) version @endif {{ $version }}
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
6
storage/fonts/.gitignore
vendored
6
storage/fonts/.gitignore
vendored
@@ -1,6 +1,2 @@
|
|||||||
# Font cache files have once been stored directly in this folder
|
|
||||||
# therefore its important the contents non-ignored by git
|
|
||||||
# are chosen selectively
|
|
||||||
*
|
*
|
||||||
!.gitignore
|
!.gitignore
|
||||||
!dompdf/
|
|
||||||
3
storage/fonts/dompdf/.gitignore
vendored
3
storage/fonts/dompdf/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
||||||
!cache/
|
|
||||||
2
storage/fonts/dompdf/cache/.gitignore
vendored
2
storage/fonts/dompdf/cache/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
||||||
@@ -27,7 +27,7 @@ class DebugViewTest extends TestCase
|
|||||||
$resp->assertSeeText('BookStack Version: ' . trim(file_get_contents(base_path('version'))));
|
$resp->assertSeeText('BookStack Version: ' . trim(file_get_contents(base_path('version'))));
|
||||||
// Dynamic help links
|
// Dynamic help links
|
||||||
$this->withHtml($resp)->assertElementExists('a[href*="q=' . urlencode('BookStack An error occurred during testing') . '"]');
|
$this->withHtml($resp)->assertElementExists('a[href*="q=' . urlencode('BookStack An error occurred during testing') . '"]');
|
||||||
$this->withHtml($resp)->assertElementExists('a[href*="?q=' . urlencode('An error occurred during testing') . '"]');
|
$this->withHtml($resp)->assertElementExists('a[href*="?q=is%3Aissue+' . urlencode('An error occurred during testing') . '"]');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_debug_view_only_shows_when_debug_mode_is_enabled()
|
public function test_debug_view_only_shows_when_debug_mode_is_enabled()
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ class PageContentTest extends TestCase
|
|||||||
|
|
||||||
public function test_base64_images_within_html_blanked_if_not_supported_extension_for_extract()
|
public function test_base64_images_within_html_blanked_if_not_supported_extension_for_extract()
|
||||||
{
|
{
|
||||||
// Relevant to https://codeberg.org/bookstack/bookstack/issues/3010 and other cases
|
// Relevant to https://github.com/BookStackApp/BookStack/issues/3010 and other cases
|
||||||
$extensions = [
|
$extensions = [
|
||||||
'jiff', 'pngr', 'png ', ' png', '.png', 'png.', 'p.ng', ',png',
|
'jiff', 'pngr', 'png ', ' png', '.png', 'png.', 'p.ng', ',png',
|
||||||
'data:image/png', ',data:image/png',
|
'data:image/png', ',data:image/png',
|
||||||
|
|||||||
@@ -79,39 +79,6 @@ class PdfExportTest extends TestCase
|
|||||||
$this->assertStringContainsString('<details open="open"', $pdfHtml);
|
$this->assertStringContainsString('<details open="open"', $pdfHtml);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_custom_fonts_loaded_for_dom_pdf_when_used()
|
|
||||||
{
|
|
||||||
// Set up custom font usage
|
|
||||||
$page = $this->entities->page()->forceFill([
|
|
||||||
'html' => '<p><strong>Bold</strong>text</p>',
|
|
||||||
]);
|
|
||||||
$page->save();
|
|
||||||
$this->setSettings([
|
|
||||||
'app-custom-head' => '<style>* { font-family: "meow words"}</style>'
|
|
||||||
]);
|
|
||||||
$normalFont = $this->files->testFilePath('fonts/Cardiff.ttf');
|
|
||||||
$normalFontTarget = storage_path('fonts/dompdf/MeowWords.ttf');
|
|
||||||
$boldFont = $this->files->testFilePath('fonts/Cardiff-Bold.ttf');
|
|
||||||
$boldFontTarget = storage_path('fonts/dompdf/MeowWords-Bold.ttf');
|
|
||||||
copy($normalFont, $normalFontTarget);
|
|
||||||
copy($boldFont, $boldFontTarget);
|
|
||||||
|
|
||||||
$resp = $this->asEditor()->get($page->getUrl('/export/pdf'));
|
|
||||||
$resp->assertStatus(200);
|
|
||||||
|
|
||||||
// Existance of UFM files indicates the metrics have been generated
|
|
||||||
$this->assertFileExists(storage_path('fonts/dompdf/MeowWords.ufm'));
|
|
||||||
$this->assertFileExists(storage_path('fonts/dompdf/MeowWords-Bold.ufm'));
|
|
||||||
// Existence of cache json files indicates the fonts have been used
|
|
||||||
$this->assertFileExists(storage_path('fonts/dompdf/cache/MeowWords.ufm.json'));
|
|
||||||
$this->assertFileExists(storage_path('fonts/dompdf/cache/MeowWords-Bold.ufm.json'));
|
|
||||||
|
|
||||||
$filesToCleanUp = [...glob(storage_path('fonts/dompdf/Meow*')), ...glob(storage_path('fonts/dompdf/cache/Meow*'))];
|
|
||||||
foreach ($filesToCleanUp as $file) {
|
|
||||||
unlink($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_wkhtmltopdf_only_used_when_allow_untrusted_is_true()
|
public function test_wkhtmltopdf_only_used_when_allow_untrusted_is_true()
|
||||||
{
|
{
|
||||||
$page = $this->entities->page();
|
$page = $this->entities->page();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Tests\Helpers;
|
namespace Tests\Helpers;
|
||||||
|
|
||||||
|
use BookStack\Access\Oidc\OidcProviderSettings;
|
||||||
use phpseclib3\Crypt\RSA;
|
use phpseclib3\Crypt\RSA;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,6 +120,41 @@ q/1PY4iJviGKddtmfClH3v4=
|
|||||||
-----END PRIVATE KEY-----';
|
-----END PRIVATE KEY-----';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function defaultSecret(): string
|
||||||
|
{
|
||||||
|
return 'test-client-secret-for-hs256';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a minimal OidcProviderSettings for use in token validation tests.
|
||||||
|
*/
|
||||||
|
public static function defaultProviderSettings(array $overrides = []): OidcProviderSettings
|
||||||
|
{
|
||||||
|
return new OidcProviderSettings(array_merge([
|
||||||
|
'issuer' => static::defaultIssuer(),
|
||||||
|
'clientId' => static::defaultClientId(),
|
||||||
|
'clientSecret' => static::defaultSecret(),
|
||||||
|
], $overrides));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an HS256-signed ID token using the given secret.
|
||||||
|
*/
|
||||||
|
public static function hs256IdToken(string $secret, array $payloadOverrides = []): string
|
||||||
|
{
|
||||||
|
$payload = array_merge(static::defaultPayload(), $payloadOverrides);
|
||||||
|
$header = ['alg' => 'HS256', 'typ' => 'JWT'];
|
||||||
|
|
||||||
|
$top = implode('.', [
|
||||||
|
static::base64UrlEncode(json_encode($header)),
|
||||||
|
static::base64UrlEncode(json_encode($payload)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$signature = hash_hmac('sha256', $top, $secret, true);
|
||||||
|
|
||||||
|
return $top . '.' . static::base64UrlEncode($signature);
|
||||||
|
}
|
||||||
|
|
||||||
public static function publicJwkKeyArray(): array
|
public static function publicJwkKeyArray(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class OidcIdTokenTest extends TestCase
|
|||||||
OidcJwtHelper::publicJwkKeyArray(),
|
OidcJwtHelper::publicJwkKeyArray(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertTrue($token->validate('xxyyzz.aaa.bbccdd.123'));
|
$this->assertTrue($token->validate(OidcJwtHelper::defaultProviderSettings()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_get_claim_returns_value_if_existing()
|
public function test_get_claim_returns_value_if_existing()
|
||||||
@@ -56,7 +56,7 @@ class OidcIdTokenTest extends TestCase
|
|||||||
$err = null;
|
$err = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$token->validate('abc');
|
$token->validate(OidcJwtHelper::defaultProviderSettings());
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$err = $exception;
|
$err = $exception;
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ class OidcIdTokenTest extends TestCase
|
|||||||
$token = new OidcIdToken(OidcJwtHelper::idToken(), OidcJwtHelper::defaultIssuer(), []);
|
$token = new OidcIdToken(OidcJwtHelper::idToken(), OidcJwtHelper::defaultIssuer(), []);
|
||||||
$this->expectException(OidcInvalidTokenException::class);
|
$this->expectException(OidcInvalidTokenException::class);
|
||||||
$this->expectExceptionMessage('Token signature could not be validated using the provided keys');
|
$this->expectExceptionMessage('Token signature could not be validated using the provided keys');
|
||||||
$token->validate('abc');
|
$token->validate(OidcJwtHelper::defaultProviderSettings());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_error_thrown_if_token_signature_not_validated_from_non_matching_key()
|
public function test_error_thrown_if_token_signature_not_validated_from_non_matching_key()
|
||||||
@@ -83,7 +83,7 @@ class OidcIdTokenTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
$this->expectException(OidcInvalidTokenException::class);
|
$this->expectException(OidcInvalidTokenException::class);
|
||||||
$this->expectExceptionMessage('Token signature could not be validated using the provided keys');
|
$this->expectExceptionMessage('Token signature could not be validated using the provided keys');
|
||||||
$token->validate('abc');
|
$token->validate(OidcJwtHelper::defaultProviderSettings());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_error_thrown_if_invalid_key_provided()
|
public function test_error_thrown_if_invalid_key_provided()
|
||||||
@@ -91,15 +91,34 @@ class OidcIdTokenTest extends TestCase
|
|||||||
$token = new OidcIdToken(OidcJwtHelper::idToken(), OidcJwtHelper::defaultIssuer(), ['url://example.com']);
|
$token = new OidcIdToken(OidcJwtHelper::idToken(), OidcJwtHelper::defaultIssuer(), ['url://example.com']);
|
||||||
$this->expectException(OidcInvalidTokenException::class);
|
$this->expectException(OidcInvalidTokenException::class);
|
||||||
$this->expectExceptionMessage('Unexpected type of key value provided');
|
$this->expectExceptionMessage('Unexpected type of key value provided');
|
||||||
$token->validate('abc');
|
$token->validate(OidcJwtHelper::defaultProviderSettings());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_error_thrown_if_token_algorithm_is_not_rs256()
|
public function test_error_thrown_if_token_algorithm_is_not_supported()
|
||||||
{
|
{
|
||||||
$token = new OidcIdToken(OidcJwtHelper::idToken([], ['alg' => 'HS256']), OidcJwtHelper::defaultIssuer(), []);
|
$token = new OidcIdToken(OidcJwtHelper::idToken([], ['alg' => 'ES256']), OidcJwtHelper::defaultIssuer(), []);
|
||||||
$this->expectException(OidcInvalidTokenException::class);
|
$this->expectException(OidcInvalidTokenException::class);
|
||||||
$this->expectExceptionMessage('Only RS256 signature validation is supported. Token reports using HS256');
|
$this->expectExceptionMessage('Only HS256, RS256 signatures validation are supported. Token reports using ES256');
|
||||||
$token->validate('abc');
|
$token->validate(OidcJwtHelper::defaultProviderSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_hs256_token_passes_validation_with_correct_secret()
|
||||||
|
{
|
||||||
|
$secret = OidcJwtHelper::defaultSecret();
|
||||||
|
$token = new OidcIdToken(OidcJwtHelper::hs256IdToken($secret), OidcJwtHelper::defaultIssuer(), []);
|
||||||
|
$settings = OidcJwtHelper::defaultProviderSettings(['clientSecret' => $secret]);
|
||||||
|
|
||||||
|
$this->assertTrue($token->validate($settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_hs256_token_fails_validation_with_wrong_secret()
|
||||||
|
{
|
||||||
|
$token = new OidcIdToken(OidcJwtHelper::hs256IdToken('correct-secret'), OidcJwtHelper::defaultIssuer(), []);
|
||||||
|
$settings = OidcJwtHelper::defaultProviderSettings(['clientSecret' => 'wrong-secret']);
|
||||||
|
|
||||||
|
$this->expectException(OidcInvalidTokenException::class);
|
||||||
|
$this->expectExceptionMessage('Token signature could not be validated using the provided secret');
|
||||||
|
$token->validate($settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_token_claim_error_cases()
|
public function test_token_claim_error_cases()
|
||||||
@@ -141,7 +160,7 @@ class OidcIdTokenTest extends TestCase
|
|||||||
$err = null;
|
$err = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$token->validate('xxyyzz.aaa.bbccdd.123');
|
$token->validate(OidcJwtHelper::defaultProviderSettings());
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$err = $exception;
|
$err = $exception;
|
||||||
}
|
}
|
||||||
@@ -160,7 +179,7 @@ class OidcIdTokenTest extends TestCase
|
|||||||
$testFilePath,
|
$testFilePath,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertTrue($token->validate('xxyyzz.aaa.bbccdd.123'));
|
$this->assertTrue($token->validate(OidcJwtHelper::defaultProviderSettings()));
|
||||||
unlink($testFilePath);
|
unlink($testFilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class ImageTest extends TestCase
|
|||||||
|
|
||||||
public function test_image_display_thumbnail_generation_for_animated_avif_images_uses_original_file()
|
public function test_image_display_thumbnail_generation_for_animated_avif_images_uses_original_file()
|
||||||
{
|
{
|
||||||
if ((gd_info()['AVIF Support'] ?? false) !== true) {
|
if (! function_exists('imageavif')) {
|
||||||
$this->markTestSkipped('imageavif() is not available');
|
$this->markTestSkipped('imageavif() is not available');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,2 +0,0 @@
|
|||||||
Font files by Roger White, in public domain.
|
|
||||||
https://web.archive.org/web/20110609213636/http://www.rogersfonts.org.uk/
|
|
||||||
Reference in New Issue
Block a user