├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── 01_bug.md
│ ├── 02_feature_request.md
│ ├── 03_enhancement.md
│ ├── 04_question.md
│ ├── 05_other.md
│ └── config.yml
└── workflows
│ ├── ci-dockerfile.yml
│ ├── ci-master.yml
│ ├── ci-pull-request.yml
│ └── ci-release-notes.yml
├── .gitignore
├── .grenrc.js
├── CHANGELOG.md
├── CODEOWNERS
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── Jenkinsfile
├── LICENSE
├── NOTICE
├── README.md
├── SECURITY.md
├── THIRD-PARTY-NOTICES
├── api-docs.json
├── codestyle
└── checkstyle.xml
├── docs
├── architecture-overview.md
├── architecture_overview.svg
└── sw_design_verification.svg
├── pom.xml
├── scripts
└── Dpkg.java
├── src
├── main
│ ├── java
│ │ └── app
│ │ │ └── coronawarn
│ │ │ └── verification
│ │ │ ├── VerificationApplication.java
│ │ │ ├── client
│ │ │ ├── IamClient.java
│ │ │ ├── TestResultServerClient.java
│ │ │ └── TestResultServerClientConfig.java
│ │ │ ├── config
│ │ │ ├── LocalSecurityConfig.java
│ │ │ ├── MtlsSecurityConfig.java
│ │ │ ├── OpenApiConfig.java
│ │ │ ├── RequestSizeLimitFilter.java
│ │ │ └── VerificationApplicationConfig.java
│ │ │ ├── controller
│ │ │ ├── ExternalTanController.java
│ │ │ ├── ExternalTestStateController.java
│ │ │ ├── ExternalTokenController.java
│ │ │ ├── InternalTanController.java
│ │ │ ├── InternalTestStateController.java
│ │ │ └── VerificationExceptionHandler.java
│ │ │ ├── domain
│ │ │ ├── VerificationAppSession.java
│ │ │ └── VerificationTan.java
│ │ │ ├── exception
│ │ │ └── VerificationServerException.java
│ │ │ ├── model
│ │ │ ├── AppSessionSourceOfTrust.java
│ │ │ ├── AuthorizationRole.java
│ │ │ ├── AuthorizationToken.java
│ │ │ ├── Certs.java
│ │ │ ├── HashedGuid.java
│ │ │ ├── InternalTestResult.java
│ │ │ ├── Key.java
│ │ │ ├── LabTestResult.java
│ │ │ ├── RegistrationToken.java
│ │ │ ├── RegistrationTokenKeyType.java
│ │ │ ├── RegistrationTokenRequest.java
│ │ │ ├── Tan.java
│ │ │ ├── TanSourceOfTrust.java
│ │ │ ├── TanType.java
│ │ │ ├── TeleTan.java
│ │ │ ├── TeleTanType.java
│ │ │ └── TestResult.java
│ │ │ ├── repository
│ │ │ ├── VerificationAppSessionRepository.java
│ │ │ └── VerificationTanRepository.java
│ │ │ ├── service
│ │ │ ├── AppSessionService.java
│ │ │ ├── EntitiesCleanupService.java
│ │ │ ├── FakeDelayService.java
│ │ │ ├── FakeRequestService.java
│ │ │ ├── HashingService.java
│ │ │ ├── JwtService.java
│ │ │ ├── TanService.java
│ │ │ └── TestResultServerService.java
│ │ │ └── validator
│ │ │ ├── RegistrationTokenKeyConstraint.java
│ │ │ └── RegistrationTokenRequestValidator.java
│ └── resources
│ │ ├── application-cloud.yml
│ │ ├── application-external.yml
│ │ ├── application-internal.yml
│ │ ├── application-local.yml
│ │ ├── application.yml
│ │ ├── bootstrap-cloud.yaml
│ │ ├── bootstrap.yaml
│ │ └── db
│ │ ├── changelog.yml
│ │ └── changelog
│ │ ├── v000-create-app-session-table.yml
│ │ ├── v000-create-tan-table.yml
│ │ ├── v001-add-dob-hash-column.yml
│ │ ├── v002-add-teletan-type-column.yml
│ │ ├── v003-add-unique-hashed-guid.yml
│ │ ├── v004-add-unique-registration-token-teletan.yml
│ │ ├── v005-add-shedlock.yml
│ │ ├── v006-add-index-dob-hashed-guid.yml
│ │ └── v007-add-seperate-unique-constraints-for-hashed-guid.yml
└── test
│ └── java
│ └── app
│ └── coronawarn
│ └── verification
│ ├── TestUtils.java
│ ├── VerificationApplicationExternalTest.java
│ ├── VerificationApplicationExternalTestHttp.java
│ ├── VerificationApplicationExternalTestWorkaround.java
│ ├── VerificationApplicationInternalTest.java
│ └── service
│ ├── EntitiesCleanupServiceTest.java
│ ├── HashingServiceTest.java
│ ├── JwtServiceTest.java
│ ├── TanServiceTest.java
│ └── TestResultServerServiceTest.java
└── trusted.key.gpg
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
3 | *.java text diff=java
4 | *.gradle text diff=groovy
5 | *.gradle.kts text diff=groovy
6 | *.css text diff=css
7 | *.df text
8 | *.htm text diff=html
9 | *.html text diff=html
10 | *.js text
11 | *.jsp text
12 | *.jspf text
13 | *.jspx text
14 | *.properties text
15 | *.tld text
16 | *.tag text
17 | *.tagx text
18 | *.xml text
19 |
20 | *.class binary
21 | *.dll binary
22 | *.ear binary
23 | *.jar binary
24 | *.so binary
25 | *.war binary
26 | *.jks binary
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/01_bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F6A8 Bug"
3 | about: Did you come across a bug or unexpected behaviour differing from the docs?
4 | labels: bug
5 | ---
6 |
7 |
13 |
14 | ## Describe the bug
15 |
16 |
17 |
18 | ## Expected behaviour
19 |
20 |
21 |
22 | ## Steps to reproduce the issue
23 |
24 |
25 |
26 |
32 |
33 | ## Technical details
34 |
35 | - Host Machine OS (Windows/Linux/Mac):
36 |
37 | ## Possible Fix
38 |
39 |
40 |
41 | ## Additional context
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/02_feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F381 Feature Request"
3 | about: Do you have an idea for a new feature?
4 | labels: feature request
5 | ---
6 |
7 |
13 |
14 | ## Feature description
15 |
16 |
21 |
22 | ## Problem and motivation
23 |
24 |
28 |
29 | ## Is this something you're interested in working on
30 |
31 |
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/03_enhancement.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\u23F1\uFE0F Enhancement"
3 | about: Do you have an idea for an enhancement?
4 | labels: enhancement
5 | ---
6 |
7 |
13 |
14 | ## Current Implementation
15 |
16 |
17 |
18 | ## Suggested Enhancement
19 |
20 |
24 |
25 | ## Expected Benefits
26 |
27 |
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/04_question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U00002753 Question"
3 | about: If you have questions about pieces of the code or documentation for this component, please post them here.
4 | labels: question
5 | ---
6 |
7 |
13 |
14 | ## Your Question
15 |
16 |
17 |
18 | * Source File:
19 | * Line(s):
20 | * Question:
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/05_other.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F4AC Other"
3 | about: For conceptual questions, please consider opening an issue in the documentation repository.
4 | labels: other
5 | ---
6 |
7 |
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/workflows/ci-dockerfile.yml:
--------------------------------------------------------------------------------
1 | name: ci-dockerfile
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - Dockerfile
9 | pull_request:
10 | types:
11 | - opened
12 | - synchronize
13 | - reopened
14 | paths:
15 | - Dockerfile
16 | jobs:
17 | lint:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: actions/checkout@v2
21 | - run: docker pull hadolint/hadolint
22 | - run: docker run --rm --interactive hadolint/hadolint < Dockerfile
23 |
--------------------------------------------------------------------------------
/.github/workflows/ci-master.yml:
--------------------------------------------------------------------------------
1 | name: ci-master
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - master
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: sigstore/cosign-installer@main
12 | - uses: actions/checkout@v2
13 | with:
14 | fetch-depth: 0
15 | - uses: actions/cache@v1
16 | env:
17 | cache-name: m2
18 | with:
19 | path: ~/.m2/repository
20 | key: ${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }}
21 | restore-keys: ${{ env.cache-name }}-
22 | - uses: actions/setup-java@v1
23 | with:
24 | java-version: 17
25 | - name: environment
26 | run: |
27 | sudo apt-get install --yes --no-install-recommends libxml-xpath-perl
28 | export ARTIFACT_ID=$(xpath -q -e "/project/artifactId/text()" pom.xml)
29 | echo "ARTIFACT_ID=${ARTIFACT_ID}" >> $GITHUB_ENV
30 | export VERSION=$(xpath -q -e "/project/version/text()" pom.xml)
31 | export VERSION=${VERSION//-SNAPSHOT}-$(git rev-parse --short ${GITHUB_SHA})
32 | echo "VERSION=${VERSION}" >> $GITHUB_ENV
33 | - name: mvn version
34 | run: mvn --batch-mode versions:set -DgenerateBackupPoms=false -DnewVersion=${VERSION}
35 | env:
36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37 | - name: mvn deploy
38 | run: mvn --batch-mode deploy
39 | env:
40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41 | - name: mvn sonar
42 | run: |
43 | mvn --batch-mode verify sonar:sonar \
44 | -Dsonar.login=${SONAR_TOKEN} \
45 | -Dsonar.host.url=${SONAR_URL} \
46 | -Dsonar.organization=${GITHUB_REPOSITORY_OWNER} \
47 | -Dsonar.projectKey=${GITHUB_REPOSITORY/\//_}
48 | env:
49 | SONAR_URL: https://sonarcloud.io
50 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 | - uses: actions/upload-artifact@v1
53 | with:
54 | name: target
55 | path: target
56 | - name: docker build
57 | run: |
58 | docker build \
59 | --tag docker.pkg.github.com/${GITHUB_REPOSITORY}/${ARTIFACT_ID}:${VERSION} \
60 | --tag ${MTR_REPOSITORY}/cwa-verification-server:${VERSION} \
61 | .
62 | env:
63 | MTR_REPOSITORY: ${{ secrets.MTR_REPOSITORY }}
64 | - name: docker push github
65 | run: |
66 | echo ${GITHUB_TOKEN} | docker login docker.pkg.github.com -u ${GITHUB_REPOSITORY_OWNER} --password-stdin
67 | docker push docker.pkg.github.com/${GITHUB_REPOSITORY}/${ARTIFACT_ID}:${VERSION}
68 | env:
69 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70 | - name: docker push mtr
71 | run: |
72 | echo ${MTR_TOKEN} | docker login ${MTR_REPOSITORY} -u ${MTR_USER} --password-stdin
73 | docker push ${MTR_REPOSITORY}/cwa-verification-server:${VERSION}
74 | cosign sign --key env://MTR_PRIVATE_KEY ${MTR_REPOSITORY}/cwa-verification-server:${VERSION}
75 | env:
76 | MTR_REPOSITORY: ${{ secrets.MTR_REPOSITORY }}
77 | MTR_USER: ${{ secrets.MTR_USER }}
78 | MTR_TOKEN: ${{ secrets.MTR_TOKEN }}
79 | MTR_PRIVATE_KEY: ${{ secrets.MTR_PRIVATE_KEY }}
80 | COSIGN_PASSWORD: ${{ secrets.MTR_PRIVATE_KEY_PASSWORD }}
81 |
--------------------------------------------------------------------------------
/.github/workflows/ci-pull-request.yml:
--------------------------------------------------------------------------------
1 | name: ci-pull-request
2 | on:
3 | pull_request:
4 | types:
5 | - opened
6 | - synchronize
7 | - reopened
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | with:
14 | fetch-depth: 0
15 | - uses: actions/cache@v1
16 | env:
17 | cache-name: m2
18 | with:
19 | path: ~/.m2/repository
20 | key: ${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }}
21 | restore-keys: ${{ env.cache-name }}-
22 | - uses: actions/setup-java@v1
23 | with:
24 | java-version: 17
25 | - name: mvn package
26 | run: mvn --batch-mode package
27 | env:
28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29 | - name: docker build
30 | run: docker build .
31 |
--------------------------------------------------------------------------------
/.github/workflows/ci-release-notes.yml:
--------------------------------------------------------------------------------
1 | name: ci-release-notes
2 | on:
3 | release:
4 | types:
5 | - created
6 | - edited
7 | - prereleased
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - name: release notes
14 | run: |
15 | TAG=${GITHUB_REF/refs\/tags\/}
16 | npx github-release-notes release --override --tags ${TAG}
17 | env:
18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 | GREN_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20 |
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**
4 | !**/src/test/**
5 |
6 | ### STS ###
7 | .apt_generated
8 | .classpath
9 | .factorypath
10 | .project
11 | .settings
12 | .springBeans
13 | .sts4-cache
14 |
15 | ### IntelliJ IDEA ###
16 | .idea
17 | *.iws
18 | *.iml
19 | *.ipr
20 |
21 | ### NetBeans ###
22 | /nbproject/private/
23 | /nbbuild/
24 | /dist/
25 | /nbdist/
26 | /.nb-gradle/
27 | build/
28 | nbactions.txt
29 | nbactions.xml
30 |
31 | ### VS Code ###
32 | .vscode/
33 |
34 | /nb-configuration.xml
35 | /nbproject/
36 |
--------------------------------------------------------------------------------
/.grenrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "dataSource": "prs",
3 | "prefix": "",
4 | "onlyMilestones": false,
5 | "groupBy": {
6 | "Enhancements": [
7 | "enhancement",
8 | "internal",
9 | "feat",
10 | "feature",
11 | "code improvement"
12 | ],
13 | "Bug Fixes": [
14 | "fix",
15 | "bug"
16 | ],
17 | "Documentation": [
18 | "doc",
19 | "documentation"
20 | ],
21 | "Others": [
22 | "other",
23 | "chore"
24 | ]
25 | },
26 | "ignoreIssuesWith": [
27 | "wontfix",
28 | "duplicate"
29 | ],
30 | "changelogFilename": "CHANGELOG.md",
31 | "template": {
32 | commit: ({ message, url, author, name }) => `- [${message}](${url}) - ${author ? `@${author}` : name}`,
33 | issue: "- {{name}} [{{text}}]({{url}})",
34 | noLabel: "other",
35 | group: "\n#### {{heading}}\n",
36 | changelogTitle: "# Changelog\n\n",
37 | release: "## {{release}} ({{date}})\n{{body}}",
38 | releaseSeparator: "\n---\n\n"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## v1.2.2 (05/08/2020)
4 |
5 | #### Enhancements
6 |
7 | - feat: changed Responsepadding to only appear if fake header exists [#197](https://github.com/corona-warn-app/cwa-verification-server/pull/197)
8 |
9 | #### Bug Fixes
10 |
11 | - fix: added json ignore if null [#198](https://github.com/corona-warn-app/cwa-verification-server/pull/198)
12 | - fix: used corret fake delay [#200](https://github.com/corona-warn-app/cwa-verification-server/pull/200)
13 | - fix: added delay for all endpoints [#201](https://github.com/corona-warn-app/cwa-verification-server/pull/201)
14 |
15 | ---
16 |
17 | ## v1.2.1 (27/07/2020)
18 |
19 | #### Bug Fixes
20 |
21 | - fix: temporary remove response padding [#194](https://github.com/corona-warn-app/cwa-verification-server/pull/194)
22 | - fix: update openAPI docs to meet v1.2.0 [#192](https://github.com/corona-warn-app/cwa-verification-server/pull/192)
23 |
24 | #### Others
25 |
26 | - doc: documentation of feature 'fake header' [#191](https://github.com/corona-warn-app/cwa-verification-server/pull/191)
27 |
28 | ---
29 |
30 | ## v1.2.0 (22/07/2020)
31 |
32 | #### Enhancements
33 |
34 | - feat: feature plausible deniability (aka fake header) [#188](https://github.com/corona-warn-app/cwa-verification-server/pull/188)
35 | - feat: added rate limiting for TeleTan creation [#172](https://github.com/corona-warn-app/cwa-verification-server/pull/172)
36 | - feat: split internal and external endpoints [#184](https://github.com/corona-warn-app/cwa-verification-server/pull/184)
37 |
38 | #### Bug Fixes
39 |
40 | - fix: add a simple filter which prevents BSI issue #55 (request size limitation) [#91](https://github.com/corona-warn-app/cwa-verification-server/pull/91)
41 |
42 | #### Documentation
43 |
44 | - doc: rate limit, teleTAN crypto spec [#187](https://github.com/corona-warn-app/cwa-verification-server/pull/187)
45 |
46 | ---
47 |
48 | ## v1.1.0 (07/07/2020)
49 |
50 | #### Enhancements
51 |
52 | - feat: improve logs for cdc and increase spring version [#178](https://github.com/corona-warn-app/cwa-verification-server/pull/178)
53 | - feat: automatic release notes generation [#175](https://github.com/corona-warn-app/cwa-verification-server/pull/175)
54 | - feat: custom validator for Registration Token Request [#169](https://github.com/corona-warn-app/cwa-verification-server/pull/169)
55 | - feat: use license-maven-plugin to allow generation of license file headers [#103](https://github.com/corona-warn-app/cwa-verification-server/pull/103)
56 | - feat: speed up unit tests [#167](https://github.com/corona-warn-app/cwa-verification-server/pull/167)
57 | - feat: improvement-logs-cdc [#177](https://github.com/corona-warn-app/cwa-verification-server/pull/177)
58 | - feat: improve logs regarding cdc [#155](https://github.com/corona-warn-app/cwa-verification-server/pull/155)
59 | - feat: removed unnecessary complexity [#165](https://github.com/corona-warn-app/cwa-verification-server/pull/165)
60 |
61 | #### Bug Fixes
62 |
63 | - fix: fix typos in architecture overview [#182](https://github.com/corona-warn-app/cwa-verification-server/pull/182)
64 | - fix: added Glen to codeowners, deleted DockerfilePaaS [#174](https://github.com/corona-warn-app/cwa-verification-server/pull/174)
65 | - fix: minor code quality improvements [#160](https://github.com/corona-warn-app/cwa-verification-server/pull/160)
66 | - fix: readme.md [#181](https://github.com/corona-warn-app/cwa-verification-server/pull/181)
67 | - fix: updated the release to 1.0.1 in POM-File [#171](https://github.com/corona-warn-app/cwa-verification-server/pull/171)
68 | - fix: enable container scanning of distroless container [#173](https://github.com/corona-warn-app/cwa-verification-server/pull/173)
69 | - fix: integration-JWT [#157](https://github.com/corona-warn-app/cwa-verification-server/pull/157)
70 |
71 | #### Documentation
72 |
73 | - doc: typo in API description [#170](https://github.com/corona-warn-app/cwa-verification-server/pull/170)
74 | - doc: corrected teleTAN specification in architecture-overview.md [#159](https://github.com/corona-warn-app/cwa-verification-server/pull/159)
75 | - doc: update architecture-overview.md [#180](https://github.com/corona-warn-app/cwa-verification-server/pull/180)
76 | - doc: update contact mail in documents [#176](https://github.com/corona-warn-app/cwa-verification-server/pull/176)
77 | - doc: reference 'Create Registration Token for E06.01' [#41](https://github.com/corona-warn-app/cwa-verification-server/pull/41)
78 | - doc: update figures to match solution architecture [#116](https://github.com/corona-warn-app/cwa-verification-server/pull/116)
79 |
80 | ---
81 |
82 | ## v1.0.0 (12/06/2020)
83 |
84 | #### Documentation
85 |
86 | - Update README [#156](https://github.com/corona-warn-app/cwa-verification-server/pull/156)
87 |
88 | ---
89 |
90 | ## v0.6.1 (09/06/2020)
91 | Release candidate
92 | * mTLS support fix
93 |
94 | ---
95 |
96 | ## 0.6.0 (08/06/2020)
97 | Release Candidate
98 | ---
99 |
100 | ## fix uppercase teleTan (01/06/2020)
101 |
102 | ---
103 |
104 | ## Beta API fix (31/05/2020)
105 |
106 | ---
107 |
108 | ## v0.5.1-beta (31/05/2020)
109 | v0.5.1-beta fo cwa-verification-server
110 | ---
111 |
112 | ## v0.3.1-alpha (22/05/2020)
113 | Initial public alpha release.
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # This file provides an overview of code owners in this repository.
2 |
3 | # Each line is a file pattern followed by one or more owners.
4 | # The last matching pattern has the most precedence.
5 | # For more details, read the following article on GitHub: https://help.github.com/articles/about-codeowners/.
6 |
7 | # These are the default owners for the whole content of this repository. The default owners are automatically added as reviewers when you open a pull request, unless different owners are specified in the file.
8 | * @corona-warn-app/cwa-verification-team
9 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 | # Contributor Covenant Code of Conduct
3 |
4 | ## Our Pledge
5 |
6 | We as members, contributors, and leaders pledge to make participation in our
7 | community a harassment-free experience for everyone, regardless of age, body
8 | size, visible or invisible disability, ethnicity, sex characteristics, gender
9 | identity and expression, level of experience, education, socio-economic status,
10 | nationality, personal appearance, race, religion, or sexual identity
11 | and orientation.
12 |
13 | We pledge to act and interact in ways that contribute to an open, welcoming,
14 | diverse, inclusive, and healthy community.
15 |
16 | ## Our Standards
17 |
18 | Examples of behavior that contributes to a positive environment for our
19 | community include:
20 |
21 | * Demonstrating empathy and kindness toward other people
22 | * Being respectful of differing opinions, viewpoints, and experiences
23 | * Giving and gracefully accepting constructive feedback
24 | * Accepting responsibility and apologizing to those affected by our mistakes,
25 | and learning from the experience
26 | * Focusing on what is best not just for us as individuals, but for the
27 | overall community
28 |
29 | Examples of unacceptable behavior include:
30 |
31 | * The use of sexualized language or imagery, and sexual attention or
32 | advances of any kind
33 | * Trolling, insulting or derogatory comments, and personal or political attacks
34 | * Public or private harassment
35 | * Publishing others' private information, such as a physical or email
36 | address, without their explicit permission
37 | * Other conduct which could reasonably be considered inappropriate in a
38 | professional setting
39 |
40 | ## Enforcement Responsibilities
41 |
42 | Community leaders are responsible for clarifying and enforcing our standards of
43 | acceptable behavior and will take appropriate and fair corrective action in
44 | response to any behavior that they deem inappropriate, threatening, offensive,
45 | or harmful.
46 |
47 | Community leaders have the right and responsibility to remove, edit, or reject
48 | comments, commits, code, wiki edits, issues, and other contributions that are
49 | not aligned to this Code of Conduct, and will communicate reasons for moderation
50 | decisions when appropriate.
51 |
52 | ## Scope
53 |
54 | This Code of Conduct applies within all community spaces, and also applies when
55 | an individual is officially representing the community in public spaces.
56 | Examples of representing our community include using an official e-mail address,
57 | posting via an official social media account, or acting as an appointed
58 | representative at an online or offline event.
59 |
60 | ## Enforcement
61 |
62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
63 | reported to the community leaders responsible for enforcement at
64 | [cwa-opensource@telekom.de](mailto:cwa-opensource@telekom.de).
65 | All complaints will be reviewed and investigated promptly and fairly.
66 |
67 | All community leaders are obligated to respect the privacy and security of the
68 | reporter of any incident.
69 |
70 | ## Enforcement Guidelines
71 |
72 | Community leaders will follow these Community Impact Guidelines in determining
73 | the consequences for any action they deem in violation of this Code of Conduct:
74 |
75 | ### 1. Correction
76 |
77 | **Community Impact**: Use of inappropriate language or other behavior deemed
78 | unprofessional or unwelcome in the community.
79 |
80 | **Consequence**: A private, written warning from community leaders, providing
81 | clarity around the nature of the violation and an explanation of why the
82 | behavior was inappropriate. A public apology may be requested.
83 |
84 | ### 2. Warning
85 |
86 | **Community Impact**: A violation through a single incident or series
87 | of actions.
88 |
89 | **Consequence**: A warning with consequences for continued behavior. No
90 | interaction with the people involved, including unsolicited interaction with
91 | those enforcing the Code of Conduct, for a specified period of time. This
92 | includes avoiding interactions in community spaces as well as external channels
93 | like social media. Violating these terms may lead to a temporary or
94 | permanent ban.
95 |
96 | ### 3. Temporary Ban
97 |
98 | **Community Impact**: A serious violation of community standards, including
99 | sustained inappropriate behavior.
100 |
101 | **Consequence**: A temporary ban from any sort of interaction or public
102 | communication with the community for a specified period of time. No public or
103 | private interaction with the people involved, including unsolicited interaction
104 | with those enforcing the Code of Conduct, is allowed during this period.
105 | Violating these terms may lead to a permanent ban.
106 |
107 | ### 4. Permanent Ban
108 |
109 | **Community Impact**: Demonstrating a pattern of violation of community
110 | standards, including sustained inappropriate behavior, harassment of an
111 | individual, or aggression toward or disparagement of classes of individuals.
112 |
113 | **Consequence**: A permanent ban from any sort of public interaction within
114 | the community.
115 |
116 | ## Attribution
117 |
118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119 | version 2.0, available at
120 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
121 |
122 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
123 | enforcement ladder](https://github.com/mozilla/diversity).
124 |
125 | [homepage]: https://www.contributor-covenant.org
126 |
127 | For answers to common questions about this code of conduct, see the FAQ at
128 | https://www.contributor-covenant.org/faq. Translations are available at
129 | https://www.contributor-covenant.org/translations.
130 |
131 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Code of conduct
4 |
5 | All members of the project community must abide by the [Contributor Covenant, version 2.0](CODE_OF_CONDUCT.md).
6 | Only by respecting each other can we develop a productive, collaborative community.
7 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting [cwa-opensource@telekom.de](mailto:cwa-opensource@telekom.de) and/or a project maintainer.
8 |
9 | We appreciate your courtesy of avoiding political questions here. Issues which are not related to the project itself will be closed by our community managers.
10 |
11 | ## Engaging in our project
12 |
13 | We use GitHub to manage reviews of pull requests.
14 |
15 | * If you are a new contributor, see: [Steps to Contribute](#steps-to-contribute)
16 |
17 | * If you have a trivial fix or improvement, go ahead and create a pull request, addressing (with `@...`) a suitable maintainer of this repository (see [CODEOWNERS](CODEOWNERS) of the repository you want to contribute to) in the description of the pull request.
18 |
19 | * If you plan to do something more involved, please reach out to us and send an [email](mailto:cwa-opensource@telekom.de). This will avoid unnecessary work and surely give you and us a good deal of inspiration.
20 |
21 | * Relevant coding style guidelines are available in the respective sub-repositories as they are programming language-dependent.
22 |
23 | ## Steps to Contribute
24 |
25 | Should you wish to work on an issue, please claim it first by commenting on the GitHub issue that you want to work on. This is to prevent duplicated efforts from other contributors on the same issue.
26 |
27 | If you have questions about one of the issues, please comment on them, and one of the maintainers will clarify.
28 |
29 | We kindly ask you to follow the [Pull Request Checklist](#Pull-Request-Checklist) to ensure reviews can happen accordingly.
30 |
31 | ## Contributing Code
32 |
33 | You are welcome to contribute code in order to fix a bug or to implement a new feature.
34 |
35 | The following rule governs code contributions:
36 |
37 | * Contributions must be licensed under the [Apache 2.0 License](LICENSE)
38 | * Newly created files must be opened by a *license file header* which can be generated with the command `mvn clean license:update-file-header`.
39 | * At least if you add a new file to the repository, add your name into the contributor section of the file NOTICE (please respect the preset entry structure)
40 |
41 | ## Contributing Documentation
42 |
43 | You are welcome to contribute documentation to the project.
44 |
45 | The following rule governs documentation contributions:
46 |
47 | * Contributions must be licensed under the same license as code, the [Apache 2.0 License](LICENSE)
48 |
49 | ## Pull Request Checklist
50 |
51 | * Branch from the master branch and, if needed, rebase to the current master branch before submitting your pull request. If it doesn't merge cleanly with master you may be asked to rebase your changes.
52 |
53 | * Commits should be as small as possible while ensuring that each commit is correct independently (i.e., each commit should compile and pass tests).
54 |
55 | * Test your changes as thoroughly as possible before you commit them. Preferably, automate your test by unit/integration tests. If tested manually, provide information about the test scope in the PR description (e.g. “Test passed: Upgrade version from 0.42 to 0.42.23.”).
56 |
57 | * Create _Work In Progress [WIP]_ pull requests only if you need clarification or an explicit review before you can continue your work item.
58 |
59 | * If your patch is not getting reviewed or you need a specific person to review it, you can @-reply a reviewer asking for a review in the pull request or a comment, or you can ask for a review by contacting us via [email](mailto:opensource@telekom.de).
60 |
61 | * Post review:
62 | * If a review requires you to change your commit(s), please test the changes again.
63 | * Amend the affected commit(s) and force push onto your branch.
64 | * Set respective comments in your GitHub review to resolved.
65 | * Create a general PR comment to notify the reviewers that your amendments are ready for another round of review.
66 |
67 | ## Issues and Planning
68 |
69 | * We use GitHub issues to track bugs and enhancement requests.
70 |
71 | * Please provide as much context as possible when you open an issue. The information you provide must be comprehensive enough to reproduce that issue for the assignee. Therefore, contributors may use but aren't restricted to the issue template provided by the project maintainers.
72 |
73 | * When creating an issue, try using one of our issue templates which already contain some guidelines on which content is expected to process the issue most efficiently. If no template applies, you can of course also create an issue from scratch.
74 |
75 | * Please apply one or more applicable [labels](https://github.com/corona-warn-app/cwa-documentation/labels) to your issue so that all community members are able to cluster the issues better.
76 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM gcr.io/distroless/java17-debian11:latest
2 | WORKDIR /
3 | COPY target/*.jar app.jar
4 | COPY scripts/Dpkg.java Dpkg.java
5 | RUN ["java", "Dpkg.java"]
6 | USER 65534:65534
7 | CMD ["app.jar"]
8 | EXPOSE 8080
9 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env groovy
2 | @Library('sbs-jenkinslib') _
3 |
4 | /*
5 | * T-Systems SBS pipeline build see below link for internal (sorry)
6 | * documentation details.
7 | * https://sbs.t-systems.com/wiki/Job+Type:+Jenkins+Pipeline+Build
8 | */
9 | sbsBuild(
10 | jdk: 'jdk11',
11 | dockerAlternateRegistries: [
12 | 'MTR_SBS@mtr.external.otc.telekomcloud.com/sbs/cwa-verification-server'
13 | ]
14 | )
15 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 Deutsche Telekom AG.
2 |
3 | This project is licensed under Apache License, Version 2.0;
4 | you may not use them except in compliance with the License.
5 |
6 | Contributors:
7 | -------------
8 |
9 | Karsten Reincke [kreincke], Deutsche Telekom AG
10 | Daniel Eder [daniel-eder], T-Mobile International Austria GmbH
11 | Dominik Fischer [dfischer-tech], T-Systems International GmbH
12 | Julien Hagestedt [jhagestedt], T-Systems International GmbH
13 | Maximilian Laue [mlaue-tech], T-Systems International GmbH
14 | Andreas Scheibal [ascheibal], T-Systems International GmbH
15 | Michael Schulte [mschulte-tsi], T-Systems International GmbH
16 | Lars Stelzner [lstelzne-tech], T-Systems International GmbH
17 | Andreas Mandel [amandel], T-Systems International GmbH
18 | Martin Scheffler [martinschefflerTSI] T-Systems International GmbH
19 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting Security Vulnerabilities
2 |
3 | The Corona-Warn-App is built with security and data privacy in mind to ensure your data is safe. We are grateful for security researchers and users reporting a vulnerability to us, first. To ensure that your request is handled in a timely manner and non-disclosure of vulnerabilities can be assured, please follow the below guideline.
4 |
5 | **Please do not report security vulnerabilities directly on GitHub. GitHub Issues can be publicly seen and therefore would result in a direct disclosure.**
6 |
7 | * Please address questions about data privacy, security concepts, and other media requests to the cert@telekom.de mailbox.
8 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | app.coronawarn.verification
6 | cwa-verification-server
7 | 1.5.9-SNAPSHOT
8 | jar
9 |
10 | cwa-verification-server
11 | CWA verification server project.
12 |
13 | T-Systems International GmbH
14 |
15 |
16 | https://www.coronawarn.app/
17 |
18 | https://github.com/corona-warn-app/cwa-verification-server/actions?query=workflow%3Aci
19 |
20 |
21 | https://github.com/corona-warn-app/cwa-verification-server/issues
22 |
23 |
24 | https://github.com/corona-warn-app/cwa-verification-server
25 |
26 |
27 |
28 | app.coronawarn
29 | cwa-parent
30 | 2.0.2
31 |
32 |
33 |
34 | Corona-Warn-App / cwa-verification
35 | 2020
36 | apache_v2
37 |
38 | **/VerificationApplication.java,
39 | **/model/*,
40 | **/domain/*,
41 | **/config/*,
42 | **/exception/*
43 |
44 |
45 |
46 |
47 |
48 | github
49 | https://maven.pkg.github.com/corona-warn-app/cwa-verification-server
50 |
51 |
52 |
53 |
54 |
55 | github
56 | https://maven.pkg.github.com/corona-warn-app/cwa-verification-server
57 |
58 |
59 |
60 |
61 |
62 | app.coronawarn
63 | cwa-parent-spring-boot
64 | ${project.parent.version}
65 | pom
66 |
67 |
68 | app.coronawarn
69 | cwa-parent-feign
70 | ${project.parent.version}
71 | pom
72 |
73 |
74 | app.coronawarn
75 | cwa-parent-psql-persistence
76 | ${project.parent.version}
77 | pom
78 |
79 |
80 | app.coronawarn
81 | cwa-parent-shedlock
82 | ${project.parent.version}
83 | pom
84 |
85 |
86 | app.coronawarn
87 | cwa-parent-validation
88 | ${project.parent.version}
89 | pom
90 |
91 |
92 |
93 | commons-io
94 | commons-io
95 |
96 |
97 | commons-codec
98 | commons-codec
99 |
100 |
101 | org.apache.commons
102 | commons-math3
103 | compile
104 |
105 |
106 |
107 | io.jsonwebtoken
108 | jjwt-api
109 |
110 |
111 | io.jsonwebtoken
112 | jjwt-impl
113 |
114 |
115 | io.jsonwebtoken
116 | jjwt-jackson
117 | runtime
118 |
119 |
120 |
121 | io.micrometer
122 | micrometer-core
123 |
124 |
125 | io.micrometer
126 | micrometer-registry-prometheus
127 |
128 |
129 |
130 |
131 |
132 |
133 | org.springframework.boot
134 | spring-boot-maven-plugin
135 |
136 |
137 |
138 | org.apache.maven.plugins
139 | maven-checkstyle-plugin
140 |
141 |
142 |
143 | org.jacoco
144 | jacoco-maven-plugin
145 |
146 |
147 |
148 | org.codehaus.mojo
149 | license-maven-plugin
150 |
151 |
152 |
153 | org.sonarsource.scanner.maven
154 | sonar-maven-plugin
155 |
156 |
157 |
158 |
159 |
--------------------------------------------------------------------------------
/scripts/Dpkg.java:
--------------------------------------------------------------------------------
1 | import java.io.*;
2 |
3 | class Dpkg {
4 | public static void main(String[] args) throws IOException {
5 | File dir = new File("/var/lib/dpkg/status.d/");
6 | PrintWriter pw = new PrintWriter("/var/lib/dpkg/status");
7 | String[] fileNames = dir.list();
8 | for (String fileName : fileNames) {
9 | System.out.println("Handling file: " + fileName);
10 | File f = new File(dir, fileName);
11 | BufferedReader br = new BufferedReader(new FileReader(f));
12 | String line = br.readLine();
13 | while (line != null) {
14 | pw.println(line);
15 | line = br.readLine();
16 | }
17 | pw.println();
18 | pw.flush();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/VerificationApplication.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification;
22 |
23 | import app.coronawarn.verification.config.VerificationApplicationConfig;
24 | import org.springframework.boot.SpringApplication;
25 | import org.springframework.boot.autoconfigure.SpringBootApplication;
26 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
27 | import org.springframework.cloud.openfeign.EnableFeignClients;
28 | import org.springframework.scheduling.annotation.EnableScheduling;
29 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
30 |
31 | /**
32 | * The Application class.
33 | */
34 | @EnableFeignClients
35 | @SpringBootApplication
36 | @EnableWebSecurity
37 | @EnableScheduling
38 | @EnableConfigurationProperties({VerificationApplicationConfig.class})
39 | public class VerificationApplication {
40 |
41 | /**
42 | * The main Method.
43 | *
44 | * @param args the args for the main method
45 | */
46 | public static void main(String[] args) {
47 | SpringApplication.run(VerificationApplication.class, args);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/client/IamClient.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.client;
22 |
23 | import app.coronawarn.verification.model.Certs;
24 | import org.springframework.cloud.openfeign.FeignClient;
25 | import org.springframework.http.MediaType;
26 | import org.springframework.web.bind.annotation.GetMapping;
27 |
28 | /**
29 | * This class represents the IAM feign client.
30 | */
31 | @FeignClient(name = "IamService", url = "${jwt.server}")
32 | public interface IamClient {
33 | /**
34 | * This method gets the cert information from the IAM Server.
35 | * @return Testresult from server
36 | */
37 | @GetMapping(value = "/auth/realms/cwa/protocol/openid-connect/certs",
38 | consumes = MediaType.APPLICATION_JSON_VALUE
39 | )
40 | Certs certs();
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/client/TestResultServerClient.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.client;
22 |
23 | import app.coronawarn.verification.model.HashedGuid;
24 | import app.coronawarn.verification.model.TestResult;
25 | import org.springframework.cloud.openfeign.FeignClient;
26 | import org.springframework.http.MediaType;
27 | import org.springframework.web.bind.annotation.PostMapping;
28 |
29 | /**
30 | * This class represents the Labor Server service feign client.
31 | */
32 | @FeignClient(
33 | name = "testResultServerClient",
34 | url = "${cwa-testresult-server.url}",
35 | configuration = TestResultServerClientConfig.class)
36 | public interface TestResultServerClient {
37 |
38 | /**
39 | * This method gets a testResult from the LabServer.
40 | *
41 | * @param guid for TestResult
42 | * @return TestResult from server
43 | */
44 | @PostMapping(value = "/api/v1/app/result",
45 | consumes = MediaType.APPLICATION_JSON_VALUE,
46 | produces = MediaType.APPLICATION_JSON_VALUE
47 | )
48 | TestResult result(HashedGuid guid);
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/client/TestResultServerClientConfig.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.client;
22 |
23 | import app.coronawarn.verification.exception.VerificationServerException;
24 | import feign.Client;
25 | import feign.httpclient.ApacheHttpClient;
26 | import java.io.IOException;
27 | import java.security.GeneralSecurityException;
28 | import javax.net.ssl.HostnameVerifier;
29 | import javax.net.ssl.SSLContext;
30 | import lombok.RequiredArgsConstructor;
31 | import org.apache.http.conn.ssl.DefaultHostnameVerifier;
32 | import org.apache.http.conn.ssl.NoopHostnameVerifier;
33 | import org.apache.http.impl.client.HttpClientBuilder;
34 | import org.apache.http.ssl.SSLContextBuilder;
35 | import org.springframework.beans.factory.annotation.Value;
36 | import org.springframework.context.annotation.Bean;
37 | import org.springframework.context.annotation.Configuration;
38 | import org.springframework.http.HttpStatus;
39 | import org.springframework.util.ResourceUtils;
40 |
41 | @Configuration
42 | @RequiredArgsConstructor
43 |
44 | public class TestResultServerClientConfig {
45 |
46 | @Value("${cwa-testresult-server.ssl.enabled}")
47 | private boolean enabled;
48 | @Value("${cwa-testresult-server.ssl.one-way}")
49 | private boolean oneWay;
50 | @Value("${cwa-testresult-server.ssl.two-way}")
51 | private boolean twoWay;
52 | @Value("${cwa-testresult-server.ssl.hostname-verify}")
53 | private boolean hostnameVerify;
54 | @Value("${cwa-testresult-server.ssl.key-store}")
55 | private String keyStorePath;
56 | @Value("${cwa-testresult-server.ssl.key-store-password}")
57 | private char[] keyStorePassword;
58 | @Value("${cwa-testresult-server.ssl.trust-store}")
59 | private String trustStorePath;
60 | @Value("${cwa-testresult-server.ssl.trust-store-password}")
61 | private char[] trustStorePassword;
62 |
63 | /**
64 | * Configure the client dependent on the ssl properties.
65 | *
66 | * @return an Apache Http Client with or without SSL features
67 | */
68 | @Bean
69 | public Client client() {
70 | if (enabled) {
71 | return new ApacheHttpClient(
72 | HttpClientBuilder
73 | .create()
74 | .setSSLContext(getSslContext())
75 | .setSSLHostnameVerifier(getSslHostnameVerifier())
76 | .build()
77 | );
78 | }
79 | return new ApacheHttpClient(HttpClientBuilder.create()
80 | .setSSLHostnameVerifier(getSslHostnameVerifier())
81 | .build());
82 | }
83 |
84 | private SSLContext getSslContext() {
85 | try {
86 | SSLContextBuilder builder = SSLContextBuilder
87 | .create();
88 | if (oneWay) {
89 | builder.loadTrustMaterial(ResourceUtils.getFile(trustStorePath),
90 | trustStorePassword);
91 | }
92 | if (twoWay) {
93 | builder.loadKeyMaterial(ResourceUtils.getFile(keyStorePath),
94 | keyStorePassword,
95 | keyStorePassword);
96 | }
97 | return builder.build();
98 | } catch (IOException | GeneralSecurityException e) {
99 | throw new VerificationServerException(HttpStatus.INTERNAL_SERVER_ERROR, "The SSL context could not be loaded.");
100 | }
101 | }
102 |
103 | private HostnameVerifier getSslHostnameVerifier() {
104 | return hostnameVerify ? new DefaultHostnameVerifier() : new NoopHostnameVerifier();
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/config/LocalSecurityConfig.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.config;
22 |
23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
24 | import org.springframework.context.annotation.Bean;
25 | import org.springframework.context.annotation.Configuration;
26 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
27 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
28 | import org.springframework.security.web.SecurityFilterChain;
29 |
30 | @EnableWebSecurity
31 | @Configuration
32 | @ConditionalOnProperty(name = "server.ssl.client-auth", havingValue = "none", matchIfMissing = true)
33 | public class LocalSecurityConfig {
34 |
35 | /**
36 | * filter Chain.
37 | */
38 | @Bean
39 | public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
40 | http
41 | .authorizeHttpRequests()
42 | .anyRequest().permitAll()
43 | .and().csrf().disable();
44 | return http.build();
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/config/MtlsSecurityConfig.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.config;
22 |
23 | import java.security.cert.CertificateEncodingException;
24 | import java.security.cert.X509Certificate;
25 | import java.util.Arrays;
26 | import java.util.Collections;
27 | import java.util.stream.Stream;
28 | import lombok.RequiredArgsConstructor;
29 | import lombok.extern.slf4j.Slf4j;
30 | import org.apache.commons.codec.digest.DigestUtils;
31 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
32 | import org.springframework.context.annotation.Bean;
33 | import org.springframework.context.annotation.Configuration;
34 | import org.springframework.http.HttpMethod;
35 | import org.springframework.http.HttpStatus;
36 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
37 | import org.springframework.security.core.userdetails.User;
38 | import org.springframework.security.core.userdetails.UserDetailsService;
39 | import org.springframework.security.web.SecurityFilterChain;
40 | import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
41 | import org.springframework.security.web.firewall.HttpFirewall;
42 | import org.springframework.security.web.firewall.StrictHttpFirewall;
43 | import org.springframework.web.server.ResponseStatusException;
44 |
45 | @Configuration
46 | @Slf4j
47 | @RequiredArgsConstructor
48 | @ConditionalOnProperty(name = "server.ssl.client-auth", havingValue = "need")
49 | public class MtlsSecurityConfig {
50 |
51 | private final VerificationApplicationConfig config;
52 |
53 | @Bean
54 | protected HttpFirewall strictFirewall() {
55 | StrictHttpFirewall firewall = new StrictHttpFirewall();
56 | firewall.setAllowedHttpMethods(Arrays.asList(
57 | HttpMethod.GET.name(),
58 | HttpMethod.POST.name()
59 | ));
60 | return firewall;
61 | }
62 |
63 | /**
64 | * FilterChain.
65 | */
66 | @Bean
67 | public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
68 | http
69 | .authorizeHttpRequests()
70 | .requestMatchers("/api/**").authenticated().and()
71 | .requiresChannel().requestMatchers("/api/**").requiresSecure().and()
72 | .x509().x509PrincipalExtractor(new ThumbprintX509PrincipalExtractor()).userDetailsService(userDetailsService())
73 | .and().authorizeHttpRequests()
74 | .requestMatchers("/version/**").permitAll()
75 | .requestMatchers("/actuator/**").permitAll()
76 | .anyRequest().denyAll()
77 | .and().csrf().disable();
78 | return http.build();
79 | }
80 |
81 | private UserDetailsService userDetailsService() {
82 | return hash -> {
83 |
84 | boolean allowed = Stream.of(config.getAllowedClientCertificates()
85 | .split(","))
86 | .map(String::trim)
87 | .anyMatch(entry -> entry.equalsIgnoreCase(hash));
88 |
89 | if (allowed) {
90 | return new User(hash, "", Collections.emptyList());
91 | } else {
92 | log.error("Failed to authenticate cert with hash {}", hash);
93 | throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
94 | }
95 | };
96 | }
97 |
98 | private static class ThumbprintX509PrincipalExtractor implements X509PrincipalExtractor {
99 |
100 | @Override
101 | public Object extractPrincipal(X509Certificate x509Certificate) {
102 |
103 | try {
104 | String hash = DigestUtils.sha256Hex(x509Certificate.getEncoded());
105 | log.debug("Accessed by Subject {} Hash {}", x509Certificate.getSubjectX500Principal().getName(), hash);
106 | return hash;
107 | } catch (CertificateEncodingException e) {
108 | log.error("Failed to extract bytes from certificate");
109 | return null;
110 | }
111 | }
112 | }
113 | }
114 |
115 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/config/OpenApiConfig.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.config;
22 |
23 | import io.swagger.v3.oas.models.OpenAPI;
24 | import io.swagger.v3.oas.models.info.Info;
25 | import io.swagger.v3.oas.models.info.License;
26 | import lombok.RequiredArgsConstructor;
27 | import org.springframework.boot.info.BuildProperties;
28 | import org.springframework.context.annotation.Bean;
29 | import org.springframework.context.annotation.Configuration;
30 |
31 | /**
32 | * This class represents the open api config.
33 | */
34 | @RequiredArgsConstructor
35 | @Configuration
36 | public class OpenApiConfig {
37 |
38 | private final BuildProperties buildProperties;
39 |
40 | /**
41 | * Configure the open api bean with build property values.
42 | *
43 | * @return the configured open api config
44 | */
45 | @Bean
46 | public OpenAPI openApi() {
47 | return new OpenAPI()
48 | .info(new Info()
49 | .title(buildProperties.getArtifact())
50 | .version(buildProperties.getVersion())
51 | .license(new License()
52 | .name("Apache 2.0")
53 | .url("http://www.apache.org/licenses/LICENSE-2.0")));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/config/RequestSizeLimitFilter.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.config;
22 |
23 | import jakarta.servlet.FilterChain;
24 | import jakarta.servlet.ServletException;
25 | import jakarta.servlet.http.HttpServletRequest;
26 | import jakarta.servlet.http.HttpServletResponse;
27 | import java.io.IOException;
28 | import lombok.RequiredArgsConstructor;
29 | import lombok.extern.slf4j.Slf4j;
30 | import org.apache.commons.lang3.StringUtils;
31 | import org.apache.http.HttpHeaders;
32 | import org.springframework.http.HttpStatus;
33 | import org.springframework.lang.NonNull;
34 | import org.springframework.stereotype.Component;
35 | import org.springframework.web.filter.OncePerRequestFilter;
36 |
37 | /**
38 | * A filter to avoid requests with a large content and chunked requests.
39 | */
40 | @Component
41 | @Slf4j
42 | @RequiredArgsConstructor
43 | public class RequestSizeLimitFilter extends OncePerRequestFilter {
44 |
45 | private final VerificationApplicationConfig verificationApplicationConfig;
46 |
47 | @Override
48 | protected void doFilterInternal(HttpServletRequest request,
49 | @NonNull HttpServletResponse response, @NonNull FilterChain filterChain)
50 | throws ServletException, IOException {
51 | long maxPostSize = verificationApplicationConfig.getRequest().getSizelimit();
52 | if (request.getContentLengthLong() > maxPostSize || isChunkedRequest(request)) {
53 | log.warn("The request size is too large or the request was sent via chunks.");
54 | response.setStatus(HttpStatus.NOT_ACCEPTABLE.value());
55 | return;
56 | }
57 | filterChain.doFilter(request, response);
58 | }
59 |
60 | private boolean isChunkedRequest(HttpServletRequest request) {
61 | String header = request.getHeader(HttpHeaders.TRANSFER_ENCODING);
62 |
63 | return !StringUtils.isEmpty(header) && header.equalsIgnoreCase("chunked");
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/config/VerificationApplicationConfig.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.config;
22 |
23 | import lombok.Getter;
24 | import lombok.Setter;
25 | import org.springframework.boot.context.properties.ConfigurationProperties;
26 |
27 | /**
28 | * This class and its nested subclasses are used to read in values from configuration file application.yml, which is
29 | * loaded via the '@EnableConfigurationProperties' annotation from SpringBootApplication main class.
30 | */
31 | @Getter
32 | @Setter
33 | @ConfigurationProperties
34 | public class VerificationApplicationConfig {
35 | private Long initialFakeDelayMilliseconds;
36 |
37 | private Long fakeDelayMovingAverageSamples;
38 | private String allowedClientCertificates;
39 |
40 | private Tan tan = new Tan();
41 | private AppSession appsession = new AppSession();
42 | private Entities entities = new Entities();
43 | private Jwt jwt = new Jwt();
44 | private Request request = new Request();
45 |
46 | private boolean disableDobHashCheckForExternalTestResult;
47 |
48 | /**
49 | * Configure the Tan with build property values and return the configured parameters.
50 | */
51 | @Getter
52 | @Setter
53 | public static class Tan {
54 |
55 | private Tele tele = new Tele();
56 | private Valid valid = new Valid();
57 |
58 | /**
59 | * Configure the Tele with build property values and return the configured parameters.
60 | */
61 | @Getter
62 | @Setter
63 | public static class Tele {
64 |
65 | private Valid valid = new Valid();
66 | private RateLimiting rateLimiting = new RateLimiting();
67 |
68 | /**
69 | * Configure the TeleValid with build property values and return the configured parameters.
70 | */
71 | @Getter
72 | @Setter
73 | public static class Valid {
74 |
75 | private String chars = "23456789ABCDEFGHJKMNPQRSTUVWXYZ";
76 | private int length = 1;
77 | // Number of hours that teleTAN remains valid
78 | private int hours = 1;
79 | private int eventDays = 2;
80 | }
81 |
82 | /**
83 | * Configure the rate limiting for creating new teletans.
84 | */
85 | @Getter
86 | @Setter
87 | public static class RateLimiting {
88 |
89 | // Number of seconds for the rate limiting time window
90 | private int seconds = 3600;
91 | // Number of teletans that are allowed to create within time window
92 | private int count = 1000;
93 | // Threshold in percent for a warning in log stream
94 | private int thresholdInPercent = 80;
95 | }
96 | }
97 |
98 | /**
99 | * Configure the Valid with build property values and return the configured parameters.
100 | */
101 | @Getter
102 | @Setter
103 | public static class Valid {
104 |
105 | // Number of days that TAN remains valid
106 | int days = 14;
107 | }
108 | }
109 |
110 | /**
111 | * Configure the AppSession with build property values and return the configured parameters.
112 | */
113 | @Getter
114 | @Setter
115 | public static class AppSession {
116 |
117 | // Maximum number of tans in a session at one time
118 | int tancountermax = 1;
119 | }
120 |
121 | /**
122 | * Configure the Entities with build property values and return the configured parameters.
123 | */
124 | @Getter
125 | @Setter
126 | public static class Entities {
127 |
128 | private Cleanup cleanup = new Cleanup();
129 |
130 | /**
131 | * Configure the Cleanup with build property values and return the configured parameters.
132 | */
133 | @Getter
134 | @Setter
135 | public static class Cleanup {
136 |
137 | private Integer days = 21;
138 | private String cron = "0 1 * * * *";
139 | private Integer locklimit = 60;
140 | }
141 |
142 | }
143 |
144 | /**
145 | * Configure the Jwt with build property values and return the configured parameters.
146 | */
147 | @Getter
148 | @Setter
149 | public static class Jwt {
150 |
151 | private String server = "http://localhost:8080";
152 | private Boolean enabled = false;
153 | }
154 |
155 | /**
156 | * Configure the requests with build property values and return the configured parameters.
157 | */
158 | @Getter
159 | @Setter
160 | public static class Request {
161 |
162 | private long sizelimit = 10000;
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/controller/ExternalTanController.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.controller;
22 |
23 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
24 |
25 | import app.coronawarn.verification.config.VerificationApplicationConfig;
26 | import app.coronawarn.verification.domain.VerificationAppSession;
27 | import app.coronawarn.verification.exception.VerificationServerException;
28 | import app.coronawarn.verification.model.AppSessionSourceOfTrust;
29 | import app.coronawarn.verification.model.HashedGuid;
30 | import app.coronawarn.verification.model.LabTestResult;
31 | import app.coronawarn.verification.model.RegistrationToken;
32 | import app.coronawarn.verification.model.Tan;
33 | import app.coronawarn.verification.model.TanSourceOfTrust;
34 | import app.coronawarn.verification.model.TestResult;
35 | import app.coronawarn.verification.service.AppSessionService;
36 | import app.coronawarn.verification.service.FakeDelayService;
37 | import app.coronawarn.verification.service.FakeRequestService;
38 | import app.coronawarn.verification.service.TanService;
39 | import app.coronawarn.verification.service.TestResultServerService;
40 | import io.swagger.v3.oas.annotations.Operation;
41 | import io.swagger.v3.oas.annotations.responses.ApiResponse;
42 | import io.swagger.v3.oas.annotations.responses.ApiResponses;
43 | import jakarta.validation.Valid;
44 | import java.time.LocalDateTime;
45 | import java.util.Optional;
46 | import java.util.concurrent.Executors;
47 | import java.util.concurrent.ScheduledExecutorService;
48 | import lombok.NonNull;
49 | import lombok.RequiredArgsConstructor;
50 | import lombok.extern.slf4j.Slf4j;
51 | import org.apache.commons.lang3.RandomStringUtils;
52 | import org.springframework.context.annotation.Profile;
53 | import org.springframework.http.HttpStatus;
54 | import org.springframework.http.MediaType;
55 | import org.springframework.http.ResponseEntity;
56 | import org.springframework.util.StopWatch;
57 | import org.springframework.validation.annotation.Validated;
58 | import org.springframework.web.bind.annotation.PostMapping;
59 | import org.springframework.web.bind.annotation.RequestBody;
60 | import org.springframework.web.bind.annotation.RequestHeader;
61 | import org.springframework.web.bind.annotation.RequestMapping;
62 | import org.springframework.web.bind.annotation.RestController;
63 | import org.springframework.web.context.request.async.DeferredResult;
64 |
65 | /**
66 | * This class represents the rest controller for external tan interactions.
67 | */
68 | @Slf4j
69 | @RequiredArgsConstructor
70 | @RestController
71 | @RequestMapping("/version/v1")
72 | @Validated
73 | @Profile("external")
74 | public class ExternalTanController {
75 |
76 | /**
77 | * The route to the tan generation endpoint.
78 | */
79 | public static final String TAN_ROUTE = "/tan";
80 | private static final Integer RESPONSE_PADDING_LENGTH = 15;
81 | private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4);
82 |
83 | @NonNull
84 | private final AppSessionService appSessionService;
85 |
86 | @NonNull
87 | private final FakeDelayService fakeDelayService;
88 |
89 | @NonNull
90 | private final VerificationApplicationConfig verificationApplicationConfig;
91 |
92 | @NonNull
93 | private final TestResultServerService testResultServerService;
94 |
95 | @NonNull
96 | private final TanService tanService;
97 |
98 | @NonNull
99 | private final FakeRequestService fakeRequestService;
100 |
101 | /**
102 | * This method generates a transaction number by a Registration Token, if the state of the COVID-19 lab-test is
103 | * positive.
104 | *
105 | * @param registrationToken generated by a hashed guid or a teleTAN. {@link RegistrationToken}
106 | * @param fake flag for fake request
107 | * @return A generated transaction number {@link Tan}.
108 | */
109 | @Operation(
110 | summary = "Generates a Tan",
111 | description = "Generates a TAN on input of Registration Token. With the TAN one can submit his Diagnosis keys"
112 | )
113 | @ApiResponses(value = {
114 | @ApiResponse(responseCode = "201", description = "Registration Token is valid"),
115 | @ApiResponse(responseCode = "400", description = "Registration Token does not exist")})
116 | @PostMapping(value = TAN_ROUTE,
117 | consumes = MediaType.APPLICATION_JSON_VALUE,
118 | produces = MediaType.APPLICATION_JSON_VALUE
119 | )
120 | public DeferredResult> generateTan(@Valid @RequestBody RegistrationToken registrationToken,
121 | @RequestHeader(value = "cwa-fake", required = false)
122 | String fake) {
123 | if ((fake != null) && (fake.equals("1"))) {
124 | return fakeRequestService.generateTan(registrationToken);
125 | }
126 | StopWatch stopWatch = new StopWatch();
127 | stopWatch.start();
128 | Optional actual
129 | = appSessionService.getAppSessionByToken(registrationToken.getRegistrationToken());
130 | if (actual.isPresent()) {
131 | VerificationAppSession appSession = actual.get();
132 | int tancountermax = verificationApplicationConfig.getAppsession().getTancountermax();
133 | if (appSession.getTanCounter() < tancountermax) {
134 | AppSessionSourceOfTrust appSessionSourceOfTrust = appSession.getSourceOfTrust();
135 | TanSourceOfTrust tanSourceOfTrust = TanSourceOfTrust.CONNECTED_LAB;
136 | switch (appSessionSourceOfTrust) {
137 | case HASHED_GUID -> {
138 | TestResult covidTestResult = testResultServerService.result(new HashedGuid(appSession.getHashedGuid()));
139 | if (covidTestResult.getTestResult() != LabTestResult.POSITIVE.getTestResult()
140 | && covidTestResult.getTestResult() != LabTestResult.QUICK_POSITIVE.getTestResult()
141 | ) {
142 | stopWatch.stop();
143 | throw new VerificationServerException(HttpStatus.BAD_REQUEST,
144 | "Tan cannot be created, caused by the non positive result of the labserver");
145 | }
146 | }
147 | case TELETAN -> tanSourceOfTrust = TanSourceOfTrust.TELETAN;
148 | default -> {
149 | stopWatch.stop();
150 | throw new VerificationServerException(HttpStatus.BAD_REQUEST,
151 | "Unknown source of trust inside the appsession for the registration token");
152 | }
153 | }
154 | appSession.incrementTanCounter();
155 | appSession.setUpdatedAt(LocalDateTime.now());
156 |
157 | appSessionService.saveAppSession(appSession);
158 | String generatedTan = tanService.generateVerificationTan(tanSourceOfTrust, appSession.getTeleTanType());
159 |
160 | Tan returnTan = generateReturnTan(generatedTan, fake);
161 | stopWatch.stop();
162 | fakeDelayService.updateFakeTanRequestDelay(stopWatch.getTotalTimeMillis());
163 | DeferredResult> deferredResult = new DeferredResult<>();
164 | scheduledExecutor.schedule(() -> deferredResult.setResult(
165 | ResponseEntity.status(HttpStatus.CREATED).body(returnTan)),
166 | fakeDelayService.realDelayTan(), MILLISECONDS);
167 | log.info("Returning the successfully generated tan.");
168 | return deferredResult;
169 | }
170 | throw new VerificationServerException(HttpStatus.BAD_REQUEST,
171 | "The maximum of generating tans for this registration token is reached");
172 | }
173 | throw new VerificationServerException(HttpStatus.BAD_REQUEST,
174 | "VerificationAppSession not found for the registration token");
175 | }
176 |
177 | private Tan generateReturnTan(String tan, String fake) {
178 | if (fake == null) {
179 | return new Tan(tan);
180 | }
181 | return new Tan(tan, RandomStringUtils.randomAlphanumeric(RESPONSE_PADDING_LENGTH));
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/controller/ExternalTokenController.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.controller;
22 |
23 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
24 |
25 | import app.coronawarn.verification.domain.VerificationTan;
26 | import app.coronawarn.verification.exception.VerificationServerException;
27 | import app.coronawarn.verification.model.RegistrationToken;
28 | import app.coronawarn.verification.model.RegistrationTokenKeyType;
29 | import app.coronawarn.verification.model.RegistrationTokenRequest;
30 | import app.coronawarn.verification.service.AppSessionService;
31 | import app.coronawarn.verification.service.FakeDelayService;
32 | import app.coronawarn.verification.service.FakeRequestService;
33 | import app.coronawarn.verification.service.TanService;
34 | import io.swagger.v3.oas.annotations.Operation;
35 | import io.swagger.v3.oas.annotations.responses.ApiResponse;
36 | import io.swagger.v3.oas.annotations.responses.ApiResponses;
37 | import jakarta.validation.Valid;
38 | import java.util.Optional;
39 | import java.util.concurrent.Executors;
40 | import java.util.concurrent.ScheduledExecutorService;
41 | import lombok.RequiredArgsConstructor;
42 | import lombok.extern.slf4j.Slf4j;
43 | import org.springframework.context.annotation.Profile;
44 | import org.springframework.http.HttpStatus;
45 | import org.springframework.http.MediaType;
46 | import org.springframework.http.ResponseEntity;
47 | import org.springframework.util.StopWatch;
48 | import org.springframework.validation.annotation.Validated;
49 | import org.springframework.web.bind.annotation.PostMapping;
50 | import org.springframework.web.bind.annotation.RequestBody;
51 | import org.springframework.web.bind.annotation.RequestHeader;
52 | import org.springframework.web.bind.annotation.RequestMapping;
53 | import org.springframework.web.bind.annotation.RestController;
54 | import org.springframework.web.context.request.async.DeferredResult;
55 |
56 | /**
57 | * This class represents the rest controller for externally reachable TAN interactions.
58 | */
59 | @Slf4j
60 | @RequiredArgsConstructor
61 | @RestController
62 | @RequestMapping("/version/v1")
63 | @Validated
64 | @Profile("external")
65 | public class ExternalTokenController {
66 |
67 | /**
68 | * The route to the token registration endpoint.
69 | */
70 | public static final String REGISTRATION_TOKEN_ROUTE = "/registrationToken";
71 |
72 | private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4);
73 |
74 | private final FakeRequestService fakeRequestController;
75 |
76 | private final AppSessionService appSessionService;
77 |
78 | private final TanService tanService;
79 |
80 | private final FakeDelayService fakeDelayService;
81 |
82 | /**
83 | * This method generates a registrationToken by a hashed guid or a teleTAN.
84 | *
85 | * @param request {@link RegistrationTokenRequest}
86 | * @param fake flag for fake request
87 | * @return RegistrationToken - the created registration token {@link RegistrationToken}
88 | */
89 | @Operation(
90 | summary = "Get registration Token",
91 | description = "Get a registration token by providing a SHA-256 hasehd GUID or a teleTAN")
92 | @ApiResponses(value = {
93 | @ApiResponse(responseCode = "201", description = "registration token generated."),
94 | @ApiResponse(responseCode = "400", description = "GUID/TeleTAN already exists.")})
95 | @PostMapping(value = REGISTRATION_TOKEN_ROUTE,
96 | consumes = MediaType.APPLICATION_JSON_VALUE,
97 | produces = MediaType.APPLICATION_JSON_VALUE)
98 | public DeferredResult> generateRegistrationToken(
99 | @RequestBody @Valid RegistrationTokenRequest request,
100 | @RequestHeader(value = "cwa-fake", required = false) String fake) {
101 | if ((fake != null) && (fake.equals("1"))) {
102 | return fakeRequestController.generateRegistrationToken(request);
103 | }
104 | StopWatch stopWatch = new StopWatch();
105 | stopWatch.start();
106 | String key = request.getKey();
107 | RegistrationTokenKeyType keyType = request.getKeyType();
108 | DeferredResult> deferredResult = new DeferredResult<>();
109 |
110 | switch (keyType) {
111 | case GUID -> {
112 | ResponseEntity responseEntity =
113 | appSessionService.generateRegistrationTokenByGuid(key, request.getKeyDob(), fake);
114 | stopWatch.stop();
115 | fakeDelayService.updateFakeTokenRequestDelay(stopWatch.getTotalTimeMillis());
116 | deferredResult.setResult(responseEntity);
117 | log.info("Returning the successfully generated RegistrationToken.");
118 | return deferredResult;
119 | }
120 | case TELETAN -> {
121 | Optional optional = tanService.getEntityByTan(key);
122 | ResponseEntity response = appSessionService.generateRegistrationTokenByTeleTan(
123 | key,
124 | fake,
125 | optional.map(VerificationTan::getTeleTanType).orElse(null));
126 | if (optional.isPresent()) {
127 | VerificationTan teleTan = optional.get();
128 | teleTan.setRedeemed(true);
129 | tanService.saveTan(teleTan);
130 | stopWatch.stop();
131 | fakeDelayService.updateFakeTokenRequestDelay(stopWatch.getTotalTimeMillis());
132 | scheduledExecutor.schedule(() -> deferredResult.setResult(response), fakeDelayService.realDelayToken(),
133 | MILLISECONDS);
134 | log.info("Returning the successfully generated RegistrationToken.");
135 | return deferredResult;
136 | }
137 | stopWatch.stop();
138 | throw new VerificationServerException(HttpStatus.BAD_REQUEST, "The teleTAN verification failed");
139 | }
140 | default -> {
141 | stopWatch.stop();
142 | throw new VerificationServerException(HttpStatus.BAD_REQUEST,
143 | "Unknown registration key type for registration token");
144 | }
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/controller/InternalTanController.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.controller;
22 |
23 | import app.coronawarn.verification.exception.VerificationServerException;
24 | import app.coronawarn.verification.model.AuthorizationRole;
25 | import app.coronawarn.verification.model.AuthorizationToken;
26 | import app.coronawarn.verification.model.Tan;
27 | import app.coronawarn.verification.model.TeleTan;
28 | import app.coronawarn.verification.model.TeleTanType;
29 | import app.coronawarn.verification.service.JwtService;
30 | import app.coronawarn.verification.service.TanService;
31 | import io.swagger.v3.oas.annotations.Operation;
32 | import io.swagger.v3.oas.annotations.headers.Header;
33 | import io.swagger.v3.oas.annotations.responses.ApiResponse;
34 | import io.swagger.v3.oas.annotations.responses.ApiResponses;
35 | import jakarta.validation.Valid;
36 | import java.time.LocalDateTime;
37 | import java.util.ArrayList;
38 | import java.util.List;
39 | import lombok.NonNull;
40 | import lombok.RequiredArgsConstructor;
41 | import lombok.extern.slf4j.Slf4j;
42 | import org.springframework.context.annotation.Profile;
43 | import org.springframework.http.HttpStatus;
44 | import org.springframework.http.MediaType;
45 | import org.springframework.http.ResponseEntity;
46 | import org.springframework.validation.annotation.Validated;
47 | import org.springframework.web.bind.annotation.PostMapping;
48 | import org.springframework.web.bind.annotation.RequestBody;
49 | import org.springframework.web.bind.annotation.RequestHeader;
50 | import org.springframework.web.bind.annotation.RequestMapping;
51 | import org.springframework.web.bind.annotation.RestController;
52 |
53 | /**
54 | * This class represents the rest controller for internally needed tan operations.
55 | */
56 | @Slf4j
57 | @RequiredArgsConstructor
58 | @RestController
59 | @RequestMapping("/version/v1")
60 | @Validated
61 | @Profile("internal")
62 | public class InternalTanController {
63 |
64 | /**
65 | * The route to the tan verification endpoint.
66 | */
67 | public static final String TAN_VERIFY_ROUTE = "/tan/verify";
68 | /**
69 | * The route to the teleTAN generation endpoint.
70 | */
71 | public static final String TELE_TAN_ROUTE = "/tan/teletan";
72 |
73 | public static final String TELE_TAN_TYPE_HEADER = "X-CWA-TELETAN-TYPE";
74 |
75 | @NonNull
76 | private final TanService tanService;
77 |
78 | @NonNull
79 | private final JwtService jwtService;
80 |
81 | /**
82 | * This provided REST method verifies the transaction number (TAN).
83 | *
84 | * @param tan - the transaction number, which needs to be verified {@link Tan}
85 | * @return HTTP 200, if the verification was successful. Otherwise HTTP 404.
86 | */
87 | @Operation(
88 | summary = "Verify provided Tan",
89 | description = "The provided Tan is verified to be formerly issued by the verification server"
90 | )
91 | @ApiResponses(value = {
92 | @ApiResponse(
93 | responseCode = "200",
94 | description = "Tan is valid an formerly issued by the verification server",
95 | headers = {
96 | @Header(name = TELE_TAN_TYPE_HEADER, description = "Type of the TeleTan (TEST or EVENT)")
97 | }),
98 | @ApiResponse(responseCode = "404", description = "Tan could not be verified")})
99 | @PostMapping(value = TAN_VERIFY_ROUTE,
100 | consumes = MediaType.APPLICATION_JSON_VALUE
101 | )
102 | public ResponseEntity> verifyTan(@Valid @RequestBody Tan tan) {
103 | return tanService.getEntityByTan(tan.getTan())
104 | .filter(t -> t.canBeRedeemed(LocalDateTime.now()))
105 | .map(t -> {
106 | tanService.deleteTan(t);
107 | log.info("The Tan is valid.");
108 | return t;
109 | })
110 | .map(t -> {
111 | ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.ok();
112 |
113 | if (t.getTeleTanType() != null) {
114 | responseBuilder.header(TELE_TAN_TYPE_HEADER, t.getTeleTanType().toString());
115 | }
116 |
117 | return responseBuilder.build();
118 | })
119 | .orElseGet(() -> {
120 | log.info("The Tan is invalid.");
121 | throw new VerificationServerException(HttpStatus.NOT_FOUND, "No Tan found or Tan is invalid");
122 | });
123 | }
124 |
125 | /**
126 | * This method generates a valid teleTAN.
127 | *
128 | * @param authorization auth
129 | * @return a created teletan
130 | */
131 | @Operation(
132 | summary = "Request generation of a teleTAN",
133 | description = "A teleTAN is a human readable TAN with 7 characters which is supposed to be issued via call line"
134 | )
135 | @ApiResponses(value = {
136 | @ApiResponse(responseCode = "201", description = "TeleTan created")})
137 | @PostMapping(value = TELE_TAN_ROUTE,
138 | produces = MediaType.APPLICATION_JSON_VALUE
139 | )
140 | public ResponseEntity createTeleTan(
141 | @RequestHeader(JwtService.HEADER_NAME_AUTHORIZATION) @Valid AuthorizationToken authorization,
142 | @RequestHeader(value = TELE_TAN_TYPE_HEADER, required = false) @Valid TeleTanType teleTanType) {
143 |
144 | List requiredRoles = new ArrayList<>();
145 |
146 | if (teleTanType == null) {
147 | teleTanType = TeleTanType.TEST;
148 | requiredRoles.add(AuthorizationRole.AUTH_C19_HOTLINE);
149 | } else if (teleTanType == TeleTanType.EVENT) {
150 | requiredRoles.add(AuthorizationRole.AUTH_C19_HOTLINE_EVENT);
151 | }
152 |
153 | if (jwtService.isAuthorized(authorization.getToken(), requiredRoles)) {
154 | if (tanService.isTeleTanRateLimitNotExceeded()) {
155 | String teleTan = tanService.generateVerificationTeleTan(teleTanType);
156 | log.info("The teleTAN is generated.");
157 | return ResponseEntity.status(HttpStatus.CREATED).body(new TeleTan(teleTan));
158 | } else {
159 | throw new VerificationServerException(HttpStatus.TOO_MANY_REQUESTS, "Rate Limit exceed. Try again later.");
160 | }
161 | }
162 | throw new VerificationServerException(HttpStatus.UNAUTHORIZED, "JWT is invalid.");
163 | }
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/controller/InternalTestStateController.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.controller;
22 |
23 | import app.coronawarn.verification.domain.VerificationAppSession;
24 | import app.coronawarn.verification.exception.VerificationServerException;
25 | import app.coronawarn.verification.model.AppSessionSourceOfTrust;
26 | import app.coronawarn.verification.model.HashedGuid;
27 | import app.coronawarn.verification.model.InternalTestResult;
28 | import app.coronawarn.verification.model.RegistrationToken;
29 | import app.coronawarn.verification.model.TestResult;
30 | import app.coronawarn.verification.service.AppSessionService;
31 | import app.coronawarn.verification.service.TestResultServerService;
32 | import io.swagger.v3.oas.annotations.Operation;
33 | import io.swagger.v3.oas.annotations.responses.ApiResponse;
34 | import io.swagger.v3.oas.annotations.responses.ApiResponses;
35 | import jakarta.validation.Valid;
36 | import java.util.Optional;
37 | import lombok.RequiredArgsConstructor;
38 | import lombok.extern.slf4j.Slf4j;
39 | import org.springframework.context.annotation.Profile;
40 | import org.springframework.http.HttpStatus;
41 | import org.springframework.http.MediaType;
42 | import org.springframework.http.ResponseEntity;
43 | import org.springframework.validation.annotation.Validated;
44 | import org.springframework.web.bind.annotation.PostMapping;
45 | import org.springframework.web.bind.annotation.RequestBody;
46 | import org.springframework.web.bind.annotation.RequestMapping;
47 | import org.springframework.web.bind.annotation.RestController;
48 |
49 |
50 | /**
51 | * This class represents the rest controller for requests regarding test states.
52 | */
53 | @Slf4j
54 | @RequiredArgsConstructor
55 | @RestController
56 | @RequestMapping("/version/v1")
57 | @Validated
58 | @Profile("internal")
59 | public class InternalTestStateController {
60 |
61 | /**
62 | * The route to the test status of the COVID-19 test endpoint.
63 | */
64 | public static final String TESTRESULT_ROUTE = "/testresult";
65 |
66 | private final AppSessionService appSessionService;
67 |
68 | private final TestResultServerService testResultServerService;
69 |
70 | /**
71 | * Returns the test status of the COVID-19 test.
72 | *
73 | * @param registrationToken generated by a hashed guid {@link RegistrationToken}
74 | * @return result of the test, which can be POSITIVE, NEGATIVE, INVALID, PENDING, FAILED,
75 | * quick-test-POSITIVE, quick-test-NEGATIVE, quick-test-INVALID, quick-test-PENDING or quick-test-FAILED
76 | * will be POSITIVE for TeleTan
77 | */
78 | @Operation(
79 | summary = "COVID-19 test result for given RegistrationToken",
80 | description = "Gets the result of COVID-19 Test. "
81 | + "If the RegistrationToken belongs to a TeleTan the result is always positive"
82 | )
83 | @ApiResponses(value = {
84 | @ApiResponse(responseCode = "200", description = "Testresult retrieved"),
85 | @ApiResponse(responseCode = "403", description = "RegistrationToken is issued for TeleTan"),
86 | @ApiResponse(responseCode = "404", description = "RegistrationToken not found")
87 | })
88 | @PostMapping(value = TESTRESULT_ROUTE,
89 | consumes = MediaType.APPLICATION_JSON_VALUE,
90 | produces = MediaType.APPLICATION_JSON_VALUE
91 | )
92 | public ResponseEntity getTestState(@Valid @RequestBody RegistrationToken registrationToken) {
93 |
94 | Optional appSession =
95 | appSessionService.getAppSessionByToken(registrationToken.getRegistrationToken());
96 |
97 | if (appSession.isPresent()) {
98 | AppSessionSourceOfTrust sourceOfTrust = appSession.get().getSourceOfTrust();
99 |
100 | switch (sourceOfTrust) {
101 | case HASHED_GUID -> {
102 | HashedGuid hash = new HashedGuid(appSession.get().getHashedGuid());
103 | TestResult testResult = testResultServerService.result(hash);
104 |
105 | // Check DOB Hash if present
106 | if (appSession.get().getHashedGuidDob() != null) {
107 | HashedGuid hashDob = new HashedGuid(appSession.get().getHashedGuidDob());
108 | TestResult testResultDob = testResultServerService.result(hashDob);
109 |
110 | // TRS will always respond with a TestResult so we have to check if both results are equal
111 | if (testResultDob.getTestResult() != testResult.getTestResult()) {
112 | // given DOB Hash is invalid
113 | throw new VerificationServerException(HttpStatus.FORBIDDEN,
114 | "TestResult of dob hash does not equal to TestResult of hash");
115 | }
116 | }
117 | log.debug("Result {}", testResult);
118 | log.info("The result for registration token based on hashed Guid will be returned.");
119 | return ResponseEntity.ok(new InternalTestResult(
120 | testResult.getTestResult(),
121 | testResult.getSc(),
122 | testResult.getLabId(),
123 | testResult.getResponsePadding(),
124 | appSession.get().getHashedGuid()));
125 | }
126 | case TELETAN -> {
127 | log.info("Internal TestState is not allowed for TeleTan Token.");
128 | throw new VerificationServerException(HttpStatus.FORBIDDEN,
129 | "Internal TestState is not allowed for TeleTan Token.");
130 | }
131 | default -> throw new VerificationServerException(HttpStatus.BAD_REQUEST,
132 | "Unknown source of trust inside the appsession for the registration token");
133 | }
134 | }
135 | log.info("The registration token doesn't exist.");
136 | throw new VerificationServerException(HttpStatus.NOT_FOUND,
137 | "Registration Token not found");
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/controller/VerificationExceptionHandler.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.controller;
22 |
23 | import app.coronawarn.verification.exception.VerificationServerException;
24 | import jakarta.validation.ConstraintViolationException;
25 | import lombok.extern.slf4j.Slf4j;
26 | import org.springframework.http.HttpStatus;
27 | import org.springframework.http.ResponseEntity;
28 | import org.springframework.http.converter.HttpMessageNotReadableException;
29 | import org.springframework.web.HttpMediaTypeNotSupportedException;
30 | import org.springframework.web.HttpRequestMethodNotSupportedException;
31 | import org.springframework.web.bind.MethodArgumentNotValidException;
32 | import org.springframework.web.bind.ServletRequestBindingException;
33 | import org.springframework.web.bind.annotation.ExceptionHandler;
34 | import org.springframework.web.bind.annotation.ResponseStatus;
35 | import org.springframework.web.bind.annotation.RestControllerAdvice;
36 | import org.springframework.web.context.request.WebRequest;
37 |
38 | /**
39 | * This class represents the Exception Handler.
40 | */
41 | @Slf4j
42 | @RestControllerAdvice
43 | public class VerificationExceptionHandler {
44 |
45 | /**
46 | * This method handles unknown Exceptions and Server Errors.
47 | *
48 | * @param ex the thrown exception
49 | * @param wr the WebRequest
50 | */
51 | @ExceptionHandler(Exception.class)
52 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
53 | public void unknownException(Exception ex, WebRequest wr) {
54 | log.error("Unable to handle {}", wr.getDescription(false), ex);
55 | }
56 |
57 | /**
58 | * This method handles Bad Requests.
59 | *
60 | * @param ex the thrown exception
61 | * @param wr the WebRequest
62 | */
63 | @ExceptionHandler({
64 | HttpMessageNotReadableException.class,
65 | ServletRequestBindingException.class
66 | })
67 | @ResponseStatus(HttpStatus.BAD_REQUEST)
68 | public void bindingExceptions(Exception ex, WebRequest wr) {
69 | log.error("Binding failed {}", wr.getDescription(false), ex);
70 | }
71 |
72 | /**
73 | * This method handles Validation Exceptions.
74 | *
75 | * @return ResponseEntity> returns Bad Request
76 | */
77 | @ExceptionHandler({
78 | MethodArgumentNotValidException.class,
79 | ConstraintViolationException.class
80 | })
81 | public ResponseEntity> handleValidationExceptions() {
82 | return ResponseEntity.badRequest().build();
83 | }
84 |
85 | /**
86 | * This method handles Validation Exceptions.
87 | *
88 | * @param exception the thrown exception
89 | * @return ResponseEntity> returns a HTTP Status
90 | */
91 | @ExceptionHandler(VerificationServerException.class)
92 | public ResponseEntity handleVerificationServerExceptions(VerificationServerException exception) {
93 | log.warn("The verification server response preventation due to: {}", exception.getMessage());
94 | return ResponseEntity.status(exception.getHttpStatus()).build();
95 | }
96 |
97 | /**
98 | * This method handles invalid HTTP methods.
99 | *
100 | * @param ex the thrown exception
101 | * @param wr the WebRequest
102 | */
103 | @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
104 | @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
105 | public void methodNotAllowedException(Exception ex, WebRequest wr) {
106 | log.warn("Invalid http method {}", wr.getDescription(false), ex);
107 | }
108 |
109 | /**
110 | * This method handles unsupported content types.
111 | *
112 | * @param ex the thrown exception
113 | * @param wr the WebRequest
114 | */
115 | @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
116 | @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
117 | public void contentTypeNotAllowedException(Exception ex, WebRequest wr) {
118 | log.warn("Unsupported content type {}", wr.getDescription(false), ex);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/domain/VerificationAppSession.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.domain;
22 |
23 | import app.coronawarn.verification.model.AppSessionSourceOfTrust;
24 | import app.coronawarn.verification.model.TeleTanType;
25 | import jakarta.persistence.Column;
26 | import jakarta.persistence.Entity;
27 | import jakarta.persistence.EnumType;
28 | import jakarta.persistence.Enumerated;
29 | import jakarta.persistence.GeneratedValue;
30 | import jakarta.persistence.GenerationType;
31 | import jakarta.persistence.Id;
32 | import jakarta.persistence.Table;
33 | import jakarta.persistence.Version;
34 | import java.io.Serializable;
35 | import java.time.LocalDateTime;
36 | import lombok.AllArgsConstructor;
37 | import lombok.EqualsAndHashCode;
38 | import lombok.Getter;
39 | import lombok.NoArgsConstructor;
40 | import lombok.Setter;
41 |
42 | /**
43 | * This class represents the AppSession-entity.
44 | */
45 | @Getter
46 | @Setter
47 | @EqualsAndHashCode
48 | @NoArgsConstructor
49 | @AllArgsConstructor
50 | @Entity
51 | @Table(name = "app_session")
52 | public class VerificationAppSession implements Serializable {
53 |
54 | private static final long serialVersionUID = 1L;
55 |
56 | @Id
57 | @GeneratedValue(strategy = GenerationType.IDENTITY)
58 | @Column(name = "id")
59 | private Long id;
60 |
61 | @Version
62 | @Column(name = "version")
63 | private long version;
64 |
65 | @Column(name = "created_at")
66 | private LocalDateTime createdAt;
67 |
68 | @Column(name = "updated_at")
69 | private LocalDateTime updatedAt;
70 |
71 | @Column(name = "hashed_guid")
72 | private String hashedGuid;
73 |
74 | @Column(name = "hashed_guid_dob")
75 | private String hashedGuidDob;
76 |
77 | @Column(name = "registration_token_hash")
78 | private String registrationTokenHash;
79 |
80 | @Column(name = "tele_tan_hash")
81 | private String teleTanHash;
82 |
83 | @Column(name = "tan_counter")
84 | private int tanCounter;
85 |
86 | @Column(name = "sot")
87 | @Enumerated(EnumType.STRING)
88 | private AppSessionSourceOfTrust sourceOfTrust;
89 |
90 | @Column(name = "teletan_type")
91 | @Enumerated(EnumType.STRING)
92 | private TeleTanType teleTanType;
93 |
94 | /**
95 | * This method increments the tan counter.
96 | */
97 | public void incrementTanCounter() {
98 | this.tanCounter++;
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/domain/VerificationTan.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.domain;
22 |
23 | import app.coronawarn.verification.model.TanSourceOfTrust;
24 | import app.coronawarn.verification.model.TanType;
25 | import app.coronawarn.verification.model.TeleTanType;
26 | import jakarta.persistence.Column;
27 | import jakarta.persistence.Entity;
28 | import jakarta.persistence.EnumType;
29 | import jakarta.persistence.Enumerated;
30 | import jakarta.persistence.GeneratedValue;
31 | import jakarta.persistence.GenerationType;
32 | import jakarta.persistence.Id;
33 | import jakarta.persistence.Table;
34 | import jakarta.persistence.Version;
35 | import java.io.Serializable;
36 | import java.time.LocalDateTime;
37 | import lombok.AllArgsConstructor;
38 | import lombok.EqualsAndHashCode;
39 | import lombok.Getter;
40 | import lombok.NoArgsConstructor;
41 | import lombok.Setter;
42 |
43 | /**
44 | * This class represents the TAN - entity.
45 | */
46 | @Getter
47 | @Setter
48 | @EqualsAndHashCode
49 | @NoArgsConstructor
50 | @AllArgsConstructor
51 | @Entity
52 | @Table(name = "tan")
53 | public class VerificationTan implements Serializable {
54 |
55 | static final long SERIAL_VERSION_UID = 1L;
56 |
57 | @Id
58 | @GeneratedValue(strategy = GenerationType.IDENTITY)
59 | @Column(name = "id")
60 | private Long id;
61 |
62 | @Version
63 | @Column(name = "version")
64 | private long version;
65 |
66 | @Column(name = "created_at")
67 | private LocalDateTime createdAt;
68 |
69 | @Column(name = "updated_at")
70 | private LocalDateTime updatedAt;
71 |
72 | @Column(name = "tan_hash")
73 | private String tanHash;
74 |
75 | @Column(name = "valid_from")
76 | private LocalDateTime validFrom;
77 |
78 | @Column(name = "valid_until")
79 | private LocalDateTime validUntil;
80 |
81 | @Column(name = "sot")
82 | @Enumerated(EnumType.STRING)
83 | private TanSourceOfTrust sourceOfTrust;
84 |
85 | @Column(name = "redeemed")
86 | private boolean redeemed;
87 |
88 | @Column(name = "type")
89 | @Enumerated(EnumType.STRING)
90 | private TanType type;
91 |
92 | @Column(name = "teletan_type")
93 | @Enumerated(EnumType.STRING)
94 | private TeleTanType teleTanType;
95 |
96 | /**
97 | * Check if the tan can be redeemed by date.
98 | *
99 | * @param reference the date to check if it is in between from and until range
100 | * @return true or false if it can be redeemed
101 | */
102 | public boolean canBeRedeemed(LocalDateTime reference) {
103 | return validFrom.isBefore(reference)
104 | && validUntil.isAfter(reference)
105 | && !isRedeemed();
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/exception/VerificationServerException.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.exception;
22 |
23 | import lombok.Getter;
24 | import org.springframework.http.HttpStatus;
25 |
26 | /**
27 | * This class represents the Verification Server Exception.
28 | */
29 | @Getter
30 | public class VerificationServerException extends RuntimeException {
31 |
32 | private final HttpStatus httpStatus;
33 |
34 | /**
35 | * The Constructor for the Exception class.
36 | *
37 | * @param httpStatus the state of the server
38 | * @param message the message
39 | */
40 | public VerificationServerException(HttpStatus httpStatus, String message) {
41 | super(message);
42 | this.httpStatus = httpStatus;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/AppSessionSourceOfTrust.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | /**
24 | * This class represents the possible sources of trust for an appsession entity.
25 | *
26 | * @see Entity AppSession - sourceOfTrust
27 | */
28 | public enum AppSessionSourceOfTrust {
29 | HASHED_GUID,
30 | TELETAN
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/AuthorizationRole.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import lombok.Getter;
24 |
25 | /**
26 | * The different possible roles, which are authorizated to create a tele tan.
27 | *
28 | * @see
29 | *
30 | * Use Case - Create TeleTan
31 | */
32 | @Getter
33 | public enum AuthorizationRole {
34 | AUTH_C19_HOTLINE("c19hotline"),
35 | AUTH_C19_HEALTHAUTHORITY("c19healthauthority"),
36 | AUTH_C19_HOTLINE_EVENT("c19hotline_event");
37 |
38 | private final String roleName;
39 |
40 | AuthorizationRole(final String role) {
41 | this.roleName = role;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/AuthorizationToken.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import io.swagger.v3.oas.annotations.media.Schema;
24 | import jakarta.validation.constraints.NotNull;
25 | import lombok.AllArgsConstructor;
26 | import lombok.Data;
27 | import lombok.NoArgsConstructor;
28 |
29 | /**
30 | * This class represents the authorization bearer token (JWT), which is used for the
31 | * creation of a teleTan.
32 | */
33 | @Schema(
34 | description = "The bearer jwt token header model."
35 | )
36 | @Data
37 | @NoArgsConstructor
38 | @AllArgsConstructor
39 | public class AuthorizationToken {
40 |
41 | @NotNull
42 | private String token;
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/Certs.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import java.util.HashMap;
24 | import java.util.List;
25 | import java.util.Map;
26 | import lombok.AllArgsConstructor;
27 | import lombok.Data;
28 | import lombok.NoArgsConstructor;
29 |
30 | @Data
31 | @NoArgsConstructor
32 | @AllArgsConstructor
33 | public class Certs {
34 |
35 | private List keys = null;
36 | private Map additionalProperties = new HashMap<>();
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/HashedGuid.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import io.swagger.v3.oas.annotations.media.Schema;
24 | import lombok.AllArgsConstructor;
25 | import lombok.Data;
26 | import lombok.NoArgsConstructor;
27 |
28 | /**
29 | * This class represents the hashed Guid.
30 | * Hash (SHA256) aka QR-Code, GUID encoded as hex string.
31 | *
32 | * @see Core Entities
33 | */
34 | @Schema(
35 | description = "The hashed Guid request model."
36 | )
37 | @Data
38 | @NoArgsConstructor
39 | @AllArgsConstructor
40 | public class HashedGuid {
41 | private String id;
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/InternalTestResult.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import io.swagger.v3.oas.annotations.media.Schema;
24 | import lombok.AllArgsConstructor;
25 | import lombok.Getter;
26 | import lombok.NoArgsConstructor;
27 |
28 |
29 | /**
30 | * This class represents the TestResult.
31 | *
32 | * @see Core Entities
33 | */
34 | @Schema(
35 | description = "The test result model for internal test result requests."
36 | )
37 | @NoArgsConstructor
38 | @AllArgsConstructor
39 | @Getter
40 | public class InternalTestResult extends TestResult {
41 |
42 | private String testId;
43 |
44 | public InternalTestResult(int testResult, long sc, String labId, String responsePadding, String testId) {
45 | super(testResult, sc, labId, responsePadding);
46 | this.testId = testId;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/Key.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import java.util.HashMap;
24 | import java.util.List;
25 | import java.util.Map;
26 | import lombok.AllArgsConstructor;
27 | import lombok.Data;
28 | import lombok.NoArgsConstructor;
29 |
30 | @Data
31 | @NoArgsConstructor
32 | @AllArgsConstructor
33 | public class Key {
34 | public static final String SIG = "sig";
35 | public static final String RS256 = "RS256";
36 | private String kid;
37 | private String kty;
38 | private String alg;
39 | private String use;
40 | private String nn;
41 | private String ee;
42 | private List x5c = null;
43 | private String x5t;
44 | private String x5tS256;
45 | private final Map additionalProperties = new HashMap<>();
46 |
47 | /**
48 | * Check if the cert is valid for use.
49 | * @return true
, if the cert has the right use and alg keys, otherwise false
50 | */
51 |
52 | public boolean isCertValid() {
53 | return getUse().equals(SIG) && getAlg().equals(RS256);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/LabTestResult.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import io.swagger.v3.oas.annotations.media.Schema;
24 | import lombok.Getter;
25 |
26 | /**
27 | * The possible result results of the COVID-19 lab-test.
28 | * Pending = 0 : The test result does not exist yet
29 | * Negative = 1 : No indication for COVID-19
30 | * Positive = 2 : The test result indicates infection with COVID-19
31 | * Invalid = 3 : The test result is invalid due to unknown reason
32 | * Redeemed = 4 : The test result is redeemed by time
33 | */
34 |
35 | @Schema(
36 | description = "The lab test result model."
37 | )
38 | @Getter
39 | public enum LabTestResult {
40 | PENDING(0),
41 | NEGATIVE(1),
42 | POSITIVE(2),
43 | INVALID(3),
44 | REDEEMED(4),
45 | QUICK_PENDING(5),
46 | QUICK_NEGATIVE(6),
47 | QUICK_POSITIVE(7),
48 | QUICK_INVALID(8),
49 | QUICK_REDEEMED(9);
50 |
51 | /**
52 | * The identifier for the test result from the lab-server.
53 | */
54 | private final int testResult;
55 |
56 | /**
57 | * The constructor.
58 | *
59 | * @param stateValue the lab test state
60 | */
61 | LabTestResult(final int stateValue) {
62 | this.testResult = stateValue;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/RegistrationToken.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import com.fasterxml.jackson.annotation.JsonInclude;
24 | import io.swagger.v3.oas.annotations.media.Schema;
25 | import jakarta.persistence.Transient;
26 | import jakarta.validation.constraints.NotNull;
27 | import jakarta.validation.constraints.Pattern;
28 | import lombok.AllArgsConstructor;
29 | import lombok.Data;
30 | import lombok.NoArgsConstructor;
31 | import lombok.NonNull;
32 | import lombok.RequiredArgsConstructor;
33 |
34 | /**
35 | * This class represents the registration Token.
36 | */
37 | @Schema(
38 | description = "The registration token model."
39 | )
40 | @Data
41 | @NoArgsConstructor
42 | @RequiredArgsConstructor
43 | @AllArgsConstructor
44 | public class RegistrationToken {
45 |
46 | @NonNull
47 | @NotNull
48 | @Pattern(regexp = "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}$")
49 | private String registrationToken;
50 |
51 | @JsonInclude(JsonInclude.Include.NON_NULL)
52 | @Transient
53 | private String responsePadding;
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/RegistrationTokenKeyType.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | /**
24 | * This class represent the different key types, with whom you can generate a registration token.
25 | */
26 | public enum RegistrationTokenKeyType {
27 |
28 | /**
29 | * The key type GUID(Hash).
30 | */
31 | GUID,
32 | /**
33 | * The key type TeleTaN, which can be generated by the hotline or a health
34 | * authority.
35 | */
36 | TELETAN
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/RegistrationTokenRequest.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import app.coronawarn.verification.validator.RegistrationTokenKeyConstraint;
24 | import io.swagger.v3.oas.annotations.media.Schema;
25 | import jakarta.validation.constraints.NotNull;
26 | import jakarta.validation.constraints.Pattern;
27 | import lombok.AllArgsConstructor;
28 | import lombok.Data;
29 | import lombok.NoArgsConstructor;
30 |
31 | /**
32 | * This class represents a registration token request parameter with a hashed guid or a teleTAN.
33 | */
34 | @Schema(
35 | description = "The registration token request model."
36 | )
37 | @Data
38 | @NoArgsConstructor
39 | @AllArgsConstructor
40 | @RegistrationTokenKeyConstraint
41 | public class RegistrationTokenRequest {
42 |
43 | /**
44 | * The key which can be a teletan or a hashed guid.
45 | */
46 | @NotNull
47 | private String key;
48 |
49 | /**
50 | * The hashed GUID built with date of birth.
51 | */
52 | @Pattern(regexp = "^[XxA-Fa-f0-9]([A-Fa-f0-9]{63})$")
53 | private String keyDob;
54 |
55 | /**
56 | * The type of key, which can be "GUID" or "TELETAN".
57 | */
58 | @NotNull
59 | private RegistrationTokenKeyType keyType;
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/Tan.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import com.fasterxml.jackson.annotation.JsonInclude;
24 | import io.swagger.v3.oas.annotations.media.Schema;
25 | import jakarta.persistence.Transient;
26 | import jakarta.validation.constraints.NotNull;
27 | import jakarta.validation.constraints.Pattern;
28 | import lombok.AllArgsConstructor;
29 | import lombok.Data;
30 | import lombok.NoArgsConstructor;
31 | import lombok.NonNull;
32 | import lombok.RequiredArgsConstructor;
33 |
34 | /**
35 | * This class represents the transaction number.
36 | */
37 | @Schema(
38 | description = "The transaction number model."
39 | )
40 | @Data
41 | @AllArgsConstructor
42 | @RequiredArgsConstructor
43 | @NoArgsConstructor
44 | public class Tan {
45 |
46 | @NotNull
47 | @NonNull
48 | @Pattern(regexp = "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}$")
49 | private String tan;
50 |
51 | @JsonInclude(JsonInclude.Include.NON_NULL)
52 | @Transient
53 | private String responsePadding;
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/TanSourceOfTrust.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | /**
24 | * This class represents the possible sources of trust for a TAN entity.
25 | *
26 | * @see Entity TAN - sourceOfTrust
27 | */
28 | public enum TanSourceOfTrust {
29 | CONNECTED_LAB,
30 | TELETAN
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/TanType.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import io.swagger.v3.oas.annotations.media.Schema;
24 |
25 | /**
26 | * This class represents the different types of tans.
27 | *
28 | * @see Entity TAN - Type
29 | */
30 | @Schema
31 | public enum TanType {
32 | /**
33 | * The general tan.
34 | */
35 | TAN,
36 |
37 | /**
38 | * The TeleTAN, which was created by, e.g the hotline.
39 | */
40 | TELETAN
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/TeleTan.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import io.swagger.v3.oas.annotations.media.Schema;
24 | import lombok.AllArgsConstructor;
25 | import lombok.Data;
26 | import lombok.NoArgsConstructor;
27 |
28 | /**
29 | * This class represents the tele transaction number (teleTAN).
30 | */
31 | @Schema(
32 | description = "The teleTAN model."
33 | )
34 | @Data
35 | @NoArgsConstructor
36 | @AllArgsConstructor
37 | public class TeleTan {
38 | private String value;
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/TeleTanType.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import io.swagger.v3.oas.annotations.media.Schema;
24 |
25 | @Schema(
26 | description = "The TeleTan Type model."
27 | )
28 | public enum TeleTanType {
29 |
30 | @Schema(description = "TeleTan is issued because of a positive PCR Test")
31 | TEST,
32 |
33 | @Schema(description = "TeleTan is issued because of a confirmed infection at an event.")
34 | EVENT
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/model/TestResult.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.model;
22 |
23 | import com.fasterxml.jackson.annotation.JsonInclude;
24 | import io.swagger.v3.oas.annotations.media.Schema;
25 | import jakarta.persistence.Transient;
26 | import lombok.AllArgsConstructor;
27 | import lombok.Data;
28 | import lombok.NoArgsConstructor;
29 |
30 |
31 | /**
32 | * This class represents the TestResult.
33 | *
34 | * @see Core Entities
35 | */
36 | @Schema(
37 | description = "The test result model."
38 | )
39 | @Data
40 | @NoArgsConstructor
41 | @AllArgsConstructor
42 | public class TestResult {
43 |
44 | private int testResult;
45 |
46 | private long sc;
47 |
48 | @JsonInclude(JsonInclude.Include.NON_NULL)
49 | private String labId;
50 |
51 | @JsonInclude(JsonInclude.Include.NON_NULL)
52 | @Transient
53 | private String responsePadding;
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/repository/VerificationAppSessionRepository.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.repository;
22 |
23 | import app.coronawarn.verification.domain.VerificationAppSession;
24 | import java.time.LocalDateTime;
25 | import java.util.Optional;
26 | import org.springframework.data.jpa.repository.JpaRepository;
27 | import org.springframework.data.jpa.repository.Modifying;
28 | import org.springframework.data.jpa.repository.Query;
29 |
30 | /**
31 | * This class represents the AppSession repository.
32 | */
33 | public interface VerificationAppSessionRepository extends JpaRepository {
34 |
35 | /**
36 | * This method looks in the Database for an Appsession with the given registrationTokenHash.
37 | *
38 | * @param registrationTokenHash hash to search for
39 | * @return Optional VerificationAppSession the optional Appsession
40 | */
41 | Optional findByRegistrationTokenHash(String registrationTokenHash);
42 |
43 | /**
44 | * This method looks in the Database for an Appsession with the given hashedGuid.
45 | *
46 | * @param hashedGuid hash to search for
47 | * @param hashedGuidDob hash to search for
48 | * @return Optional VerificationAppSession the optional Appsession
49 | */
50 | Optional findByHashedGuidOrHashedGuidDob(String hashedGuid, String hashedGuidDob);
51 |
52 | /**
53 | * This method looks in the Database for an Appsession with the given teleTanHash.
54 | *
55 | * @param teleTanHash hash to search for
56 | * @return Optional VerificationAppSession the optional Appsession
57 | */
58 | Optional findByTeleTanHash(String teleTanHash);
59 |
60 | /**
61 | * This method looks in the Database for Appsessions that are older than the before value and deletes them.
62 | *
63 | * @param before the Date to delete by
64 | */
65 | @Modifying
66 | @Query("delete from VerificationAppSession a where a.createdAt < ?1")
67 | void deleteByCreatedAtBefore(LocalDateTime before);
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/repository/VerificationTanRepository.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.repository;
22 |
23 | import app.coronawarn.verification.domain.VerificationTan;
24 | import app.coronawarn.verification.model.TanType;
25 | import java.time.LocalDateTime;
26 | import java.util.Optional;
27 | import org.springframework.data.jpa.repository.JpaRepository;
28 | import org.springframework.data.jpa.repository.Modifying;
29 | import org.springframework.data.jpa.repository.Query;
30 |
31 | /**
32 | * This class represents the Tan repository.
33 | */
34 | public interface VerificationTanRepository extends JpaRepository {
35 |
36 | /**
37 | * This method looks in the Database for an if a VerificationTan exists for the tan hash.
38 | *
39 | * @param tanHash hash to search for
40 | * @return Boolean if there is an Entity for the tanHash
41 | */
42 | boolean existsByTanHash(String tanHash);
43 |
44 | /**
45 | * This method looks in the Database for an if a VerificationTan exists for the tan hash.
46 | *
47 | * @param tanHash hash to search for
48 | * @return Optional VerificationTan
49 | */
50 | Optional findByTanHash(String tanHash);
51 |
52 | /**
53 | * This method purges Entities from the database that are older than before value.
54 | *
55 | * @param before LocalDateTime to delete older entities
56 | */
57 | @Modifying
58 | @Query("delete from VerificationTan a where a.createdAt < ?1")
59 | void deleteByCreatedAtBefore(LocalDateTime before);
60 |
61 | /**
62 | * This method counts entities which are newer then after value.
63 | *
64 | * @param after - LocalDateTime to count entities
65 | * @param tanType - TanType of the tans that should be counted
66 | * @return number of relevant entities
67 | */
68 | int countByCreatedAtIsAfterAndTypeIs(LocalDateTime after, TanType tanType);
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/service/AppSessionService.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import app.coronawarn.verification.domain.VerificationAppSession;
24 | import app.coronawarn.verification.exception.VerificationServerException;
25 | import app.coronawarn.verification.model.AppSessionSourceOfTrust;
26 | import app.coronawarn.verification.model.RegistrationToken;
27 | import app.coronawarn.verification.model.TeleTanType;
28 | import app.coronawarn.verification.repository.VerificationAppSessionRepository;
29 | import java.time.LocalDateTime;
30 | import java.util.Optional;
31 | import java.util.UUID;
32 | import lombok.NonNull;
33 | import lombok.RequiredArgsConstructor;
34 | import lombok.extern.slf4j.Slf4j;
35 | import org.apache.commons.lang3.RandomStringUtils;
36 | import org.springframework.dao.DataIntegrityViolationException;
37 | import org.springframework.http.HttpStatus;
38 | import org.springframework.http.ResponseEntity;
39 | import org.springframework.stereotype.Component;
40 |
41 | /**
42 | * This class represents the VerificationAppSession service.
43 | */
44 | @Slf4j
45 | @RequiredArgsConstructor
46 | @Component
47 | public class AppSessionService {
48 |
49 | private static final Integer TOKEN_PADDING_LENGTH = 1;
50 | /**
51 | * The {@link VerificationAppSessionRepository}.
52 | */
53 | @NonNull
54 | private final VerificationAppSessionRepository appSessionRepository;
55 |
56 | /**
57 | * The {@link HashingService}.
58 | */
59 | @NonNull
60 | private final HashingService hashingService;
61 |
62 | /**
63 | * Creates an AppSession-Entity.
64 | *
65 | * @param registrationToken Token for registration
66 | * @return appSession for registrationToken
67 | */
68 | public VerificationAppSession generateAppSession(String registrationToken) {
69 | log.info("Create the app session entity with the created registration token.");
70 | VerificationAppSession appSession = new VerificationAppSession();
71 | appSession.setCreatedAt(LocalDateTime.now());
72 | appSession.setUpdatedAt(LocalDateTime.now());
73 | appSession.setRegistrationTokenHash(hashingService.hash(registrationToken));
74 | return appSession;
75 | }
76 |
77 | private String generateRegistrationToken() {
78 | return UUID.randomUUID().toString();
79 | }
80 |
81 | /**
82 | * This method generates a registration Token by a guid .
83 | *
84 | * @param hashedGuid the hashed guid
85 | * @return an {@link ResponseEntity}
86 | */
87 | public ResponseEntity generateRegistrationTokenByGuid(
88 | String hashedGuid, String hashedGuidDob, String fake) {
89 |
90 | if (checkRegistrationTokenAlreadyExistsForGuid(hashedGuid)) {
91 | log.warn("The registration token already exists for the hashed guid.");
92 | return ResponseEntity.badRequest().build();
93 | }
94 |
95 | if (hashedGuidDob != null && checkRegistrationTokenAlreadyExistsForGuid(hashedGuidDob)) {
96 | log.warn("The registration token already exists for the hashed guid dob.");
97 | return ResponseEntity.badRequest().build();
98 | }
99 |
100 | log.info("Start generating a new registration token for the given hashed guid.");
101 |
102 | String registrationToken = generateRegistrationToken();
103 | VerificationAppSession appSession = generateAppSession(registrationToken);
104 | appSession.setHashedGuid(hashedGuid);
105 | appSession.setHashedGuidDob(hashedGuidDob);
106 | appSession.setSourceOfTrust(AppSessionSourceOfTrust.HASHED_GUID);
107 |
108 | try {
109 | saveAppSession(appSession);
110 | } catch (DataIntegrityViolationException e) {
111 | log.error("Failed to save RegistrationToken because of Hashed GUID Conflict: {}", hashedGuid);
112 | throw new VerificationServerException(
113 | HttpStatus.BAD_REQUEST, "Failed to save RegistrationToken because of Hashed GUID Conflict");
114 | }
115 |
116 | log.info("Returning the successfully created registration token.");
117 | return ResponseEntity.status(HttpStatus.CREATED).body(
118 | getBackwardCompatibleRegistrationToken(registrationToken, fake));
119 |
120 | }
121 |
122 | /**
123 | * This method generates a registration Token by a TeleTAN.
124 | *
125 | * @param teleTan the TeleTan
126 | * @return an {@link ResponseEntity}
127 | */
128 | public ResponseEntity generateRegistrationTokenByTeleTan(
129 | String teleTan, String fake, TeleTanType teleTanType) {
130 | if (checkRegistrationTokenAlreadyExistForTeleTan(teleTan)) {
131 | log.warn("The registration token already exists for this TeleTAN.");
132 | return ResponseEntity.badRequest().build();
133 | } else {
134 | log.info("Start generating a new registration token for the given TeleTAN.");
135 | String registrationToken = generateRegistrationToken();
136 | VerificationAppSession appSession = generateAppSession(registrationToken);
137 | appSession.setTeleTanHash(hashingService.hash(teleTan));
138 | appSession.setSourceOfTrust(AppSessionSourceOfTrust.TELETAN);
139 | appSession.setTeleTanType(teleTanType);
140 | saveAppSession(appSession);
141 | log.info("Returning the successfully created registration token.");
142 | return ResponseEntity.status(HttpStatus.CREATED).body(
143 | getBackwardCompatibleRegistrationToken(registrationToken, fake));
144 | }
145 | }
146 |
147 | /**
148 | * Persists the specified entity of {@link VerificationAppSession} instances.
149 | *
150 | * @param appSession the verification app session entity
151 | */
152 | public void saveAppSession(VerificationAppSession appSession) {
153 | log.info("Start saveAppSession.");
154 | appSessionRepository.save(appSession);
155 | }
156 |
157 | /**
158 | * Get existing VerificationAppSession for Reg Token from {@link VerificationAppSessionRepository}.
159 | *
160 | * @param registrationToken the registrationToken
161 | * @return Optional VerificationAppSession
162 | */
163 | public Optional getAppSessionByToken(String registrationToken) {
164 | log.info("Start getAppSessionByToken.");
165 | return appSessionRepository.findByRegistrationTokenHash(hashingService.hash(registrationToken));
166 | }
167 |
168 | /**
169 | * Check for existing hashed GUID Token in the {@link VerificationAppSessionRepository}.
170 | *
171 | * @param hashedGuid the hashed guid
172 | * @return flag for existing guid
173 | */
174 | public boolean checkRegistrationTokenAlreadyExistsForGuid(String hashedGuid) {
175 | log.info("Start checkRegistrationTokenAlreadyExistsForGuid.");
176 | return appSessionRepository.findByHashedGuidOrHashedGuidDob(hashedGuid, hashedGuid).isPresent();
177 | }
178 |
179 | /**
180 | * Check for existing hashed TeleTAN in the {@link VerificationAppSessionRepository}.
181 | *
182 | * @param teleTan the teleTAN
183 | * @return flag for existing teleTAN
184 | */
185 | public boolean checkRegistrationTokenAlreadyExistForTeleTan(String teleTan) {
186 | log.info("Start checkTeleTanAlreadyExistForTeleTan.");
187 | return appSessionRepository.findByTeleTanHash(hashingService.hash(teleTan)).isPresent();
188 | }
189 |
190 | private RegistrationToken getBackwardCompatibleRegistrationToken(String registrationToken, String fake) {
191 | if (fake == null) {
192 | return new RegistrationToken(registrationToken);
193 | }
194 | return new RegistrationToken(registrationToken, RandomStringUtils.randomAlphanumeric(TOKEN_PADDING_LENGTH));
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/service/EntitiesCleanupService.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import app.coronawarn.verification.config.VerificationApplicationConfig;
24 | import app.coronawarn.verification.repository.VerificationAppSessionRepository;
25 | import app.coronawarn.verification.repository.VerificationTanRepository;
26 | import jakarta.transaction.Transactional;
27 | import java.time.LocalDateTime;
28 | import java.time.Period;
29 | import lombok.RequiredArgsConstructor;
30 | import lombok.extern.slf4j.Slf4j;
31 | import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
32 | import org.springframework.scheduling.annotation.Scheduled;
33 | import org.springframework.stereotype.Component;
34 |
35 | /**
36 | * A Service to delete entities that are older than configured days.
37 | */
38 | @Slf4j
39 | @RequiredArgsConstructor
40 | @Component
41 | public class EntitiesCleanupService {
42 |
43 | private final VerificationApplicationConfig applicationConfig;
44 | private final VerificationAppSessionRepository appSessionRepository;
45 | private final VerificationTanRepository tanRepository;
46 |
47 | /**
48 | * All entities that are older than configured days get deleted.
49 | */
50 | @Scheduled(
51 | cron = "${entities.cleanup.cron}"
52 | )
53 | @SchedulerLock(name = "VerificationCleanupService_cleanup", lockAtLeastFor = "PT0S",
54 | lockAtMostFor = "${entities.cleanup.locklimit}")
55 | @Transactional
56 | public void cleanup() {
57 | log.info("cleanup execution");
58 | appSessionRepository.deleteByCreatedAtBefore(LocalDateTime.now()
59 | .minus(Period.ofDays(applicationConfig.getEntities().getCleanup().getDays())));
60 | tanRepository.deleteByCreatedAtBefore(LocalDateTime.now()
61 | .minus(Period.ofDays(applicationConfig.getEntities().getCleanup().getDays())));
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/service/FakeDelayService.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import app.coronawarn.verification.config.VerificationApplicationConfig;
24 | import org.apache.commons.math3.distribution.PoissonDistribution;
25 | import org.springframework.stereotype.Component;
26 |
27 | /**
28 | * {@link FakeDelayService} instances manage the response delay in the processing of fake (or "dummy") requests.
29 | */
30 | @Component
31 | public class FakeDelayService {
32 |
33 | private final long movingAverageSampleSize;
34 | private long fakeDelayTest;
35 |
36 | private long fakeDelayTan;
37 |
38 | private long fakeDelayToken;
39 |
40 | /**
41 | * Constructor for the FakeDelayService.
42 | */
43 | public FakeDelayService(VerificationApplicationConfig applicationConfig) {
44 | this.fakeDelayTest = applicationConfig.getInitialFakeDelayMilliseconds();
45 | this.fakeDelayTan = applicationConfig.getInitialFakeDelayMilliseconds();
46 | this.fakeDelayToken = applicationConfig.getInitialFakeDelayMilliseconds();
47 | this.movingAverageSampleSize = applicationConfig.getFakeDelayMovingAverageSamples();
48 | }
49 |
50 | /**
51 | * Returns the current fake delay after applying random jitter.
52 | */
53 | public long getJitteredFakeTanDelay() {
54 | return new PoissonDistribution(fakeDelayTan).sample();
55 | }
56 |
57 | /**
58 | * Returns the current fake delay after applying random jitter.
59 | */
60 | public long getJitteredFakeTestDelay() {
61 | return new PoissonDistribution(fakeDelayTest).sample();
62 | }
63 |
64 | /**
65 | * Returns the current fake delay after applying random jitter.
66 | */
67 | public long getJitteredFakeTokenDelay() {
68 | return new PoissonDistribution(fakeDelayToken).sample();
69 | }
70 |
71 | /**
72 | * Updates the moving average for the request duration for the Tan Endpoint with the specified value.
73 | */
74 | public void updateFakeTanRequestDelay(long realRequestDuration) {
75 | final long currentDelay = fakeDelayTan;
76 | fakeDelayTan = currentDelay + (realRequestDuration - currentDelay) / movingAverageSampleSize;
77 | }
78 |
79 | /**
80 | * Updates the moving average for the request duration for the Tan Endpoint with the specified value.
81 | */
82 | public void updateFakeTestRequestDelay(long realRequestDuration) {
83 | final long currentDelay = fakeDelayTest;
84 | fakeDelayTan = currentDelay + (realRequestDuration - currentDelay) / movingAverageSampleSize;
85 | }
86 |
87 | /**
88 | * Updates the moving average for the request duration for the Tan Endpoint with the specified value.
89 | */
90 | public void updateFakeTokenRequestDelay(long realRequestDuration) {
91 | final long currentDelay = fakeDelayToken;
92 | fakeDelayTan = currentDelay + (realRequestDuration - currentDelay) / movingAverageSampleSize;
93 | }
94 |
95 | /**
96 | * Returns the current fake delay in seconds. Used for monitoring.
97 | */
98 | public Double getFakeTanDelayInSeconds() {
99 | return fakeDelayTan / 1000.;
100 | }
101 |
102 | /**
103 | * Returns the current fake delay in seconds. Used for monitoring.
104 | */
105 | public Double getFakeTestDelayInSeconds() {
106 | return fakeDelayTest / 1000.;
107 | }
108 |
109 | /**
110 | * Returns the current fake delay in seconds. Used for monitoring.
111 | */
112 | public Double getFakeTokenDelayInSeconds() {
113 | return fakeDelayToken / 1000.;
114 | }
115 |
116 | /**
117 | * Returns the longest fake delay jittered in milliseconds.
118 | * @return longest jittered
119 | */
120 | public long getLongestJitter() {
121 | if ((fakeDelayTan > fakeDelayTest) && (fakeDelayTan > fakeDelayToken)) {
122 | return getJitteredFakeTanDelay();
123 | } else if ((fakeDelayToken > fakeDelayTest) && (fakeDelayToken > fakeDelayTan)) {
124 | return getJitteredFakeTokenDelay();
125 | } else {
126 | return getJitteredFakeTestDelay();
127 | }
128 | }
129 |
130 | /**
131 | * Returns the longest fake delay minus average time for Tan in milliseconds.
132 | * @return delay for TAN
133 | */
134 | public long realDelayTan() {
135 | return (getLongestJitter() - getJitteredFakeTanDelay());
136 | }
137 |
138 | /**
139 | * Returns the longest fake delay minus average time for RegistrationToken in milliseconds.
140 | * @return delay for RegistrationToken
141 | */
142 | public long realDelayToken() {
143 | return (getLongestJitter() - getJitteredFakeTokenDelay());
144 | }
145 |
146 | /**
147 | * Returns the longest fake delay minus average time for TestResult in milliseconds.
148 | * @return delay for TestResult
149 | */
150 | public long realDelayTest() {
151 | return (getLongestJitter() - getJitteredFakeTestDelay());
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/service/FakeRequestService.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
24 |
25 | import app.coronawarn.verification.model.LabTestResult;
26 | import app.coronawarn.verification.model.RegistrationToken;
27 | import app.coronawarn.verification.model.RegistrationTokenRequest;
28 | import app.coronawarn.verification.model.Tan;
29 | import app.coronawarn.verification.model.TestResult;
30 | import jakarta.validation.Valid;
31 | import java.time.LocalDateTime;
32 | import java.time.ZoneOffset;
33 | import java.util.UUID;
34 | import java.util.concurrent.Executors;
35 | import java.util.concurrent.ScheduledExecutorService;
36 | import lombok.NonNull;
37 | import lombok.RequiredArgsConstructor;
38 | import lombok.extern.slf4j.Slf4j;
39 | import org.apache.commons.lang3.RandomStringUtils;
40 | import org.springframework.http.HttpStatus;
41 | import org.springframework.http.ResponseEntity;
42 | import org.springframework.stereotype.Service;
43 | import org.springframework.web.bind.annotation.RequestBody;
44 | import org.springframework.web.context.request.async.DeferredResult;
45 |
46 |
47 | /**
48 | * This Service generates the fake responses for the Endpoints.
49 | */
50 | @Slf4j
51 | @Service
52 | @RequiredArgsConstructor
53 | public class FakeRequestService {
54 |
55 | @NonNull
56 | private final FakeDelayService fakeDelayService;
57 |
58 | private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4);
59 |
60 | private static final Integer TEST_RESPONSE_PADDING_LENGTH = 45;
61 | private static final Integer TESTRESULT_RESULT_PADDING = 1;
62 | private static final Integer TAN_RESPONSE_PADDING_LENGTH = 15;
63 |
64 |
65 | /**
66 | * This method generates a fake transaction number by a Registration Token, if the state of the COVID-19 lab-test is
67 | * positive.
68 | *
69 | * @param registrationToken generated by a hashed guid or a teleTAN. {@link RegistrationToken}
70 | * @return A generated transaction number {@link Tan}.
71 | */
72 | public DeferredResult> generateTan(@Valid @RequestBody RegistrationToken registrationToken) {
73 | long delay = fakeDelayService.getLongestJitter();
74 | DeferredResult> deferredResult = new DeferredResult<>();
75 | Tan returnTan = new Tan(UUID.randomUUID().toString(),
76 | RandomStringUtils.randomAlphanumeric(TAN_RESPONSE_PADDING_LENGTH));
77 | scheduledExecutor.schedule(() -> deferredResult.setResult(ResponseEntity.status(HttpStatus.CREATED)
78 | .body(returnTan)), delay, MILLISECONDS);
79 | return deferredResult;
80 | }
81 |
82 |
83 | /**
84 | * This method generates a fake registrationToken by a hashed guid or a teleTAN.
85 | *
86 | * @param request {@link RegistrationTokenRequest}
87 | * @return RegistrationToken - the created registration token {@link RegistrationToken}
88 | */
89 | public DeferredResult> generateRegistrationToken(
90 | @RequestBody @Valid RegistrationTokenRequest request) {
91 | long delay = fakeDelayService.getLongestJitter();
92 | DeferredResult> deferredResult = new DeferredResult<>();
93 | scheduledExecutor.schedule(() -> deferredResult.setResult(ResponseEntity.status(HttpStatus.CREATED)
94 | .body(new RegistrationToken(UUID.randomUUID().toString(),
95 | RandomStringUtils.randomAlphanumeric(TESTRESULT_RESULT_PADDING)))), delay, MILLISECONDS);
96 | return deferredResult;
97 | }
98 |
99 |
100 | /**
101 | * Returns the fake test status of the COVID-19 test.
102 | *
103 | * @param registrationToken generated by a hashed guid {@link RegistrationToken}
104 | * @return the test result / status of the COVID-19 test, which can be POSITIVE, NEGATIVE, INVALID, PENDING or FAILED
105 | * and will always be POSITIVE for a TeleTan.
106 | */
107 | public DeferredResult> getTestState(
108 | @Valid @RequestBody RegistrationToken registrationToken) {
109 | long delay = fakeDelayService.getLongestJitter();
110 | DeferredResult> deferredResult = new DeferredResult<>();
111 | scheduledExecutor.schedule(() -> deferredResult.setResult(ResponseEntity
112 | .ok(new TestResult(LabTestResult.POSITIVE.getTestResult(), LocalDateTime.now().toEpochSecond(ZoneOffset.UTC),
113 | null, RandomStringUtils.randomAlphanumeric(TEST_RESPONSE_PADDING_LENGTH)))), delay, MILLISECONDS);
114 | return deferredResult;
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/service/HashingService.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import java.util.regex.Matcher;
24 | import java.util.regex.Pattern;
25 | import lombok.extern.slf4j.Slf4j;
26 | import org.apache.commons.codec.digest.DigestUtils;
27 | import org.springframework.stereotype.Component;
28 |
29 | /**
30 | * This class represents the hashing service for providing and check a hash string.
31 | */
32 | @Slf4j
33 | @Component
34 | public class HashingService {
35 |
36 | private static final String GUID_HASH_PATTERN = "^[0-9A-Fa-f]{64}$";
37 | private static final Pattern PATTERN = Pattern.compile(GUID_HASH_PATTERN);
38 |
39 | /**
40 | * Calculates the SHA-256 digest and returns the value as a hex string.
41 | *
42 | * @param toHash that will be Hashed
43 | * @return the hash of the supplied string
44 | */
45 | public String hash(String toHash) {
46 | log.debug("Hash process has been called.");
47 | return DigestUtils.sha256Hex(toHash);
48 | }
49 |
50 | /**
51 | * Calculates the SHA-256 digest and returns an check digit.
52 | *
53 | * @param toHash that will be Hashed
54 | * @return the check digit
55 | */
56 | public String getCheckDigit(String toHash) {
57 | log.info("get check digit process has been called.");
58 | return DigestUtils.sha256Hex(toHash).substring(0, 1).toUpperCase().replace("0", "G").replace("1", "H");
59 | }
60 |
61 | /**
62 | * Returns true if the String is resembles a SHA256 Pattern.
63 | *
64 | * @param toValidate String that will be checked to match the pattern of a SHA256 Hash
65 | * @return Boolean if the String Matches the Pattern
66 | */
67 | public boolean isHashValid(String toValidate) {
68 | Matcher matcher = PATTERN.matcher(toValidate);
69 | boolean matches = matcher.matches();
70 | if (!matches) {
71 | log.warn("The hashed guid has no valid pattern");
72 | }
73 | return matches;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/service/JwtService.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import app.coronawarn.verification.client.IamClient;
24 | import app.coronawarn.verification.config.VerificationApplicationConfig;
25 | import app.coronawarn.verification.model.AuthorizationRole;
26 | import app.coronawarn.verification.model.Certs;
27 | import app.coronawarn.verification.model.Key;
28 | import io.jsonwebtoken.Claims;
29 | import io.jsonwebtoken.JwtException;
30 | import io.jsonwebtoken.Jwts;
31 | import java.io.ByteArrayInputStream;
32 | import java.io.InputStream;
33 | import java.security.PublicKey;
34 | import java.security.cert.CertificateException;
35 | import java.security.cert.CertificateFactory;
36 | import java.security.cert.X509Certificate;
37 | import java.util.ArrayList;
38 | import java.util.List;
39 | import java.util.Map;
40 | import java.util.function.Function;
41 | import lombok.NonNull;
42 | import lombok.RequiredArgsConstructor;
43 | import lombok.extern.slf4j.Slf4j;
44 | import org.springframework.stereotype.Component;
45 |
46 | /**
47 | * This class represents the JWT service for token authorization and validation.
48 | */
49 | @Slf4j
50 | @RequiredArgsConstructor
51 | @Component
52 | public class JwtService {
53 |
54 | /**
55 | * The bearer prefix for the json web token.
56 | */
57 | public static final String TOKEN_PREFIX = "Bearer ";
58 | /**
59 | * The certificate begin prefix.
60 | */
61 | public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
62 | /**
63 | * The certificate end suffix.
64 | */
65 | public static final String END_CERT = "-----END CERTIFICATE-----";
66 | /**
67 | * The http request header name for JWT 'Authorization'.
68 | */
69 | public static final String HEADER_NAME_AUTHORIZATION = "Authorization";
70 |
71 | private static final String ROLES = "roles";
72 | private static final String REALM_ACCESS = "realm_access";
73 |
74 | @NonNull
75 | private final IamClient iamClient;
76 |
77 | @NonNull
78 | private final VerificationApplicationConfig verificationApplicationConfig;
79 |
80 | /**
81 | * Validates the given token is given, the token starts with the needed prefix, the signing key is not null and the
82 | * token is valid.
83 | *
84 | * @param authorizationToken The authorization token to validate
85 | * @param mandatoryRoles list of roles which are required to pass
86 | * @return true
, if the token is valid, otherwise false
87 | */
88 | public boolean isAuthorized(String authorizationToken, List mandatoryRoles) {
89 | // check if the JWT is enabled
90 | if (!verificationApplicationConfig.getJwt().getEnabled()) {
91 | return true;
92 | }
93 | if (null != authorizationToken && authorizationToken.startsWith(TOKEN_PREFIX)) {
94 | String jwtToken = authorizationToken.substring(TOKEN_PREFIX.length());
95 | return validateToken(jwtToken, getPublicKey(), mandatoryRoles);
96 | }
97 | return false;
98 | }
99 |
100 | /**
101 | * Validates the given token. If one of the given roles {@link AuthorizationRole} exists and verified by a public key
102 | *
103 | * @param token The authorization token to validate
104 | * @param publicKey the key from the IAM server
105 | * @param mandatoryRoles List of roles which are required to pass.
106 | * @return true
, if the token is valid, otherwise false
107 | */
108 | public boolean validateToken(final String token, final PublicKey publicKey, List mandatoryRoles) {
109 | log.debug("process validateToken() by - token: {} PK: {}", token, publicKey);
110 | if (null != publicKey) {
111 | try {
112 | List roleNames = getRoles(token, publicKey);
113 |
114 | // Return false if one of the mandatory roles are not present
115 | for (AuthorizationRole mandatoryRole : mandatoryRoles) {
116 | if (!roleNames.contains(mandatoryRole.getRoleName())) {
117 | return false;
118 | }
119 | }
120 |
121 | // Return true if at least one of the authorization roles are present
122 | AuthorizationRole[] roles = AuthorizationRole.values();
123 | for (AuthorizationRole role : roles) {
124 | if (roleNames.contains(role.getRoleName())) {
125 | return true;
126 | }
127 | }
128 | } catch (JwtException ex) {
129 | log.warn("Token is not valid: {}.", ex.getMessage());
130 | return false;
131 | }
132 | }
133 | log.warn("No public key for Token validation found.");
134 | return false;
135 | }
136 |
137 | public String getSubject(final String token, final PublicKey publicKey) {
138 | return getClaimFromToken(token, Claims::getSubject, publicKey);
139 | }
140 |
141 | private List getRoles(final String token, final PublicKey publicKey) {
142 | Map> realm = getRealmFromToken(token, publicKey);
143 | return realm.getOrDefault(ROLES, new ArrayList<>());
144 | }
145 |
146 | @SuppressWarnings("unchecked")
147 | private Map> getRealmFromToken(final String token, final PublicKey publicKey) {
148 | final Claims claims = getAllClaimsFromToken(token, publicKey);
149 | return claims.get(REALM_ACCESS, Map.class);
150 | }
151 |
152 | public T getClaimFromToken(final String token, Function claimsResolver, final PublicKey publicKey) {
153 | final Claims claims = getAllClaimsFromToken(token, publicKey);
154 | return claimsResolver.apply(claims);
155 | }
156 |
157 | private Claims getAllClaimsFromToken(final String token, final PublicKey publicKey) {
158 | return Jwts.parserBuilder().setSigningKey(publicKey).build().parseClaimsJws(token).getBody();
159 | }
160 |
161 | /**
162 | * Get the certificate from IAM client.
163 | * As long as Keycloak can rotate it’s keys we decided to reload
164 | * the key on every validateToken call especially the method call
165 | * is fortunately limited in time and number too.
166 | *
167 | * @return the calculated Public key from the certificate
168 | */
169 | public PublicKey getPublicKey() {
170 | Certs certs = iamClient.certs();
171 | log.debug("process getPublicKey() - cert info from IAM certs: {}", certs);
172 | for (Key key : certs.getKeys()) {
173 | if (key.isCertValid()) {
174 | String certb64 = key.getX5c().get(0);
175 | String wrappedCert = BEGIN_CERT + System.lineSeparator() + certb64 + System.lineSeparator() + END_CERT;
176 | try {
177 | byte[] certBytes = wrappedCert.getBytes(java.nio.charset.StandardCharsets.UTF_8);
178 | CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
179 | InputStream in = new ByteArrayInputStream(certBytes);
180 | X509Certificate certificate = (X509Certificate) certFactory.generateCertificate(in);
181 | return certificate.getPublicKey();
182 | } catch (CertificateException ex) {
183 | log.warn("Error generate certificate: {}.", ex.getMessage());
184 | }
185 | } else {
186 | log.warn("Wrong use or alg key given! use: {} alg: {}", key.getUse(), key.getAlg());
187 | log.warn("Keys use: {} and alg: {} are expected!", Key.SIG, Key.RS256);
188 | }
189 | }
190 | return null;
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/service/TestResultServerService.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import app.coronawarn.verification.client.TestResultServerClient;
24 | import app.coronawarn.verification.model.HashedGuid;
25 | import app.coronawarn.verification.model.TestResult;
26 | import lombok.RequiredArgsConstructor;
27 | import lombok.extern.slf4j.Slf4j;
28 | import org.springframework.stereotype.Component;
29 |
30 | /**
31 | * This class represents the lab server service.
32 | */
33 | @Slf4j
34 | @RequiredArgsConstructor
35 | @Component
36 | public class TestResultServerService {
37 |
38 | private final TestResultServerClient testResultServerClient;
39 |
40 | /**
41 | * This method gives an TestResult for a guid.
42 | *
43 | * @param guid hashed GUID
44 | * @return Testresult for GUID
45 | */
46 | public TestResult result(HashedGuid guid) {
47 | return testResultServerClient.result(guid);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/validator/RegistrationTokenKeyConstraint.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.validator;
22 |
23 | import static java.lang.annotation.ElementType.TYPE;
24 |
25 | import jakarta.validation.Constraint;
26 | import jakarta.validation.Payload;
27 | import java.lang.annotation.Documented;
28 | import java.lang.annotation.Retention;
29 | import java.lang.annotation.RetentionPolicy;
30 | import java.lang.annotation.Target;
31 |
32 |
33 | /**
34 | * The validation-annotation for a registration token request.
35 | */
36 | @Documented
37 | @Constraint(validatedBy = RegistrationTokenRequestValidator.class)
38 | @Target({TYPE})
39 | @Retention(RetentionPolicy.RUNTIME)
40 | public @interface RegistrationTokenKeyConstraint {
41 |
42 | /**
43 | * The default key for creating error messages in case the constraint is violated.
44 | */
45 | String message() default "The key is not valid";
46 |
47 | /**
48 | * Allows the specification of validation groups, to which this constraint belongs.
49 | */
50 | Class>[] groups() default {};
51 |
52 | /**
53 | * Assigns custom payload objects to a constraint.
54 | */
55 | Class extends Payload>[] payload() default {};
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/app/coronawarn/verification/validator/RegistrationTokenRequestValidator.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.validator;
22 |
23 | import app.coronawarn.verification.model.RegistrationTokenKeyType;
24 | import app.coronawarn.verification.model.RegistrationTokenRequest;
25 | import app.coronawarn.verification.service.HashingService;
26 | import app.coronawarn.verification.service.TanService;
27 | import jakarta.validation.ConstraintValidator;
28 | import jakarta.validation.ConstraintValidatorContext;
29 | import lombok.NonNull;
30 | import lombok.RequiredArgsConstructor;
31 | import lombok.extern.slf4j.Slf4j;
32 | import org.springframework.stereotype.Component;
33 |
34 | /**
35 | * The registration token request validator.
36 | */
37 | @Slf4j
38 | @RequiredArgsConstructor
39 | @Component
40 | public class RegistrationTokenRequestValidator
41 | implements ConstraintValidator {
42 | /**
43 | * The {@link HashingService}.
44 | */
45 | @NonNull
46 | private final HashingService hashingService;
47 |
48 | /**
49 | * The {@link TanService}.
50 | */
51 | @NonNull
52 | private final TanService tanService;
53 |
54 | @Override
55 | public boolean isValid(RegistrationTokenRequest request, ConstraintValidatorContext arg1) {
56 |
57 | String key = request.getKey();
58 | RegistrationTokenKeyType keyType = request.getKeyType();
59 | if (key == null || keyType == null) {
60 | return false;
61 | }
62 | return switch (keyType) {
63 | case GUID -> hashingService.isHashValid(key);
64 | case TELETAN -> tanService.verifyTeleTan(key);
65 | };
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/resources/application-cloud.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | driver-class-name: org.postgresql.Driver
4 | url: jdbc:postgresql://${POSTGRESQL_SERVICE_HOST}:${POSTGRESQL_SERVICE_PORT}/${POSTGRESQL_DATABASE}
5 | username: ${POSTGRESQL_USER}
6 | password: ${POSTGRESQL_PASSWORD}
7 | jpa:
8 | database-platform: org.hibernate.dialect.PostgreSQLDialect
9 | server:
10 | ssl:
11 | hostname-verify: false
12 | key-store: ${SSL_VERIFICATION_KEYSTORE_PATH}
13 | key-store-password: ${SSL_VERIFICATION_KEYSTORE_PASSWORD}
14 | trust-store: ${SSL_VERIFICATION_TRUSTSTORE_PATH}
15 | trust-store-password: ${SSL_VERIFICATION_TRUSTSTORE_PASSWORD}
16 | cwa-testresult-server:
17 | ssl:
18 | enabled: true
19 | one-way: true
20 | two-way: true
21 | hostname-verify: false
22 | key-store: ${SSL_VERIFICATION_KEYSTORE_PATH}
23 | key-store-password: ${SSL_VERIFICATION_KEYSTORE_PASSWORD}
24 | trust-store: ${SSL_TESTRESULTSERVER_TRUSTSTORE_PATH}
25 | trust-store-password: ${SSL_TESTRESULTSERVER_TRUSTSTORE_PASSWORD}
26 | disable-dob-hash-check-for-external-test-result: ${DISABLE_DOB_EXTERNAL_TR}
27 | allowed-client-certificates: ${VERIFICATION_ALLOWEDCLIENTCERTIFICATES}
28 |
--------------------------------------------------------------------------------
/src/main/resources/application-external.yml:
--------------------------------------------------------------------------------
1 | server:
2 | ssl:
3 | protocol: TLS
4 | enabled-protocols: TLSv1.3,TLSv1.2
5 | ciphers: >-
6 | TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
7 | TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
8 | TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
9 | TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
10 | TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
11 | TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
12 | TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
13 | TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
14 | TLS_AES_128_GCM_SHA256
15 | TLS_AES_256_GCM_SHA384
16 | TLS_AES_128_CCM_SHA256
17 |
--------------------------------------------------------------------------------
/src/main/resources/application-internal.yml:
--------------------------------------------------------------------------------
1 | server:
2 | ssl:
3 | protocol: TLS
4 | enabled-protocols: TLSv1.3
5 |
--------------------------------------------------------------------------------
/src/main/resources/application-local.yml:
--------------------------------------------------------------------------------
1 | cwa-testresult-server:
2 | url: http://localhost:8088
3 | ssl:
4 | enabled: false
5 | one-way: false
6 | two-way: false
7 | hostname-verify: false
8 | key-store: classpath:keystore.jks
9 | key-store-password: changeit
10 | trust-store: classpath:truststore.jks
11 | trust-store-password: changeit
12 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: cwa-verification-server
4 | datasource:
5 | driver-class-name: org.h2.Driver
6 | url: jdbc:h2:mem:verification
7 | username: sa
8 | password: ''
9 | jpa:
10 | database-platform: org.hibernate.dialect.H2Dialect
11 | hibernate:
12 | ddl-auto: validate
13 | liquibase:
14 | change-log: classpath:db/changelog.yml
15 | server:
16 | max-post-size: 10000
17 | feign:
18 | client:
19 | config:
20 | default:
21 | connect-timeout: 5000
22 | read-timeout: 5000
23 | logger-level: basic
24 | jwt:
25 | server: http://localhost:8080
26 | enabled: false
27 | springdoc:
28 | api-docs:
29 | path: /api/docs
30 | swagger-ui:
31 | path: /api/swagger
32 | management:
33 | server:
34 | ssl:
35 | enabled: false
36 | port: 8081
37 | endpoint:
38 | info:
39 | enabled: true
40 | health:
41 | enabled: true
42 | metrics:
43 | enabled: true
44 | prometheus:
45 | enabled: true
46 | endpoints:
47 | enabled-by-default: false
48 | web:
49 | exposure:
50 | include: info,health,metrics,prometheus
51 | jmx:
52 | exposure:
53 | include: info,health,metrics,prometheus
54 | health:
55 | probes:
56 | enabled: true
57 | metrics:
58 | export:
59 | prometheus:
60 | enabled: true
61 | tan:
62 | tele:
63 | ratelimiting:
64 | count: 1000
65 | seconds: 3600
66 | threshold-in-percent: 80
67 | valid:
68 | length: 9
69 | hours: 1
70 | eventDays: 2
71 | valid:
72 | days: 14
73 | appsession:
74 | tancountermax: 1
75 | entities:
76 | cleanup:
77 | cron: "0 1 * * * *"
78 | days: 21
79 | initialFakeDelayMilliseconds: 10
80 | fakeDelayMovingAverageSamples: 5
81 | request:
82 | sizelimit: 10000
83 |
84 | cwa-testresult-server:
85 | url: http://localhost:8088
86 | allowed-client-certificates:
87 |
--------------------------------------------------------------------------------
/src/main/resources/bootstrap-cloud.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | spring:
3 | application:
4 | name: cwa-verification-server
5 | cloud:
6 | vault:
7 | ssl:
8 | trust-store: file:${SSL_VAULT_TRUSTSTORE_PATH}
9 | trust-store-password: ${SSL_VAULT_TRUSTSTORE_PASSWORD}
10 | enabled: true
11 | generic:
12 | enabled: false
13 | kv:
14 | enabled: true
15 | backend: ${VAULT_BACKEND}
16 | profile-separator: '/'
17 | application-name: 'cwa-verification-server'
18 | default-context: ''
19 | profiles: cloud
20 | fail-fast: true
21 | authentication: KUBERNETES
22 | kubernetes:
23 | role: ${VAULT_ROLE}
24 | kubernetes-path: kubernetes
25 | service-account-token-file: /var/run/secrets/kubernetes.io/serviceaccount/token
26 | uri: ${VAULT_URI}
27 | connection-timeout: 5000
28 | read-timeout: 15000
29 | config:
30 | order: -10
31 |
--------------------------------------------------------------------------------
/src/main/resources/bootstrap.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | spring:
3 | cloud:
4 | vault:
5 | enabled: false
6 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - include:
3 | file: changelog/v000-create-app-session-table.yml
4 | relativeToChangelogFile: true
5 | - include:
6 | file: changelog/v000-create-tan-table.yml
7 | relativeToChangelogFile: true
8 | - include:
9 | file: changelog/v001-add-dob-hash-column.yml
10 | relativeToChangelogFile: true
11 | - include:
12 | file: changelog/v002-add-teletan-type-column.yml
13 | relativeToChangelogFile: true
14 | - include:
15 | file: changelog/v003-add-unique-hashed-guid.yml
16 | relativeToChangelogFile: true
17 | - include:
18 | file: changelog/v004-add-unique-registration-token-teletan.yml
19 | relativeToChangelogFile: true
20 | - include:
21 | file: changelog/v005-add-shedlock.yml
22 | relativeToChangelogFile: true
23 | - include:
24 | file: changelog/v006-add-index-dob-hashed-guid.yml
25 | relativeToChangelogFile: true
26 | - include:
27 | file: changelog/v007-add-seperate-unique-constraints-for-hashed-guid.yml
28 | relativeToChangelogFile: true
29 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog/v000-create-app-session-table.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - changeSet:
3 | id: create-app-session-table
4 | author: jhagestedt
5 | changes:
6 | - createTable:
7 | tableName: app_session
8 | columns:
9 | - column:
10 | name: id
11 | type: bigint
12 | constraints:
13 | unique: true
14 | nullable: false
15 | primaryKey: true
16 | - column:
17 | name: version
18 | type: bigint
19 | constraints:
20 | nullable: false
21 | - column:
22 | name: created_at
23 | type: datetime
24 | constraints:
25 | nullable: false
26 | - column:
27 | name: updated_at
28 | type: datetime
29 | constraints:
30 | nullable: false
31 | - column:
32 | name: hashed_guid
33 | type: varchar(64)
34 | - column:
35 | name: registration_token_hash
36 | type: varchar(64)
37 | - column:
38 | name: tele_tan_hash
39 | type: varchar(64)
40 | - column:
41 | name: tan_counter
42 | type: int
43 | - column:
44 | name: sot
45 | type: varchar(255)
46 | - changeSet:
47 | id: create-app-session-table-increment
48 | author: jhagestedt
49 | changes:
50 | - addAutoIncrement:
51 | tableName: app_session
52 | columnName: id
53 | columnDataType: bigint
54 | startWith: 1
55 | incrementBy: 1
56 | - changeSet:
57 | id: create-app-session-table-indexes
58 | author: jhagestedt
59 | changes:
60 | - createIndex:
61 | tableName: app_session
62 | indexName: idx_app_session_guid_hash
63 | columns:
64 | - column:
65 | name: hashed_guid
66 | type: varchar(64)
67 | - createIndex:
68 | tableName: app_session
69 | indexName: idx_app_session_registration_token_hash
70 | columns:
71 | - column:
72 | name: registration_token_hash
73 | type: varchar(64)
74 | - createIndex:
75 | tableName: app_session
76 | indexName: idx_app_session_tele_tan_hash
77 | columns:
78 | - column:
79 | name: tele_tan_hash
80 | type: varchar(64)
81 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog/v000-create-tan-table.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - changeSet:
3 | id: create-tan-table
4 | author: jhagestedt
5 | changes:
6 | - createTable:
7 | tableName: tan
8 | columns:
9 | - column:
10 | name: id
11 | type: bigint
12 | constraints:
13 | unique: true
14 | nullable: false
15 | primaryKey: true
16 | - column:
17 | name: version
18 | type: bigint
19 | constraints:
20 | nullable: false
21 | - column:
22 | name: created_at
23 | type: datetime
24 | constraints:
25 | nullable: false
26 | - column:
27 | name: updated_at
28 | type: datetime
29 | constraints:
30 | nullable: false
31 | - column:
32 | name: valid_from
33 | type: datetime
34 | constraints:
35 | nullable: false
36 | - column:
37 | name: valid_until
38 | type: datetime
39 | constraints:
40 | nullable: false
41 | - column:
42 | name: tan_hash
43 | type: varchar(64)
44 | - column:
45 | name: sot
46 | type: varchar(255)
47 | - column:
48 | name: type
49 | type: varchar(255)
50 | - column:
51 | name: redeemed
52 | type: boolean
53 | - changeSet:
54 | id: create-tan-table-increment
55 | author: jhagestedt
56 | changes:
57 | - addAutoIncrement:
58 | tableName: tan
59 | columnName: id
60 | columnDataType: bigint
61 | startWith: 1
62 | incrementBy: 1
63 | - changeSet:
64 | id: create-tan-table-indexes
65 | author: jhagestedt
66 | changes:
67 | - createIndex:
68 | tableName: tan
69 | indexName: idx_tan_tan_hash
70 | columns:
71 | - column:
72 | name: tan_hash
73 | type: varchar(64)
74 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog/v001-add-dob-hash-column.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - changeSet:
3 | id: add-dob-hash-column
4 | author: f11h
5 | changes:
6 | - addColumn:
7 | tableName: app_session
8 | columns:
9 | name: hashed_guid_dob
10 | type: varchar(64)
11 | constraints:
12 | nullable: true
13 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog/v002-add-teletan-type-column.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - changeSet:
3 | id: add-teletan-type-column
4 | author: f11h
5 | changes:
6 | - addColumn:
7 | tableName: tan
8 | columns:
9 | name: teletan_type
10 | type: varchar(10)
11 | constraints:
12 | nullable: true
13 | - addColumn:
14 | tableName: app_session
15 | columns:
16 | name: teletan_type
17 | type: varchar(10)
18 | constraints:
19 | nullable: true
20 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog/v003-add-unique-hashed-guid.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - changeSet:
3 | id: add-unique-hashed-guid
4 | author: f11h
5 | changes:
6 | - addUniqueConstraint:
7 | tableName: app_session
8 | columnNames: hashed_guid, hashed_guid_dob
9 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog/v004-add-unique-registration-token-teletan.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - changeSet:
3 | id: add-unique-registration-token-teletan
4 | author: f11h
5 | changes:
6 | - addUniqueConstraint:
7 | tableName: app_session
8 | columnNames: registration_token_hash, tele_tan_hash
9 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog/v005-add-shedlock.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - changeSet:
3 | id: add-shedlock
4 | author: mschulte-tsi
5 | changes:
6 | - createTable:
7 | tableName: shedlock
8 | columns:
9 | - column:
10 | name: name
11 | type: varchar(64)
12 | constraints:
13 | nullable: false
14 | primaryKey: true
15 | - column:
16 | name: lock_until
17 | type: datetime(2)
18 | constraints:
19 | nullable: false
20 | - column:
21 | name: locked_at
22 | type: datetime(2)
23 | constraints:
24 | nullable: false
25 | - column:
26 | name: locked_by
27 | type: varchar(255)
28 | constraints:
29 | nullable: false
30 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog/v006-add-index-dob-hashed-guid.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - changeSet:
3 | id: add-unique-hashed-guid
4 | author: mschulte-tsi
5 | changes:
6 | - createIndex:
7 | tableName: app_session
8 | indexName: idx_app_session_hashed_guid_dob
9 | columns:
10 | - column:
11 | name: hashed_guid_dob
12 | type: varchar(64)
13 |
--------------------------------------------------------------------------------
/src/main/resources/db/changelog/v007-add-seperate-unique-constraints-for-hashed-guid.yml:
--------------------------------------------------------------------------------
1 | databaseChangeLog:
2 | - changeSet:
3 | id: add-seperate-unique-constraints-for-hashed-guid
4 | author: f11h
5 | changes:
6 | - addUniqueConstraint:
7 | tableName: app_session
8 | columnNames: hashed_guid
9 | - addUniqueConstraint:
10 | tableName: app_session
11 | columnNames: hashed_guid_dob
12 |
--------------------------------------------------------------------------------
/src/test/java/app/coronawarn/verification/TestUtils.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification;
22 |
23 | import app.coronawarn.verification.domain.VerificationAppSession;
24 | import app.coronawarn.verification.domain.VerificationTan;
25 | import app.coronawarn.verification.model.AppSessionSourceOfTrust;
26 | import app.coronawarn.verification.model.AuthorizationRole;
27 | import app.coronawarn.verification.model.TanSourceOfTrust;
28 | import app.coronawarn.verification.model.TanType;
29 | import app.coronawarn.verification.model.TeleTanType;
30 | import app.coronawarn.verification.model.TestResult;
31 | import app.coronawarn.verification.repository.VerificationAppSessionRepository;
32 | import com.fasterxml.jackson.core.JsonProcessingException;
33 | import com.fasterxml.jackson.databind.ObjectMapper;
34 | import io.jsonwebtoken.Jwts;
35 | import io.jsonwebtoken.SignatureAlgorithm;
36 | import java.security.PrivateKey;
37 | import java.time.Instant;
38 | import java.time.LocalDateTime;
39 | import java.util.ArrayList;
40 | import java.util.Date;
41 | import java.util.HashMap;
42 | import java.util.List;
43 | import java.util.Map;
44 | import org.springframework.beans.factory.annotation.Autowired;
45 | import org.springframework.stereotype.Component;
46 |
47 | @Component
48 | public class TestUtils {
49 |
50 | static final String TEST_GUI_HASH = "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b";
51 | static final String TEST_GUI_HASH2 = "a1f7347308703928470938247903247903274903274903297041bea091d14d4d";
52 | static final String TEST_GUI_HASH_DOB = "x0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b";
53 | static final String TEST_GUI_HASH_DOB2 = "x1f7347308703928470938247903247903274903274903297041bea091d14d4d";
54 | static final String RESULT_PADDING = "";
55 | static final String LAB_ID = "l".repeat(64);
56 | static final String TEST_INVALID_GUI_HASH = "f0e4c2f76c58916ec2b";
57 | static final String TEST_TELE_TAN = "R3ZNUeV";
58 | static final String TEST_TELE_TAN_HASH = "eeaa54dc40aa84f587e3bc0cbbf18f7c05891558a5fe1348d52f3277794d8730";
59 | static final String TEST_INVALID_REG_TOK = "1234567890";
60 | static final String TEST_REG_TOK = "1ea6ce8a-9740-41ea-bb37-0242ac130002";
61 | static final String TEST_REG_TOK_HASH = "0199effab87800689c15c08e234db54f088cc365132ffc230e882b82cd3ecf95";
62 | static final TestResult TEST_LAB_POSITIVE_RESULT = new TestResult(2,0, LAB_ID, null);
63 | static final TestResult QUICK_TEST_POSITIVE_RESULT = new TestResult(7,0, LAB_ID, null);
64 | static final TestResult TEST_LAB_NEGATIVE_RESULT = new TestResult(1,0, LAB_ID, null);
65 | static final String TEST_TAN = "1819d933-45f6-4e3c-80c7-eeffd2d44ee6";
66 | static final String TEST_INVALID_TAN = "1ea6ce8a-9740-11ea-is-invalid";
67 | static final TanSourceOfTrust TEST_SOT = TanSourceOfTrust.CONNECTED_LAB;
68 | static final String TEST_HASHED_TAN = "cfb5368fc0fca485847acb28e6a96c958bb6ab7350ac766be88ad13841750231";
69 | static final TanType TEST_TAN_TYPE = TanType.TAN;
70 | static final LocalDateTime TAN_VALID_UNTIL_IN_DAYS = LocalDateTime.now().plusDays(7);
71 | static final String PREFIX_API_VERSION = "/version/v1";
72 | static final String REGISTRATION_TOKEN_URI = "/registrationToken";
73 | static final String TAN_VERIFICATION_URI = "/tan/verify";
74 |
75 | private static ObjectMapper objectMapper;
76 |
77 | @Autowired
78 | public TestUtils(ObjectMapper objectMapper) {
79 | TestUtils.objectMapper = objectMapper;
80 | }
81 |
82 | static void prepareAppSessionTestData(VerificationAppSessionRepository appSessionRepository) {
83 | appSessionRepository.deleteAll();
84 | appSessionRepository.save(getAppSessionTestData());
85 | }
86 |
87 | static void prepareAppSessionTestDataDob(VerificationAppSessionRepository appSessionRepository) {
88 | appSessionRepository.deleteAll();
89 | appSessionRepository.save(getAppSessionTestData(AppSessionSourceOfTrust.HASHED_GUID, true));
90 | }
91 |
92 | static void prepareAppSessionTestDataSotTeleTan(VerificationAppSessionRepository appSessionRepository) {
93 | appSessionRepository.deleteAll();
94 | appSessionRepository.save(getAppSessionTestData(AppSessionSourceOfTrust.TELETAN, false));
95 | }
96 |
97 | static VerificationAppSession getAppSessionTestData(AppSessionSourceOfTrust sot, boolean dob) {
98 | VerificationAppSession cv = new VerificationAppSession();
99 | cv.setTeleTanType(TeleTanType.EVENT);
100 | cv.setHashedGuid(TEST_GUI_HASH);
101 | cv.setHashedGuidDob(dob ? TEST_GUI_HASH_DOB : null);
102 | cv.setCreatedAt(LocalDateTime.now());
103 | cv.setUpdatedAt(LocalDateTime.now());
104 | cv.setTanCounter(0);
105 | cv.setSourceOfTrust(sot);
106 | cv.setRegistrationTokenHash(TEST_REG_TOK_HASH);
107 | return cv;
108 | }
109 |
110 | static VerificationAppSession getAppSessionTestData() {
111 | return getAppSessionTestData(AppSessionSourceOfTrust.HASHED_GUID, false);
112 | }
113 |
114 | static VerificationTan getTeleTanTestData() {
115 | VerificationTan cvtan = new VerificationTan();
116 | cvtan.setTeleTanType(TeleTanType.EVENT);
117 | cvtan.setCreatedAt(LocalDateTime.now());
118 | cvtan.setUpdatedAt(LocalDateTime.now());
119 | cvtan.setRedeemed(false);
120 | cvtan.setSourceOfTrust(TanSourceOfTrust.TELETAN);
121 | cvtan.setTanHash(TEST_HASHED_TAN);
122 | cvtan.setType(TanType.TELETAN);
123 | cvtan.setValidFrom(LocalDateTime.now());
124 | cvtan.setValidUntil(LocalDateTime.now().plusHours(1));
125 | return cvtan;
126 | }
127 |
128 | static VerificationTan getVerificationTANTestData() {
129 | VerificationTan cvtan = new VerificationTan();
130 | cvtan.setTeleTanType(TeleTanType.EVENT);
131 | cvtan.setCreatedAt(LocalDateTime.now());
132 | cvtan.setUpdatedAt(LocalDateTime.now());
133 | cvtan.setRedeemed(false);
134 | cvtan.setSourceOfTrust(TEST_SOT);
135 | cvtan.setTanHash(TEST_HASHED_TAN);
136 | cvtan.setType(TEST_TAN_TYPE);
137 | cvtan.setValidFrom(LocalDateTime.now().minusDays(5));
138 | cvtan.setValidUntil(TAN_VALID_UNTIL_IN_DAYS);
139 | return cvtan;
140 | }
141 |
142 | static String getAsJsonFormat(Object o) throws JsonProcessingException {
143 | return objectMapper.writeValueAsString(o);
144 | }
145 |
146 | static String getJwtTestData(final long expirationSecondsToAdd, PrivateKey privateKey, AuthorizationRole... roles) {
147 | final Map> realmAccessMap = new HashMap<>();
148 | final List roleNames = new ArrayList<>();
149 | for (AuthorizationRole role : roles) {
150 | roleNames.add(role.getRoleName());
151 | }
152 |
153 | realmAccessMap.put("roles", roleNames);
154 |
155 | return Jwts.builder()
156 | .setExpiration(Date.from(Instant.now().plusSeconds(expirationSecondsToAdd)))
157 | .setIssuedAt(Date.from(Instant.now()))
158 | .setId("baeaa733-521e-4d2e-8abe-95bb440a9f5f")
159 | .setIssuer("http://localhost:8080/auth/realms/cwa")
160 | .setAudience("account")
161 | .setSubject("72b3b494-a0f4-49f5-b235-1e9f93c86e58")
162 | .claim("auth_time", "1590742669")
163 | .claim("iss", "http://localhost:8080/auth/realms/cwa")
164 | .claim("aud", "account")
165 | .claim("typ", "Bearer")
166 | .claim("azp", "verification-portal")
167 | .claim("session_state", "41cc4d83-e394-4d08-b887-28d8c5372d4a")
168 | .claim("acr", "0")
169 | .claim("realm_access", realmAccessMap)
170 | .claim("resource_access", new HashMap<>())
171 | .claim("scope", "openid profile email")
172 | .claim("email_verified", false)
173 | .claim("preferred_username", "test")
174 | .signWith(privateKey, SignatureAlgorithm.RS256)
175 | .compact();
176 | }
177 |
178 | }
179 |
--------------------------------------------------------------------------------
/src/test/java/app/coronawarn/verification/VerificationApplicationExternalTestHttp.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | //package app.coronawarn.verification;
22 | //
23 | //import java.io.BufferedReader;
24 | //import java.io.IOException;
25 | //import java.io.InputStreamReader;
26 | //import java.io.PrintWriter;
27 | //import java.net.InetAddress;
28 | //import java.net.Socket;
29 | //
30 | //import app.coronawarn.verification.config.SecurityConfig;
31 | //import org.junit.Assert;
32 | //import org.junit.Test;
33 | //import org.junit.runner.RunWith;
34 | //import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
35 | //import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
36 | //import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
37 | //import org.springframework.boot.test.context.SpringBootTest;
38 | //import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
39 | //import org.springframework.boot.web.server.LocalServerPort;
40 | //import org.springframework.context.annotation.ComponentScan;
41 | //import org.springframework.context.annotation.FilterType;
42 | //import org.springframework.test.context.junit4.SpringRunner;
43 | //
44 | //@RunWith(SpringRunner.class)
45 | //@SpringBootTest(webEnvironment = RANDOM_PORT)
46 | //@ComponentScan(basePackages = {"app.coronawan.verification"},
47 | // excludeFilters = {@ComponentScan.Filter(
48 | // type = FilterType.ASSIGNABLE_TYPE,
49 | // value = {SecurityConfig.class})
50 | // })
51 | //public class VerificationApplicationExternalTestHttp {
52 | //
53 | // @LocalServerPort
54 | // private int port;
55 | //
56 | // @Test
57 | // public void testChunkedModeIsDenied() throws IOException {
58 | // Socket socket = new Socket(InetAddress.getLocalHost(), port);
59 | // PrintWriter writer = new PrintWriter(socket.getOutputStream());
60 | //
61 | // writer.print("POST /version/v1/registrationToken HTTP/1.1\r\n");
62 | // writer.print("Host: 127.0.0.1:" + port + "\r\n");
63 | // writer.print("Content-Type: application/json\r\n");
64 | // writer.print("Transfer-Encoding: Chunked\r\n\r\n");
65 | // writer.flush();
66 | //
67 | // writer.print("{ \"randomBody\": 42 }");
68 | // writer.flush();
69 | //
70 | // BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
71 | // Assert.assertEquals("HTTP/1.1 406", reader.readLine().trim());
72 | //
73 | // reader.close();
74 | // socket.close();
75 | // }
76 | //
77 | // private class SecurityAutoConfiguration {
78 | // }
79 | //}
80 |
--------------------------------------------------------------------------------
/src/test/java/app/coronawarn/verification/VerificationApplicationExternalTestWorkaround.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification;
22 |
23 | import static org.mockito.BDDMockito.given;
24 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
25 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
26 |
27 | import app.coronawarn.verification.model.HashedGuid;
28 | import app.coronawarn.verification.model.RegistrationToken;
29 | import app.coronawarn.verification.repository.VerificationAppSessionRepository;
30 | import app.coronawarn.verification.service.TestResultServerService;
31 | import lombok.extern.slf4j.Slf4j;
32 | import org.junit.jupiter.api.Test;
33 | import org.junit.jupiter.api.extension.ExtendWith;
34 | import org.springframework.beans.factory.annotation.Autowired;
35 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
36 | import org.springframework.boot.test.context.SpringBootTest;
37 | import org.springframework.boot.test.mock.mockito.MockBean;
38 | import org.springframework.http.MediaType;
39 | import org.springframework.test.context.ActiveProfiles;
40 | import org.springframework.test.context.ContextConfiguration;
41 | import org.springframework.test.context.junit.jupiter.SpringExtension;
42 | import org.springframework.test.web.servlet.MockMvc;
43 |
44 | /**
45 | * This is the test class for the verification application.
46 | */
47 | @Slf4j
48 | @ExtendWith(SpringExtension.class)
49 | @SpringBootTest(properties = "disable-dob-hash-check-for-external-test-result=true")
50 | @ContextConfiguration(classes = VerificationApplication.class)
51 | @AutoConfigureMockMvc
52 | @ActiveProfiles({"external","local"})
53 | public class VerificationApplicationExternalTestWorkaround {
54 |
55 | private static final String TOKEN_PADDING = "1";
56 | @Autowired
57 | private MockMvc mockMvc;
58 | @MockBean
59 | private TestResultServerService testResultServerService;
60 | @Autowired
61 | private VerificationAppSessionRepository appSessionrepository;
62 |
63 | @Test
64 | public void callGetTestStateWithDobRegistrationTokenAndTrsRespondsWithDifferentResults() throws Exception {
65 | TestUtils.prepareAppSessionTestDataDob(appSessionrepository);
66 |
67 | given(this.testResultServerService.result(new HashedGuid(TestUtils.TEST_GUI_HASH))).willReturn(TestUtils.TEST_LAB_POSITIVE_RESULT);
68 | given(this.testResultServerService.result(new HashedGuid(TestUtils.TEST_GUI_HASH_DOB))).willReturn(TestUtils.TEST_LAB_NEGATIVE_RESULT);
69 |
70 | mockMvc.perform(post(TestUtils.PREFIX_API_VERSION + "/testresult").contentType(MediaType.APPLICATION_JSON)
71 | .secure(true)
72 | .content(TestUtils.getAsJsonFormat(new RegistrationToken(TestUtils.TEST_REG_TOK, TOKEN_PADDING))))
73 | .andExpect(status().isOk());
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/test/java/app/coronawarn/verification/service/EntitiesCleanupServiceTest.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import app.coronawarn.verification.VerificationApplication;
24 | import app.coronawarn.verification.domain.VerificationAppSession;
25 | import app.coronawarn.verification.domain.VerificationTan;
26 | import app.coronawarn.verification.model.AppSessionSourceOfTrust;
27 | import app.coronawarn.verification.model.TanSourceOfTrust;
28 | import app.coronawarn.verification.model.TanType;
29 | import app.coronawarn.verification.repository.VerificationAppSessionRepository;
30 | import app.coronawarn.verification.repository.VerificationTanRepository;
31 | import java.time.LocalDateTime;
32 | import java.time.Period;
33 | import java.util.Optional;
34 | import java.util.concurrent.TimeUnit;
35 | import org.junit.jupiter.api.Assertions;
36 | import org.junit.jupiter.api.BeforeEach;
37 | import org.junit.jupiter.api.Test;
38 | import org.junit.jupiter.api.extension.ExtendWith;
39 | import org.springframework.beans.factory.annotation.Autowired;
40 | import org.springframework.boot.test.context.SpringBootTest;
41 | import org.springframework.test.context.ActiveProfiles;
42 | import org.springframework.test.context.ContextConfiguration;
43 | import org.springframework.test.context.junit.jupiter.SpringExtension;
44 |
45 | @ExtendWith(SpringExtension.class)
46 | @ActiveProfiles("local")
47 | @SpringBootTest(
48 | properties = {
49 | "entities.cleanup.cron=* * * * * *"
50 | }
51 | )
52 | @ContextConfiguration(classes = VerificationApplication.class)
53 | public class EntitiesCleanupServiceTest {
54 |
55 | public static final String TEST_GUI_HASH = "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b";
56 | public static final String TEST_REG_TOK_HASH = "c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646";
57 | public static final String TEST_HASHED_TAN = "16154ea91c2c59d6ef9d0e7f902a59283b1e7ff9111570d20139a4e6b1832876";
58 |
59 | @Autowired
60 | private VerificationAppSessionRepository appSessionRepository;
61 |
62 | @Autowired
63 | private VerificationTanRepository tanRepository;
64 |
65 | @BeforeEach
66 | public void before() {
67 | appSessionRepository.deleteAll();
68 | tanRepository.deleteAll();
69 | }
70 |
71 | @Test
72 | public void cleanupDatabase() throws InterruptedException {
73 | LocalDateTime testCreationTime = LocalDateTime.now().minus(Period.ofDays(21));
74 | // create repo 1
75 | VerificationAppSession session = appSessionRepository.save(getAppSessionTestData(testCreationTime));
76 | Assertions.assertNotNull(session);
77 | Assertions.assertEquals(TEST_GUI_HASH, session.getHashedGuid());
78 | // create repo 2
79 | VerificationTan tan = tanRepository.save(getVerificationTANTestData(testCreationTime));
80 | Assertions.assertNotNull(tan);
81 | Assertions.assertEquals(TEST_HASHED_TAN, tan.getTanHash());
82 | // find in repos
83 | Optional findSession = appSessionRepository.findByRegistrationTokenHash(TEST_REG_TOK_HASH);
84 | Assertions.assertTrue(findSession.isPresent());
85 | Assertions.assertEquals(TEST_GUI_HASH, findSession.get().getHashedGuid());
86 |
87 | Assertions.assertEquals(testCreationTime.withNano(5), findSession.get().getCreatedAt().withNano(5));
88 | Optional findTan = tanRepository.findByTanHash(TEST_HASHED_TAN);
89 | Assertions.assertTrue(findTan.isPresent());
90 | Assertions.assertEquals(TEST_HASHED_TAN, findTan.get().getTanHash());
91 | Assertions.assertEquals(testCreationTime.withNano(5), findTan.get().getCreatedAt().withNano(5));
92 | // wait
93 | TimeUnit.SECONDS.sleep(1);
94 | // find and check both repos clean up
95 | findSession = appSessionRepository.findByRegistrationTokenHash(TEST_REG_TOK_HASH);
96 | Assertions.assertFalse(findSession.isPresent());
97 | findTan = tanRepository.findByTanHash(TEST_HASHED_TAN);
98 | Assertions.assertFalse(findTan.isPresent());
99 | }
100 |
101 | private VerificationAppSession getAppSessionTestData(LocalDateTime testCreationTime) {
102 | VerificationAppSession cv = new VerificationAppSession();
103 | cv.setHashedGuid(TEST_GUI_HASH);
104 | cv.setCreatedAt(testCreationTime);
105 | cv.setUpdatedAt(LocalDateTime.now());
106 | cv.setTanCounter(0);
107 | cv.setSourceOfTrust(AppSessionSourceOfTrust.HASHED_GUID);
108 | cv.setRegistrationTokenHash(TEST_REG_TOK_HASH);
109 | return cv;
110 | }
111 |
112 | private VerificationTan getVerificationTANTestData(LocalDateTime testCreationTime) {
113 | VerificationTan cvtan = new VerificationTan();
114 | cvtan.setCreatedAt(testCreationTime);
115 | cvtan.setUpdatedAt(LocalDateTime.now());
116 | cvtan.setRedeemed(false);
117 | cvtan.setSourceOfTrust(TanSourceOfTrust.CONNECTED_LAB);
118 | cvtan.setTanHash(TEST_HASHED_TAN);
119 | cvtan.setType(TanType.TAN);
120 | cvtan.setValidFrom(LocalDateTime.now().minusDays(5));
121 | cvtan.setValidUntil(LocalDateTime.now().plusDays(7));
122 | return cvtan;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/test/java/app/coronawarn/verification/service/HashingServiceTest.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import static org.assertj.core.api.Assertions.assertThat;
24 | import static org.junit.jupiter.api.Assertions.assertFalse;
25 | import static org.junit.jupiter.api.Assertions.assertTrue;
26 |
27 | import org.junit.jupiter.api.Test;
28 |
29 | public class HashingServiceTest {
30 |
31 | HashingService hashingService = new HashingService();
32 |
33 | @Test
34 | public void validSha256Hash() {
35 | assertTrue(hashingService.isHashValid("523463041ef9ffa2950d8450feb34c88bc8692c40c9cf3c99dcdf75e270229e2"));
36 | assertTrue(hashingService.isHashValid("0000000000000000000000000000000000000000000000000000000000000000"));
37 | assertTrue(hashingService.isHashValid("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
38 | }
39 |
40 | @Test
41 | public void invalidSha256Hash() {
42 | assertFalse(hashingService.isHashValid("x23463041ef9ffa2950d8z50feb34c88bc8692c40c9cf3c99dcdf75e270229e2"));
43 | assertFalse(hashingService.isHashValid("523463041ef9ffa2950d8z50feb34c88bc8692c40c9cf3c99dcdf75e270229e2"));
44 | assertFalse(hashingService.isHashValid("0"));
45 | assertFalse(hashingService.isHashValid("0000000000000000000000000000000000000000000000000000000000000000f"));
46 | assertFalse(hashingService.isHashValid("0000000000000000000000000000000000000000000000000000000000000000\n"));
47 | }
48 |
49 | @Test
50 | public void getCheckDigit() {
51 | assertThat(hashingService.getCheckDigit("FE9A5MAK6").equals("C")).isTrue();
52 | assertThat(hashingService.getCheckDigit("WPHSATMHD").equals("4")).isTrue();
53 | assertThat(hashingService.getCheckDigit("9N4UTTACE").equals("6")).isTrue();
54 | assertThat(hashingService.getCheckDigit("S3HHJJYJD").equals("3")).isTrue();
55 | assertThat(hashingService.getCheckDigit("W3M75DUD7").equals("C")).isTrue();
56 | assertThat(hashingService.getCheckDigit("BBA3M8UVU").equals("C")).isTrue();
57 | assertThat(hashingService.getCheckDigit("MNSHDZAEJ").equals("2")).isTrue();
58 | assertThat(hashingService.getCheckDigit("WS732AR8Q").equals("B")).isTrue();
59 | // special cases
60 | assertThat(hashingService.getCheckDigit("FE9A5MAK9").equals("H")).isTrue();
61 | assertThat(hashingService.getCheckDigit("FE9A5MAKW").equals("G")).isTrue();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/test/java/app/coronawarn/verification/service/TestResultServerServiceTest.java:
--------------------------------------------------------------------------------
1 | /*-
2 | * ---license-start
3 | * Corona-Warn-App / cwa-verification
4 | * ---
5 | * Copyright (C) 2020 - 2022 T-Systems International GmbH and all other contributors
6 | * ---
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ---license-end
19 | */
20 |
21 | package app.coronawarn.verification.service;
22 |
23 | import static org.assertj.core.api.Assertions.assertThat;
24 |
25 | import app.coronawarn.verification.client.TestResultServerClient;
26 | import app.coronawarn.verification.model.HashedGuid;
27 | import app.coronawarn.verification.model.TestResult;
28 | import org.junit.jupiter.api.BeforeEach;
29 | import org.junit.jupiter.api.Test;
30 | import org.springframework.test.context.ActiveProfiles;
31 |
32 | @ActiveProfiles("local")
33 | public class TestResultServerServiceTest {
34 |
35 | public static final String TEST_GUI_HASH_1 = "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b";
36 | public static final String TEST_GUI_HASH_2 = "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13c";
37 | private static final String TEST_RESULT_PADDING = "";
38 | public static final TestResult TEST_LAB_POSITIVE_RESULT = new TestResult(2, 0, null, null);
39 | public static final TestResult TEST_LAB_REDEEMED_RESULT = new TestResult(4, 0, null, null);
40 | private TestResultServerService testResultServerService;
41 |
42 | @BeforeEach
43 | public void setUp() {
44 | testResultServerService = new TestResultServerService(new TestResultServerClientMock());
45 | }
46 |
47 | /**
48 | * Test result method by positive status.
49 | */
50 | @Test
51 | public void resultPositive() {
52 | TestResult testResult = testResultServerService.result(new HashedGuid(TEST_GUI_HASH_1));
53 | assertThat(testResult).isEqualTo(TEST_LAB_POSITIVE_RESULT);
54 | }
55 |
56 | /**
57 | * Test result method by redeemed status.
58 | */
59 | @Test
60 | public void resultRedeemed() {
61 | TestResult testResult = testResultServerService.result(new HashedGuid(TEST_GUI_HASH_2));
62 | assertThat(testResult).isEqualTo(TEST_LAB_REDEEMED_RESULT);
63 | }
64 |
65 | public static class TestResultServerClientMock implements TestResultServerClient {
66 | @Override
67 | public TestResult result(HashedGuid guid) {
68 | if (guid.getId().equals(TEST_GUI_HASH_1)) {
69 | return new TestResult(2, 0, null, null);
70 | }
71 | return new TestResult(4, 0, null, null);
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/trusted.key.gpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corona-warn-app/cwa-verification-server/bbc28be761c89bdda30f761f79f3a190e4c3a7c6/trusted.key.gpg
--------------------------------------------------------------------------------