Skip to content

Commit 2176c7c

Browse files
Merge branch 'main' into fix/823/filter-unsupported-jwks-keys
2 parents 658f25f + f81b4aa commit 2176c7c

8 files changed

Lines changed: 3500 additions & 58 deletions

File tree

.github/workflows/psalm.yml

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,102 @@
66
# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors
77
# SPDX-License-Identifier: MIT
88

9-
name: Static analysis
9+
name: Psalm Static analysis
1010

11-
on: pull_request
11+
on:
12+
pull_request:
13+
paths:
14+
- .github/workflows/psalm.yml
15+
- appinfo/**
16+
- composer.*
17+
- lib/**
18+
- templates/**
19+
- tests/**
20+
push:
21+
branches:
22+
- main
23+
- stable*
24+
- test
25+
paths:
26+
- .github/workflows/psalm.yml
27+
- appinfo/**
28+
- composer.*
29+
- lib/**
30+
- templates/**
31+
- tests/**
1232

1333
concurrency:
1434
group: psalm-${{ github.head_ref || github.run_id }}
1535
cancel-in-progress: true
1636

37+
permissions:
38+
contents: read
39+
1740
jobs:
41+
matrix:
42+
runs-on: ubuntu-latest-low
43+
outputs:
44+
ocp-matrix: ${{ steps.versions.outputs.ocp-matrix }}
45+
steps:
46+
- name: Checkout app
47+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
48+
with:
49+
persist-credentials: false
50+
51+
- name: Get version matrix
52+
id: versions
53+
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
54+
55+
- name: Check enforcement of minimum PHP version ${{ steps.versions.outputs.php-min }} in psalm.xml
56+
run: grep 'phpVersion="${{ steps.versions.outputs.php-min }}' psalm.xml
57+
1858
static-analysis:
1959
runs-on: ubuntu-latest
60+
needs: matrix
61+
strategy:
62+
# do not stop on another job's failure
63+
fail-fast: false
64+
matrix: ${{ fromJson(needs.matrix.outputs.ocp-matrix) }}
2065

21-
name: static-psalm-analysis
66+
name: static-psalm-analysis ${{ matrix.ocp-version }}
2267
steps:
2368
- name: Checkout
24-
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
69+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
70+
with:
71+
persist-credentials: false
2572

26-
- name: Set up php8.2
73+
- name: Set up php${{ matrix.php-min }}
2774
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.36.0
2875
with:
29-
php-version: 8.2
76+
php-version: ${{ matrix.php-min }}
3077
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
3178
coverage: none
3279
ini-file: development
80+
# Temporary workaround for missing pcntl_* in PHP 8.3
81+
ini-values: disable_functions=
3382
env:
3483
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3584

3685
- name: Install dependencies
37-
run: composer i
86+
run: |
87+
composer remove nextcloud/ocp --dev
88+
composer i
89+
90+
91+
- name: Install dependencies # zizmor: ignore[template-injection]
92+
run: composer require --dev 'nextcloud/ocp:${{ matrix.ocp-version }}' --ignore-platform-reqs --with-dependencies
3893

3994
- name: Run coding standards check
40-
run: composer run psalm
95+
run: composer run psalm -- --threads=1 --monochrome --no-progress --output-format=github
96+
97+
summary:
98+
runs-on: ubuntu-latest-low
99+
needs: static-analysis
100+
101+
if: always()
102+
103+
name: static-psalm-analysis-summary
104+
105+
steps:
106+
- name: Summary status
107+
run: if ${{ needs.static-analysis.result != 'success' }}; then exit 1; fi

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
.idea
44
*.iml
55
/js
6-
/vendor/
6+
vendor/
77
/build/
88
node_modules/
99
/.php_cs.cache

REUSE.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ SPDX-FileCopyrightText = "2020 Nextcloud GmbH and Nextcloud contributors"
1818
SPDX-License-Identifier = "AGPL-3.0-or-later"
1919

2020
[[annotations]]
21-
path = ["tests/integration/config/realm-export.json", "vendor-bin/mozart/composer.json", "vendor-bin/mozart/composer.lock"]
21+
path = ["tests/integration/config/realm-export.json", "vendor-bin/mozart/composer.json", "vendor-bin/mozart/composer.lock", "vendor-bin/psalm/composer.json", "vendor-bin/psalm/composer.lock"]
2222
precedence = "aggregate"
2323
SPDX-FileCopyrightText = "2021 Nextcloud GmbH and Nextcloud contributors"
2424
SPDX-License-Identifier = "AGPL-3.0-or-later"

composer.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
"cs:check": "php-cs-fixer fix --dry-run --diff",
1515
"lint": "find . -name \\*.php ! -path './vendor/*' ! -path './vendor-bin/*' ! -path './lib/Vendor/*' -exec php -l \"{}\" \\;",
1616
"test:unit": "phpunit -c tests/phpunit.xml",
17-
"psalm": "psalm.phar --no-cache",
18-
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline",
19-
"psalm:update-baseline:force": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml",
17+
"psalm": "psalm --no-cache",
18+
"psalm:update-baseline": "psalm --threads=1 --update-baseline",
19+
"psalm:update-baseline:force": "psalm --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml",
2020
"post-install-cmd": [
2121
"@composer bin all install --ansi",
2222
"\"vendor/bin/mozart\" compose",
@@ -37,7 +37,6 @@
3737
"nextcloud/coding-standard": "^1.0.0",
3838
"symfony/event-dispatcher": "^4",
3939
"nextcloud/ocp": "dev-stable29",
40-
"psalm/phar": "^6",
4140
"phpunit/phpunit": "^10"
4241
},
4342
"extra": {
@@ -50,6 +49,11 @@
5049
"firebase/php-jwt"
5150
],
5251
"delete_vendor_directories": true
53-
}
52+
},
53+
"bamarni-bin": {
54+
"bin-links": true,
55+
"target-directory": "vendor-bin",
56+
"forward-command": true
57+
}
5458
}
5559
}

composer.lock

Lines changed: 1 addition & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/Command/UpsertProvider.php

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ protected function configure() {
178178
->addArgument('identifier', InputArgument::OPTIONAL, 'Administrative identifier name of the provider in the setup')
179179
->addOption('clientid', 'c', InputOption::VALUE_REQUIRED, 'OpenID client identifier')
180180
->addOption('clientsecret', 's', InputOption::VALUE_REQUIRED, 'OpenID client secret')
181+
->addOption('clientsecret-file', null, InputOption::VALUE_REQUIRED, 'File that contains the OpenID client secret')
182+
->addOption('clientsecret-env', null, InputOption::VALUE_REQUIRED, 'Environment variable that contains the OpenID client secret')
181183
->addOption('discoveryuri', 'd', InputOption::VALUE_REQUIRED, 'OpenID discovery endpoint uri')
182184
->addOption('endsessionendpointuri', 'e', InputOption::VALUE_REQUIRED, 'OpenID end session endpoint uri')
183185
->addOption('postlogouturi', 'p', InputOption::VALUE_REQUIRED, 'Post logout URI')
@@ -192,10 +194,15 @@ protected function execute(InputInterface $input, OutputInterface $output) {
192194
$outputFormat = $input->getOption('output') ?? 'table';
193195

194196
$identifier = $input->getArgument('identifier');
195-
$clientid = $input->getOption('clientid');
196-
$clientsecret = $input->getOption('clientsecret');
197-
if ($clientsecret !== null) {
198-
$clientsecret = $this->crypto->encrypt($clientsecret);
197+
$clientId = $input->getOption('clientid');
198+
$clientSecret = $input->getOption('clientsecret');
199+
$clientSecretFile = $input->getOption('clientsecret-file');
200+
$clientSecretEnv = $input->getOption('clientsecret-env');
201+
try {
202+
$clientSecret = $this->getClientSecretInput($clientSecret, $clientSecretFile, $clientSecretEnv, $output);
203+
} catch (\Exception $e) {
204+
$output->writeln($e->getMessage());
205+
return 1;
199206
}
200207
$discoveryuri = $input->getOption('discoveryuri');
201208
$endsessionendpointuri = $input->getOption('endsessionendpointuri');
@@ -218,7 +225,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
218225
try {
219226
$provider = $this->providerMapper->findProviderByIdentifier($identifier);
220227
} catch (DoesNotExistException $e) {
221-
$output->writeln('Provider not found');
228+
$output->writeln('<error>Provider not found</error>');
222229
return -1;
223230
}
224231
$provider = $this->providerService->getProviderWithSettings($provider->getId());
@@ -250,7 +257,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
250257
}
251258
try {
252259
$provider = $this->providerMapper->createOrUpdateProvider(
253-
$identifier, $clientid, $clientsecret, $discoveryuri, $scope, $endsessionendpointuri, $postLogoutUri
260+
$identifier, $clientId, $clientSecret, $discoveryuri, $scope, $endsessionendpointuri, $postLogoutUri
254261
);
255262
// invalidate JWKS cache (even if it was just created)
256263
$this->providerService->setSetting($provider->getId(), ProviderService::SETTING_JWKS_CACHE, '');
@@ -287,7 +294,7 @@ private function listProviders(InputInterface $input, OutputInterface $output) {
287294
}
288295

289296
if (count($providers) === 0) {
290-
$output->writeln('No providers configured');
297+
$output->writeln('<error>No providers configured</error>');
291298
return 0;
292299
}
293300

@@ -306,4 +313,40 @@ private function listProviders(InputInterface $input, OutputInterface $output) {
306313
$table->render();
307314
return 0;
308315
}
316+
317+
private function getClientSecretInput(
318+
?string $clientSecret, ?string $clientSecretFile, ?string $clientSecretEnv, OutputInterface $output,
319+
): ?string {
320+
if (
321+
($clientSecret !== null && $clientSecretFile !== null)
322+
|| ($clientSecret !== null && $clientSecretEnv !== null)
323+
|| ($clientSecretFile !== null && $clientSecretEnv !== null)
324+
) {
325+
throw new \Exception('<comment>Only one of "--clientsecret", "--clientsecret-file" or "--clientsecret-env" can be used.</comment>');
326+
}
327+
if ($clientSecret !== null) {
328+
return $this->crypto->encrypt($clientSecret);
329+
}
330+
if ($clientSecretFile !== null) {
331+
$clientSecret = file_get_contents($clientSecretFile);
332+
if (is_string($clientSecret) && $clientSecret !== '') {
333+
$output->writeln('<info>Client secret loaded from file "' . $clientSecretFile . '"</info>');
334+
$clientSecret = trim($clientSecret);
335+
return $this->crypto->encrypt($clientSecret);
336+
} else {
337+
throw new \Exception('<error>Client secret file "' . $clientSecretFile . '" could not be read or is empty</error>');
338+
}
339+
}
340+
if ($clientSecretEnv !== null) {
341+
$clientSecret = getenv($clientSecretEnv);
342+
if (is_string($clientSecret) && $clientSecret !== '') {
343+
$output->writeln('<info>Client secret loaded from environment variable "' . $clientSecretEnv . '"</info>');
344+
$clientSecret = trim($clientSecret);
345+
return $this->crypto->encrypt($clientSecret);
346+
} else {
347+
throw new \Exception('<error>Client secret environment variable "' . $clientSecretEnv . '" could not be read or is empty</error>');
348+
}
349+
}
350+
return null;
351+
}
309352
}

vendor-bin/psalm/composer.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"require": {
3+
"php": "^8.2 || ^8.3 || ^8.4 || ^8.5"
4+
},
5+
"require-dev": {
6+
"vimeo/psalm": "^6.10.0"
7+
},
8+
"config": {
9+
"platform": {
10+
"php": "8.2.27"
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)