Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions docs/onboarding/Configuration-Properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ The Onboarding Server uses the following public configuration properties:
| `enrollment-server-onboarding.document-verification.checkInProgressDocumentSubmits` | `0/5 * * * * *` | Cron scheduler for checking status of submitted documents. |
| `enrollment-server-onboarding.document-verification.checkDocumentsVerifications.cron` | `0/5 * * * * *` | Cron scheduler for checking pending document verifications. |
| `enrollment-server-onboarding.document-verification.checkDocumentSubmitVerifications.cron` | `0/5 * * * * *` | Cron scheduler for checking document submit verifications. |
| `enrollment-server-onboarding.document-verification.required.primaryDocuments` | `ID_CARD` | Required primary document types to be present. Possible values: `ID_CARD`, `PASSPORT` |
| `enrollment-server-onboarding.document-verification.required.count` | `2` | Required count of documents to be present. |

## Presence Check Provider Configuration

Expand Down
18 changes: 13 additions & 5 deletions docs/onboarding/Database-Structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,17 +238,25 @@ The configuration is stored as JSON. See the following example:
"otpForIdentification": true,
"otpForIdentityVerification": true,
"documents": {
"requiredDocumentsCount": 2,
"requiredTotalDocumentsCount": 2,
"requiredPrimaryDocumentsCount": 1,
"mandatory": [
"ID_CARD"
],
"primary": [
"PASSPORT"
],
"secondary": [
"DRIVING_LICENCE"
],
"items": [
{
"type": "ID_CARD",
"sideCount": 2,
"mandatory": true
"sideCount": 2
},
{
"type": "DRIVING_LICENCE",
"sideCount": 1,
"mandatory": false
"sideCount": 1
}
]
}
Expand Down
19 changes: 19 additions & 0 deletions docs/onboarding/PowerAuth-Enrollment-Onboarding-Server-2.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ Also added a foreign key `process_config_id` to the table `es_onboarding_process

You have to insert at least one row into the table `es_onboarding_process_configuration`, and configure property `enrollment-server-onboarding.onboarding-process.default-type` (or ENV `ONBOARDING_PROCESS_DEFAULT_TYPE`) to work as a default process type.

Following configuration properties were removed:

```
enrollment-server-onboarding.document-verification.required.primaryDocuments
enrollment-server-onboarding.document-verification.required.count
```

New logic works in this way:

Field `documents.mandatory` - all document types from this list are mandatory. Verification process won't pass if any of these is missing. Empty by default.

Field `documents.primary` - list of primary document types. Minimum count of provided documents from this list is specified in field `documents.requiredPrimaryDocumentsCount`. Empty by default.

Field `documents.secondary` - list of secondary document types. Complement to primary documents in order to reach `documents.requiredTotalDocumentsCount`. Contains all supported document types by default.

Field `documents.requiredTotalDocumentsCount` - total minimum count of documents required for verification. It is sum of all unique values (document types) from `documents.mandatory`, `documents.primary` and `documents.secondary`. Default is `0`.

Field `documents.requiredPrimaryDocumentsCount` - minimum count of primary documents required for verification. Default is `0`.


## REST API Changes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import lombok.Builder;

import java.util.List;
import java.util.Set;

/**
* Configuration response.
Expand All @@ -45,8 +46,30 @@ public record ConfigurationResponse(

@Builder
public record Documents(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@banterCZ @kober32 please check these changes in the endpoint. It is according to the structure mentioned in #1435 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine, let's wait for @kober32 review.

@Schema(description = "Number of required documents to submit.", requiredMode = Schema.RequiredMode.REQUIRED, minimum = "1")
int requiredDocumentsCount,

@Schema(description = "Number of required documents to submit. It is sum of all unique values from `mandatory`, `primary` and `secondary` sets.", requiredMode = Schema.RequiredMode.REQUIRED, minimum = "1")
int requiredTotalDocumentsCount,

@Schema(description = "Number of required `primary` documents to submit.", requiredMode = Schema.RequiredMode.REQUIRED)
int requiredPrimaryDocumentsCount,

@Schema(
description = "Document types that must be always provided for successful verification.",
requiredMode = Schema.RequiredMode.REQUIRED,
defaultValue = "[]")
Set<DocumentType> mandatory,

@Schema(
description = "Primary document types. Minimum number of documents from this set for successful verification is set in `requiredPrimaryDocumentsCount`.",
requiredMode = Schema.RequiredMode.REQUIRED,
defaultValue = "[]")
Set<DocumentType> primary,

@Schema(description = "Secondary document types which are allowed for verification.",
requiredMode = Schema.RequiredMode.REQUIRED,
defaultValue = "all values from `DocumentType` enum")
Set<DocumentType> secondary,

List<Document> items
) {
}
Expand All @@ -57,9 +80,6 @@ public record Document(
@Schema(description = "Document type.", requiredMode = Schema.RequiredMode.REQUIRED)
DocumentType type,

@Schema(description = "Whether the document is mandatory.", requiredMode = Schema.RequiredMode.REQUIRED)
boolean mandatory,

@Schema(description = "Number of document sides.", requiredMode = Schema.RequiredMode.REQUIRED)
byte sideCount
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Set;

/**
* Represent JSON with the configuration stored in {@link OnboardingProcessConfigurationEntity#getConfiguration()}.
Expand Down Expand Up @@ -51,37 +52,56 @@ public static class OnboardingProcessConfigurationValueBuilder {
enabled = false;
otpForIdentification = false;
otpForIdentityVerification = false;
documents = new Documents((byte) 0, List.of());
documents = Documents.builder().build();
activationType = ActivationType.IDENTITY;
}
}

/**
* @param requiredDocumentsCount Number of required documents to submit.
* @param requiredTotalDocumentsCount Number of required documents to submit.
*/
@Jacksonized
@Builder
public record Documents(
byte requiredDocumentsCount,
byte requiredTotalDocumentsCount,
byte requiredPrimaryDocumentsCount,
Set<DocumentType> mandatory,
Set<DocumentType> primary,
Set<DocumentType> secondary,
List<Document> items
) implements Serializable {

@Serial
private static final long serialVersionUID = 1968756136278137531L;

public static class DocumentsBuilder {
DocumentsBuilder() {
requiredTotalDocumentsCount = 0;
requiredPrimaryDocumentsCount = 0;
mandatory = Set.of();
primary = Set.of();
secondary = Set.of(DocumentType.values());
items = List.of();
}
}
}

/**
* Configuration of a single documentation type.
*
* @param type document type
* @param mandatory whether the document is mandatory
* @param sideCount info if the document contains one or two sides
*/
@Jacksonized
@Builder
public record Document(
DocumentType type,
boolean mandatory,
byte sideCount
) implements Serializable {

@Serial
private static final long serialVersionUID = 191805503079489928L;

}

public enum DocumentType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;

import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;

/**
Expand All @@ -52,17 +54,19 @@ void testJsonMapping() {

final var documents = result.documents();
assertNotNull(documents);
assertEquals(2, documents.requiredDocumentsCount());
assertEquals(2, documents.requiredTotalDocumentsCount());
assertEquals(1, documents.requiredPrimaryDocumentsCount());
assertEquals(Set.of(OnboardingProcessConfigurationValue.DocumentType.ID_CARD), documents.mandatory());
assertEquals(Set.of(OnboardingProcessConfigurationValue.DocumentType.PASSPORT), documents.primary());
assertEquals(Set.of(OnboardingProcessConfigurationValue.DocumentType.DRIVING_LICENCE), documents.secondary());

final var document1 = documents.items().get(0);
assertEquals("ID_CARD", document1.type().name());
assertEquals(2, document1.sideCount());
assertTrue(document1.mandatory());

final var document2 = documents.items().get(1);
assertEquals("DRIVING_LICENCE", document2.type().name());
assertEquals(1, document2.sideCount());
assertFalse(document2.mandatory());
assertEquals(OnboardingProcessConfigurationValue.ActivationType.CODE, result.activationType());
}

Expand All @@ -77,7 +81,11 @@ void testJsonMapping_defaultValues() {

final var documents = result.documents();
assertNotNull(documents);
assertEquals(0, documents.requiredDocumentsCount());
assertEquals(0, documents.requiredTotalDocumentsCount());
assertEquals(0, documents.requiredPrimaryDocumentsCount());
assertEquals(Set.of(), documents.mandatory());
assertEquals(Set.of(), documents.primary());
assertEquals(Set.of(OnboardingProcessConfigurationValue.DocumentType.values()), documents.secondary());
assertEquals(0, documents.items().size());
assertEquals(OnboardingProcessConfigurationValue.ActivationType.IDENTITY, result.activationType());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
INSERT INTO es_onboarding_process_configuration (id, process_type, config) VALUES
(1, 'reactivation', '{"enabled":true,"activationType":"CODE","otpForIdentification":true,"otpForIdentityVerification":true,"documents":{"requiredDocumentsCount":2,"items":[{"type":"ID_CARD","sideCount":2,"mandatory":true},{"type":"DRIVING_LICENCE","sideCount":1,"mandatory":false}]}}'),
(1, 'reactivation', '{"enabled":true,"activationType":"CODE","otpForIdentification":true,"otpForIdentityVerification":true,"documents":{"requiredTotalDocumentsCount":2,"requiredPrimaryDocumentsCount":"1","mandatory":["ID_CARD"],"primary":["PASSPORT"],"secondary":["DRIVING_LICENCE"],"items":[{"type":"ID_CARD","sideCount":2},{"type":"DRIVING_LICENCE","sideCount":1}]}}'),
(2, 'onboarding', '{}');
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Configuration controller.
Expand Down Expand Up @@ -101,7 +103,11 @@ private static ConfigurationResponse.Documents convert(final OnboardingProcessCo
}

return ConfigurationResponse.Documents.builder()
.requiredDocumentsCount(source.requiredDocumentsCount())
.requiredTotalDocumentsCount(source.requiredTotalDocumentsCount())
.requiredPrimaryDocumentsCount(source.requiredPrimaryDocumentsCount())
.mandatory(convert(source.mandatory()))
.primary(convert(source.primary()))
.secondary(convert(source.secondary()))
.items(convert(source.items()))
.build();
}
Expand All @@ -119,11 +125,20 @@ private static List<ConfigurationResponse.Document> convert(final List<Onboardin
private static ConfigurationResponse.Document convert(final OnboardingProcessConfigurationValue.Document source) {
return ConfigurationResponse.Document.builder()
.type(convert(source.type()))
.mandatory(source.mandatory())
.sideCount(source.sideCount())
.build();
}

private static Set<ConfigurationResponse.DocumentType> convert(final Set<OnboardingProcessConfigurationValue.DocumentType> source) {
if (source == null) {
return Set.of();
}

return source.stream()
.map(ConfigurationController::convert)
.collect(Collectors.toSet());
}

private static ConfigurationResponse.DocumentType convert(final OnboardingProcessConfigurationValue.DocumentType source) {
return switch (source) {
case ID_CARD -> ConfigurationResponse.DocumentType.ID_CARD;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* PowerAuth Enrollment Server
* Copyright (C) 2025 Wultra s.r.o.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.wultra.app.onboardingserver.impl.service;

import com.wultra.app.onboardingserver.common.database.OnboardingProcessRepository;
import com.wultra.app.onboardingserver.common.database.entity.OnboardingProcessConfigurationEntity;
import com.wultra.app.onboardingserver.common.database.entity.OnboardingProcessConfigurationValue;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Optional;

/**
* TODO
*
* @author Michal Rozehnal, michal.rozehnal@wultra.com
*/
@Service
@AllArgsConstructor
public class OnboardingProcessConfigurationService {

private final OnboardingProcessRepository onboardingProcessRepository;

public OnboardingProcessConfigurationValue findConfigByProcessId(final String processId) {
final var onboardingProcess = onboardingProcessRepository.findById(processId)
.orElseThrow(() -> new IllegalArgumentException("Onboarding process not found for id: " + processId));

return Optional.ofNullable(onboardingProcess.getProcessConfiguration())
.map(OnboardingProcessConfigurationEntity::getConfiguration)
.orElseThrow(() -> new IllegalArgumentException("Onboarding process configuration not found for process id: " + processId));
}

}

This file was deleted.

Loading
Loading