Skip to content

Commit 29392a3

Browse files
authored
Merge pull request #5198 from LibreSign/chore/correct-validate-field
chore: Improvements at response from API when generate root certificate
2 parents 22c2860 + 4b43967 commit 29392a3

File tree

8 files changed

+314
-18
lines changed

8 files changed

+314
-18
lines changed

lib/Controller/AdminController.php

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use OCA\Libresign\Handler\CertificateEngine\IEngineHandler;
1515
use OCA\Libresign\Helper\ConfigureCheckHelper;
1616
use OCA\Libresign\ResponseDefinitions;
17+
use OCA\Libresign\Service\Certificate\ValidateService;
1718
use OCA\Libresign\Service\CertificatePolicyService;
1819
use OCA\Libresign\Service\Install\ConfigureCheckService;
1920
use OCA\Libresign\Service\Install\InstallService;
@@ -54,6 +55,7 @@ public function __construct(
5455
protected ISession $session,
5556
private SignatureBackgroundService $signatureBackgroundService,
5657
private CertificatePolicyService $certificatePolicyService,
58+
private ValidateService $validateService,
5759
) {
5860
parent::__construct(Application::APP_ID, $request);
5961
$this->eventSource = $this->eventSourceFactory->create();
@@ -136,15 +138,14 @@ private function generateCertificate(
136138
): IEngineHandler {
137139
$names = [];
138140
if (isset($rootCert['names'])) {
141+
$this->validateService->validateNames($rootCert['names']);
139142
foreach ($rootCert['names'] as $item) {
140-
if (empty($item['id'])) {
141-
throw new LibresignException('Parameter id is required!', 400);
142-
}
143-
$names[$item['id']]['value'] = $this->trimAndThrowIfEmpty($item['id'], $item['value']);
143+
$names[$item['id']]['value'] = trim((string)$item['value']);
144144
}
145145
}
146+
$this->validateService->validate('CN', $rootCert['commonName']);
146147
$this->installService->generate(
147-
$this->trimAndThrowIfEmpty('commonName', $rootCert['commonName']),
148+
trim((string)$rootCert['commonName']),
148149
$names,
149150
$properties,
150151
);
@@ -177,13 +178,6 @@ public function loadCertificate(): DataResponse {
177178
return new DataResponse($certificate);
178179
}
179180

180-
private function trimAndThrowIfEmpty(string $key, $value): string {
181-
if (empty($value)) {
182-
throw new LibresignException("parameter '{$key}' is required!", 400);
183-
}
184-
return trim((string)$value);
185-
}
186-
187181
/**
188182
* Check the configuration of LibreSign
189183
*
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Service\Certificate;
10+
11+
use OCP\IL10N;
12+
13+
class RulesService {
14+
15+
private array $rules = [
16+
'CN' => [
17+
'required' => true,
18+
'min' => 1,
19+
'max' => 64,
20+
],
21+
'C' => [
22+
'min' => 2,
23+
'max' => 2,
24+
],
25+
'ST' => [
26+
'min' => 1,
27+
'max' => 128,
28+
],
29+
'L' => [
30+
'min' => 1,
31+
'max' => 128,
32+
],
33+
'O' => [
34+
'min' => 1,
35+
'max' => 64,
36+
],
37+
'OU' => [
38+
'min' => 1,
39+
'max' => 64,
40+
],
41+
];
42+
43+
public function __construct(
44+
protected IL10N $l10n,
45+
) {
46+
47+
}
48+
49+
public function getRule(string $fieldName): array {
50+
if (!isset($this->rules[$fieldName]['helperText'])) {
51+
$this->rules[$fieldName]['helperText'] = $this->getHelperText($fieldName);
52+
if (empty($this->rules[$fieldName]['helperText'])) {
53+
unset($this->rules[$fieldName]['helperText']);
54+
}
55+
}
56+
return $this->rules[$fieldName];
57+
}
58+
59+
public function getHelperText(string $fieldName): ?string {
60+
return match ($fieldName) {
61+
'CN' => $this->l10n->t('Common Name (CN)'),
62+
'C' => $this->l10n->t('Two-letter ISO 3166 country code'),
63+
'ST' => $this->l10n->t('Full name of states or provinces'),
64+
'L' => $this->l10n->t('Name of a locality or place, such as a city, county, or other geographic region'),
65+
'O' => $this->l10n->t('Name of an organization'),
66+
'OU' => $this->l10n->t('Name of an organizational unit'),
67+
default => null,
68+
};
69+
}
70+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Service\Certificate;
10+
11+
use InvalidArgumentException;
12+
use OCP\IL10N;
13+
14+
class ValidateService {
15+
16+
public function __construct(
17+
protected RulesService $rulesService,
18+
protected IL10N $l10n,
19+
) {
20+
21+
}
22+
23+
public function validate(string $fieldName, string $value):void {
24+
$rule = $this->rulesService->getRule($fieldName);
25+
$value = trim($value);
26+
$length = strlen($value);
27+
if (!$length && isset($rule['required']) && $rule['required']) {
28+
throw new InvalidArgumentException(
29+
$this->l10n->t("Parameter '%s' is required!", [$fieldName])
30+
);
31+
}
32+
if ($length > $rule['max'] || $length < $rule['min']) {
33+
throw new InvalidArgumentException(
34+
$this->l10n->t("Parameter '%s' should be betweeen %s and %s.", [$fieldName, $rule['min'], $rule['max']])
35+
);
36+
}
37+
}
38+
39+
public function validateNames(array $names) {
40+
foreach ($names as $item) {
41+
if (empty($item['id'])) {
42+
throw new InvalidArgumentException('Parameter id is required!');
43+
}
44+
$this->validate($item['id'], $item['value']);
45+
}
46+
}
47+
48+
}

src/helpers/certification.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ export function selectCustonOption(id) {
2121
export const options = [
2222
{
2323
id: 'CN',
24-
label: t('libresign', 'Name (CN)'),
24+
label: t('libresign', 'Common Name (CN)'),
2525
max: 64,
2626
value: '',
27-
helperText: t('libresign', 'Name (CN)'),
27+
helperText: t('libresign', 'Common Name (CN)'),
2828
},
2929
{
3030
id: 'C',

src/views/Settings/RootCertificateCfssl.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<table class="grid">
1212
<tbody>
1313
<tr>
14-
<td>{{ t('libresign', 'Name (CN)') }}</td>
14+
<td>{{ t('libresign', 'Common Name (CN)') }}</td>
1515
<td>{{ certificate.rootCert.commonName }}</td>
1616
</tr>
1717
<tr v-for="(customName) in certificate.rootCert.names" :key="customName.id" class="customNames">
@@ -57,7 +57,7 @@
5757
</div>
5858
<div v-else id="formRootCertificate" class="form-libresign">
5959
<div class="form-group">
60-
<label for="commonName" class="form-heading--required">{{ t('libresign', 'Name (CN)') }}</label>
60+
<label for="commonName" class="form-heading--required">{{ t('libresign', 'Common Name (CN)') }}</label>
6161
<NcTextField id="commonName"
6262
ref="commonName"
6363
v-model="certificate.rootCert.commonName"

src/views/Settings/RootCertificateOpenSsl.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<table class="grid">
1212
<tbody>
1313
<tr>
14-
<td>{{ t('libresign', 'Name (CN)') }}</td>
14+
<td>{{ t('libresign', 'Common Name (CN)') }}</td>
1515
<td>{{ certificate.rootCert.commonName }}</td>
1616
</tr>
1717
<tr v-for="(customName) in certificate.rootCert.names" :key="customName.id" class="customNames">
@@ -53,7 +53,7 @@
5353
</div>
5454
<div v-else id="formRootCertificateOpenSsl" class="form-libresign">
5555
<div class="form-group">
56-
<label for="commonName" class="form-heading--required">{{ t('libresign', 'Name (CN)') }}</label>
56+
<label for="commonName" class="form-heading--required">{{ t('libresign', 'Common Name (CN)') }}</label>
5757
<NcTextField id="commonName"
5858
ref="commonName"
5959
v-model="certificate.rootCert.commonName"

tests/integration/features/admin/certificate_openssl.feature

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,24 @@ Feature: admin/certificate_openssl
1212
| (jq).ocs.data.rootCert.names\|length | 0 |
1313
| (jq).ocs.data.generated | true |
1414

15+
Scenario: Generate root cert with fail without CN
16+
Given as user "admin"
17+
When sending "post" to ocs "/apps/libresign/api/v1/admin/certificate/openssl"
18+
| rootCert | {"commonName":""} |
19+
Then the response should have a status code 401
20+
And the response should be a JSON array with the following mandatory values
21+
| key | value |
22+
| (jq).ocs.data.message | Parameter 'CN' is required! |
23+
24+
Scenario: Generate root cert with a big CN
25+
Given as user "admin"
26+
When sending "post" to ocs "/apps/libresign/api/v1/admin/certificate/openssl"
27+
| rootCert | {"commonName":"0123456789012345678901234567890123456789012345678901234567890123456789"} |
28+
Then the response should have a status code 401
29+
And the response should be a JSON array with the following mandatory values
30+
| key | value |
31+
| (jq).ocs.data.message | Parameter 'CN' should be betweeen 1 and 64. |
32+
1533
Scenario: Generate root cert with success using optional names values
1634
Given as user "admin"
1735
When sending "post" to ocs "/apps/libresign/api/v1/admin/certificate/openssl"
@@ -26,3 +44,21 @@ Feature: admin/certificate_openssl
2644
| (jq).ocs.data.rootCert.names[0].id | C |
2745
| (jq).ocs.data.rootCert.names[0].value | BR |
2846
| (jq).ocs.data.generated | true |
47+
48+
Scenario: Generate root cert with fail when country have less then 2 characters
49+
Given as user "admin"
50+
When sending "post" to ocs "/apps/libresign/api/v1/admin/certificate/openssl"
51+
| rootCert | {"commonName":"Common Name","names":[{"id": "C","value":"B"}]} |
52+
Then the response should have a status code 401
53+
And the response should be a JSON array with the following mandatory values
54+
| key | value |
55+
| (jq).ocs.data.message | Parameter 'C' should be betweeen 2 and 2. |
56+
57+
Scenario: Generate root cert with fail when country have more then 2 characters
58+
Given as user "admin"
59+
When sending "post" to ocs "/apps/libresign/api/v1/admin/certificate/openssl"
60+
| rootCert | {"commonName":"Common Name","names":[{"id": "C","value":"BRA"}]} |
61+
Then the response should have a status code 401
62+
And the response should be a JSON array with the following mandatory values
63+
| key | value |
64+
| (jq).ocs.data.message | Parameter 'C' should be betweeen 2 and 2. |

0 commit comments

Comments
 (0)