From 94b52023328699bc887a33a8802b7a3146000d80 Mon Sep 17 00:00:00 2001 From: rdutu-vg Date: Thu, 5 Oct 2023 15:49:56 +0000 Subject: [PATCH] Deployed c4701d4 with MkDocs version: 1.4.2 --- .nojekyll | 0 3RD_PARTY_ACK/index.html | 599 ++ 404.html | 551 ++ ADDING_LICENSE/index.html | 691 ++ CONTRIBUTING/index.html | 1139 +++ DEFINITION_OF_READY/index.html | 580 ++ DEMO_APP/index.html | 670 ++ DEVELOPMENT_ENVIRONMENT/index.html | 1003 +++ EXTERNAL_MESSAGES_ADAPTER/index.html | 1041 +++ I18N/index.html | 688 ++ SECURITY/index.html | 691 ++ SERVER_CUSTOMIZATION/index.html | 865 +++ SERVER_DOCKER_SETUP/index.html | 693 ++ SERVER_SETUP/index.html | 1442 ++++ SERVER_UPDATE/index.html | 1104 +++ SOP_DISEASES/index.html | 876 +++ SORMAS2SORMAS/index.html | 885 +++ TROUBLESHOOTING/index.html | 863 +++ assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.51d95adb.min.js | 29 + assets/javascripts/bundle.51d95adb.min.js.map | 8 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.e5c33ebb.min.js | 42 + .../workers/search.e5c33ebb.min.js.map | 8 + assets/stylesheets/main.558e4712.min.css | 1 + assets/stylesheets/main.558e4712.min.css.map | 1 + assets/stylesheets/palette.2505c338.min.css | 1 + .../stylesheets/palette.2505c338.min.css.map | 1 + images/Gitflow.png | Bin 0 -> 38102 bytes images/I18nEditor.png | Bin 0 -> 55145 bytes images/I18nFiles.png | Bin 0 -> 32814 bytes .../configured-external-message-adapter.png | Bin 0 -> 11056 bytes index.html | 832 ++ search/search_index.json | 1 + sitemap.xml | 108 + sitemap.xml.gz | Bin 0 -> 212 bytes sormas-base/doc/keycloak/index.html | 912 +++ sormas-cargoserver/index.html | 824 ++ sormas-keycloak-service-provider/index.html | 726 ++ sormas-rest/index.html | 1194 +++ 67 files changed, 26264 insertions(+) create mode 100644 .nojekyll create mode 100644 3RD_PARTY_ACK/index.html create mode 100644 404.html create mode 100644 ADDING_LICENSE/index.html create mode 100644 CONTRIBUTING/index.html create mode 100644 DEFINITION_OF_READY/index.html create mode 100644 DEMO_APP/index.html create mode 100644 DEVELOPMENT_ENVIRONMENT/index.html create mode 100644 EXTERNAL_MESSAGES_ADAPTER/index.html create mode 100644 I18N/index.html create mode 100644 SECURITY/index.html create mode 100644 SERVER_CUSTOMIZATION/index.html create mode 100644 SERVER_DOCKER_SETUP/index.html create mode 100644 SERVER_SETUP/index.html create mode 100644 SERVER_UPDATE/index.html create mode 100644 SOP_DISEASES/index.html create mode 100644 SORMAS2SORMAS/index.html create mode 100644 TROUBLESHOOTING/index.html create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.51d95adb.min.js create mode 100644 assets/javascripts/bundle.51d95adb.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.e5c33ebb.min.js create mode 100644 assets/javascripts/workers/search.e5c33ebb.min.js.map create mode 100644 assets/stylesheets/main.558e4712.min.css create mode 100644 assets/stylesheets/main.558e4712.min.css.map create mode 100644 assets/stylesheets/palette.2505c338.min.css create mode 100644 assets/stylesheets/palette.2505c338.min.css.map create mode 100644 images/Gitflow.png create mode 100644 images/I18nEditor.png create mode 100644 images/I18nFiles.png create mode 100644 images/configured-external-message-adapter.png create mode 100644 index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz create mode 100644 sormas-base/doc/keycloak/index.html create mode 100644 sormas-cargoserver/index.html create mode 100644 sormas-keycloak-service-provider/index.html create mode 100644 sormas-rest/index.html diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/3RD_PARTY_ACK/index.html b/3RD_PARTY_ACK/index.html new file mode 100644 index 000000000000..829bcfdab8a0 --- /dev/null +++ b/3RD_PARTY_ACK/index.html @@ -0,0 +1,599 @@ + + + + + + + + + + + + + + + + + + + + Acknowledgements - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

3rd Party License Acknowledgement

+

Nearform temporal tables postgres function is licensed under MIT: +sormas-backend/src/main/resources/sql/temporal_tables/LICENSE

+

Country flags in sormas-ui/src/main/webapp/VAADIN/themes/sormas/img/flag-icons/ are licensed under MIT:

+
Copyright (c) 2017 Go Squared Ltd. http://www.gosquared.com/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
+associated documentation files (the "Software"), to deal in the Software without restriction, including without 
+limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
+and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
+THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 000000000000..560b85dccb8e --- /dev/null +++ b/404.html @@ -0,0 +1,551 @@ + + + + + + + + + + + + + + + + + + SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/ADDING_LICENSE/index.html b/ADDING_LICENSE/index.html new file mode 100644 index 000000000000..f9b6bb659377 --- /dev/null +++ b/ADDING_LICENSE/index.html @@ -0,0 +1,691 @@ + + + + + + + + + + + + + + + + + + + + + + Licensing - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Adding License Headers

+

License Header

+

Use the following header for all newly created source files:

+
SORMAS® - Surveillance Outbreak Response Management & Analysis System
+Copyright © 2016-2023 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+

Eclipse

+
    +
  • Use eclipse's Releng tool to automatically add license headers to all relevant source files (see here for a usage guide)
  • +
  • After installing the tool from the marketplace, open Window > Preferences > Copyright Tool and paste the license header from above into the template text area
  • +
  • Make sure to select "Replace all existing copyright comments with this copyright template" and especially "Skip over XML files" (to make sure that headers don't get added to e.g. build files)
  • +
  • Whenever you create a new source file: Right click on the file and select "Fix Copyrights"
  • +
+

Android Studio/IntelliJ

+
    +
  • Open File > Settings > Editor > Copyright > Copyright Profiles
  • +
  • Create a new profile and paste the license header from above into the Copyright text area
  • +
  • Head back to the general Copyright settings and select the new copyright profile as the "Default project copyright"
  • +
  • (Optional: If the year has changed, right click on all projects containing manual code and select "Update Copyright...", select "Custom Scope" and in the dropdown, select "Project Source Files"; Click "Ok" and wait until the copyright has been added to/changed for all files)
  • +
  • Android Studio automatically adds the copyright to newly created files afterwards
  • +
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/CONTRIBUTING/index.html b/CONTRIBUTING/index.html new file mode 100644 index 000000000000..5af5b960947a --- /dev/null +++ b/CONTRIBUTING/index.html @@ -0,0 +1,1139 @@ + + + + + + + + + + + + + + + + + + + + + + Contributing - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Contributing Guidelines

+

If you want to contribute to SORMAS by any means - for example by submitting a bug report, requesting a new feature, translating the application into a new language, or actively contributing to the source code - please make sure to read through and follow these guidelines. +This allows us to consider and process your contribution as quickly and smoothly as possible. If there is anything unclear to you or you think that this guide is lacking coverage of a specific topic, please get in touch with us through our GitHub Discussions.

+

Table of Contents

+ +

Submitting an Issue

+

Before creating a new issue, please search the repository for similar issues first to avoid duplicates! You can do this by using the search field in the menu bar. If you find an issue that already covers your request or seems very similar, please comment on that issue.

+

We are currently distinguishing the following issue types: bug, change, feature, finding, task and epic. +Please make sure to always use one of the templates that are automatically presented to you when creating a new issue because it ensures that your issue is structured and contains all the information that we need. +Issues that have not been created like this will be closed and you will be asked to submit a new issue that adheres to these guidelines.

+

Please add information to all mandatory sections of the issue templates, and try to fill in the optional sections if possible. Do not remove any section after submitting the issue because they might be filled later by the development team. +While moving forward in the development process, developers might also extend or alter the initial issue description, e.g. to adjust it according to the outcome of a refinement process.

+

Security Issues

+

If you want to report a security issue, please follow our guidelines for Responsible Disclosure.

+

Issue Types

+

Bug Report

+

Bug reports cover everything that leads to the application behaving in an unintended way, including crashes, unexpected error messages, data loss, etc.

+

Before creating a bug report, please check the following rules:

+
    +
  1. If something seems to be working correctly but does not necessarily match your expectations of how it should be working, please consider opening a change request instead. This applies also for performance issues.
  2. +
  3. If you have more than one possible bug, especially when you are tempted to create a list of independent findings in the Bug Description, please document each bug as separate issue.
  4. +
  5. Bugs should generally only be reported as new issues if they occur on a released version. If a bug only occurs on the current development version, it has very likely been caused by implementation done in the current iteration; in this case, the issue responsible for introducing the bug has to be reopened instead of creating a new issue. +This process helps to keep both the commit history and the release notes clean.
  6. +
+

The development team defines a severity for bugs to distinguish the consequences for affected users:

+
    +
  • critical: The system is unusable as a whole or on critical functionality with no reasonable workaround available.
  • +
  • major: A functional requirement is incorrect or incomplete and leads to errors or undesirable behavior.
  • +
  • minor: Text issues or grammatical mistakes, layouting or cosmetic problems that do not affect the functionality.
  • +
+

Change Request

+

Change requests cover features that are already part of SORMAS. This primarily includes aspects (or whole features) for which you would like to request an altered behavior, but also small extensions (e.g. additional values being added to a dropdown field).

+

A change request (excluding the optional fields) could look like this:

+

Problem Description

+

The "Test Results" card on the surveillance dashboard has a misleading caption. It suggests that it counts the results of all pathogen tests related to samples reported in the chosen time period while it actually refers to the final laboratory result of the latest sample associated to each case.

+

Proposed Change

+

Change the caption to "Final Laboratory Results" and add a tooltip that explains how the numbers are collected.

+

Added Value/Benefit

+

Users will have a better intuitive understanding of what the card shows, and misunderstandings are further prevented by adding the tooltip.

+

Acceptance Criteria

+
    +
  • [ ] The "Test Results" caption on the surveillance dashboard has been renamed to "Pathogen Test Results"
  • +
  • [ ] An info icon has been added to the right of the caption; should look like the info icon in the case directory filters next to the case reference date dropdown
  • +
  • [ ] Hovering over the icon displays the following tooltip: "When a case has multiple samples, only the final laboratory result of the sample with the latest sample collection date is considered."
  • +
+

Feature Request

+

Feature requests cover everything that involves adding new features to SORMAS. This includes both very large additions like completely new app sections, but also smaller ones like adding a new field to an existing form.

+

Finding

+

Findings are used to document unexpected behavior, mostly encountered during the development process. You can also use this issue type if you're not sure whether the behavior is a bug or not.

+

The development team will investigate the finding and add more details when needed. The goal is to either +- convert it to a bug if it is considered to be a bug on a released version. +- convert it to a change or feature request if there is something to improve. +- dismiss it as a duplicate if a responsible issue in the current iteration is found and reopened or fixed. +- dismiss it as discarded if the finding is not an issue.

+

A severity as for bugs can also be used for findings.

+

Task

+

Tasks are things that need to be done but do not directly change anything about the product. This could for example be the preparation of an upcoming feature/change, optimization of processes on GitHub, working on automated tests, or the update of one of the guides or Wiki articles in this repository.

+

Epic

+

The development team uses an epic as an umbrella for large change or feature streams that are linked together. Within the epic the included issues are linked in the Tasks section.

+

Contributing to the Project

+

There are many ways in which you can contribute to this project as a non-developer. If there is something you would like to do that you don't find instructions about here - or if you want to learn how you can get involved - please contact us at sormas@helmholtz-hzi.de +or through our GitHub Discussions and let us know how we can assist you!

+

Some possibilities to contribute to SORMAS are:

+ +

Contributing to the Code

+

If you're interested in participating in the development of SORMAS, please follow the Development Environment Setup Instructions before you start developing. If you have problems setting up your development environment or need assistance in choosing the first issue to work on, +please get in touch with us through our GitHub Discussions or by contacting us at sormas@helmholtz-hzi.de.

+

Additionally, our Wiki contains some specific development guides that cover common issues like adding new fields to an entity that we suggest to check out before you start implementing something related to those topics:

+ +

Development Contributing Guidelines

+

In addition to the guidelines covered by the Development Environment Setup Instructions, please ensure to adhere to the following principles and procedures while developing code for SORMAS.

+

Source Code

+
    +
  1. Remember to always apply code formatting and import reordering for all classes you work on; we recommend to use the Save Actions plugin as described in the setup instructions instead of manually executing these steps.
  2. +
  3. Some code formatting rules can't be enforced by the code formatter. Please make sure to write your code in accordance to the following rules:
      +
    • When defining a method, enter a blank line before starting to write its body (except for methods with only one line of code, e.g. most getters and setters).
    • +
    • Use a blank line to separate logical blocks of code within a method.
    • +
    • Apart from those, don't use blank lines where they are not necessarily needed to keep the code compact and readable.
    • +
    • Don't use blank lines after the last statement of a block, but a closing } with proper indentation in the next line instead.
    • +
    • Don't use blank lines between two closing }.
    • +
    +
  4. +
  5. You can use //@formatter:off and //@formatter:on to encapsulate code blocks that you don't want automatic code formatting to be applied to, e.g. because it would mess up readability. Please only use this if really needed and try to use proper indentation nonetheless.
  6. +
  7. Separate code and comments, i.e. write the comment in a separate line before the statement that you want to explain.
  8. +
  9. When you create new classes, please add license headers to them according to the Adding License Headers guide.
  10. +
+

Commits

+
    +
  1. Commit messages of every commit have to be related to a specific issue on GitHub and reference its issue number as well as include a short description on what has been done in the commit. We will reject pull requests that violate this principle and ask you to re-submit it with proper commit messages. An acceptable commit message could look like this:
    +

    1234 - Added model to define classification

    +
    +
  2. +
  3. A common practice for Git commits is to keep the first line with 50 characters as a meaningful short description, and to deliver additional details following in line 3 onwards. Most git viewers abbreviate the first line after 50 characters.
    +

    1234 - Added model to define classification

    +
      +
    • Apply automatic case classification whenever a field value changes
    • +
    • Show classification in lists
    • +
    +
    +
  4. +
  5. Keep changes in bugfixes clean and compact to be able to easily review them and leave the possibility to cherry-pick the changes to another branch to fix a previous version. Don't clean dirty code along the way if not really needed to fix the problem.
  6. +
  7. If an issue requires a lot of code changes, consider breaking down these changes in logical steps. They are then easier to review, have more meaningful commit messages and partly deliver the intended value.
  8. +
  9. Don't mix refactoring with functional changes (new functionality, changes on features, bugfixes) within the same commit, since it makes reviewing the changes much harder. Usually the refactoring of existing code has to happen beforehand in at least one separate commit. Refactoring includes e.g. cleaning up and restructuring code or renaming.
  10. +
  11. If it helps, it is okay to have several branches and pull requests for the same ticket (usually one after another, sometimes to work in parallel or to prepare changes in advance).
  12. +
  13. If there is a finding concerning an issue which has been already closed, it will be reopened if that version is not yet released. You (or someone else) will have to quickly fix the finding before the version is released. If the version has been released already, the issue will stay closed and a new issue has to be filed.
  14. +
+

Picking Issues for Development

+

When picking tasks for development, you can either search the repository for existing issues that you would like to work on, or you can submit your own issues if you don't find any that cover your topic of interest (see the "Submitting an Issue" section of this guide). However, please note that issues need to fit our overall vision of the project.

+

Once you have chosen an issue that you want to work on, please adhere to the following process and ideally only start developing once it has been completed. This ensures that your development work is perfectly aligned with the current state and vision of the project, and will make the pull request review process as smooth as possible.

+
    +
  1. Leave a comment on the issue about your intention to implement it.
  2. +
  3. A member of the core development team will check the issue and might:
      +
    • Turn the issue into a discussion if they're not sure whether it fits the overall SORMAS vision or there are a lot of discussion points.
    • +
    • Ask for additional details to be added to the issue description if you have created the issue yourself and there are questions e.g. concerning its added value or the proposed solution.
    • +
    • Take the issue into internal refinement if it doesn't meet the Definition of Ready yet.
    • +
    +
  4. +
  5. If the issue meets the Definition of Ready, a member of the core development team adds the ready label and notifies you that you can start to implement it.
  6. +
+

Issues that are already marked with the ready label can theoretically be picked up for development immediately. However, please note that these issues will usually already be planned for one of the upcoming iterations and it's possible that we'll start development on it ourselves. This is especially true for issues that are assigned to one of the sprint backlog projects.

+

Submitting Pull Requests

+

Contributing to the SORMAS code requires you to submit pull requests that will be reviewed by one of our core developers. Once a pull request has been submitted to our repository, a developer will either assign themselves as its reviewer or we will get back to you in case we won't be able to review it in time. +This may e.g. happen if your pull request involves a lot of technical changes that we would like to merge together with other issues of the same nature or that could potentially break a lot of logic. Usually though, the general process looks like this:

+
    +
  1. A developer assigns themselves as the reviewer of your pull request (core developers assign each other). Please wait until the review is done; if you think that the review is taking too long, feel free to add a comment to the pull request as a reminder to the developer.
  2. +
  3. The developer might request changes to the code. If that's the case, please implement the requested changes or answer to their change request if you have questions or don't agree with a specific part of the request.
  4. +
  5. Once you've implemented all requested changes, please request another review by the assigned developer by clicking on the "Re-request review" icon next to their name. This step is necessary in order for the reviewer to be properly notified and for the pull request to show up in their review list again.
  6. +
  7. As soon as the developer is happy with the submitted code (which might require multiple iterations of step 2 and 3, especially for larger pull requests), they will merge it into the development branch and close the pull request.
  8. +
+

Please adhere to the following principles when submitting pull requests:

+
    +
  1. Only submit pull requests that are directly associated with one specific issue in our GitHub repository. If there is no issue for the development work that you would like to do, create one before you start working on it.
  2. +
  3. Link your pull request to the issue(s) that they are associated with. This can be done either by using the "Linked issues" section at the right side when viewing a pull request, or by adding the keyword "Closes" or "Fixes" followed by the issue number to the pull request description (e.g. "Fixes #1234").
  4. +
  5. Make sure that your pull request has a meaningful title. By default, GitHub will use your commit message as the title which might not be appropriate. In general, using the same name as the linked issue is a good rule of thumb.
  6. +
  7. Try to not use force-push when updating an existing pull request (e.g. after changes have been requested or because you need to resolve merge conflicts).
  8. +
  9. Ideally, your pull request should pass the checks done by the automatic CI pipeline before it gets reviewed. If that's not the case, please make sure that your branch is up-to-date with the current development branch. If the checks also fail for the development branch, you're not required to do anything. +In any other case, please fix the issues (most likely failed unit tests) before requesting another review.
  10. +
+

Development Workflow

+

For SORMAS we use the Gitflow development workflow.

+

General Gitflow Development Workflow

+

Versioning

+

For version numbers we use semantic versioning. The meaning of a given version number X.Y.Z is:

+
    +
  • X: Major version: Major changes, severe changes in API or technical architecture.
  • +
  • Y: Minor version: Usually a new release of a development iteration of a few weeks, containing new features and changes.
  • +
  • Z: Micro version: Fixing problems in the last minor version to make it properly or better to use. Usually contains only bugfixes.
  • +
+

Versions are defined as Git tags with release notes attached to the tag.

+

An unstable version currently under development is denoted as X.Y.Z-SNAPSHOT.

+

Branches

+

Permanent branches

+
    +
  • development: This is where the changes are commited/merged to by the developers.
  • +
  • master: In regular intervals, the changes from development are merged to master with an identifiable version (tag). On top of this branch is always the latest released version.
  • +
  • master-: In case an older version than the head version on master needs to be fixed (a new micro release), a dedicated master-<version> branch is split from master to manage the following micro releases (Example: master-1.75.x). Changes made on this branch are usually cherry-picked from a newer version (on master or development).
  • +
  • l10n_development: Incoming changes on translation files from Crowdin that are regularly merged into development.
  • +
+

Supporting branches

+
    +
  • release-: To manage changes when merging from development to master. Once the new version is merged to master, the release-<version> branch is automatically removed.
  • +
  • hotfix-: To manage changes that are needed on an already released version (on any master branch) that need to be fixed with a new micro release.
  • +
+

Some branches contain the concerned version in its name, examples: release-1.75.0, hotfix-1.75.1. To manage new versions, tools are used to automatically merge between branches and tag the new version. +Once the new version is merged to master/master-<version, the release-/hotfix- branch are automatically deleted. There is only one release- and only hotfix- branch allowed at same time (enforced by the used Maven plugin).

+

Implementation branches

+

These kind of branches are manually created and maintained by developers who work on an issue. Such branches are used to create pull requests or to review the changes before merged into a permanent or supporting branch. +* feature-1234_short_description: Any branch that is supposed to contribute to development or a hotfix branch.

+

Dependency Management

+

Dependencies are managed in Maven POM files. For most dependencies the version is defined in sormas-base/pom.xml. \ +sormas-app and sormas-e2e-tests use Gradle instead.

+

Automatic updates

+

Process: At the beginning of each iteration, look over all dependency update PRs created by dependabot and process them according to the following rules: +1. Always use "Rebase and merge" instead of creating a merge commit! If needed use the comment @dependabot rebase to rebase the PR to the latest development commit. +2. If all CI checks are successful, minor and micro version updates can be merged +3. For major version updates read the changelog to understand implications and possible migration steps of the update. \ + Decide on you own wheter it is necessary to manually test the PR before merging. +4. If CI checks are negative or migration steps are needed, decide whether the update is important enough to justify the effort of additional work. \ +If the PR is not needed, you can use @dependabot ignore this major version or @dependabot ignore this minor version.

+

Payara

+

All required dependencies provided by the Payara application server are set to "provided" in maven and may only be updated in combination with an update of the Payara version. \ +These dependencies have been added to the dependabot ignore list, so no dependency update PR is automatically created.

+

Keycloak

+

The Keycloak version in SORMAS-Project only defines the version of the Keycloak admin client library. When a new version of Keycloak is available this should be updated after the Keycloak version is SORMAS-Docker has been updated. \ +This can be tested locally by building the updated Keycloak docker image and then using it in a container together with a local Payara instance.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/DEFINITION_OF_READY/index.html b/DEFINITION_OF_READY/index.html new file mode 100644 index 000000000000..b8c281fed15c --- /dev/null +++ b/DEFINITION_OF_READY/index.html @@ -0,0 +1,580 @@ + + + + + + + + + + + + + + + + + + Definition of Ready - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Definition of Ready

+

In order to be eligible for being implemented in the current iteration, an issue has to be marked with the ready label. This label indicates that the issue fulfills our Definition of Ready, which consists of the following requirements:

+
    +
  • A representative title that summarizes what the issue is about
  • +
  • A dedicated ticket type according to the content of the issue: feature, change, bug, or task
  • +
  • All mandatory sections of the issue template are filled in with unambiguous information, i.e. without any points of discussion
  • +
  • Features/changes that involve significant innovative UI/UX work (e.g. new sections, new UI elements, new workflows) provide mockups that visually describe how the feature/change is supposed to look like
  • +
  • Issues that are technically complex have at least rough implementation details
  • +
  • The issue is appropriately labeled, i.e. with the affected part of the application (android-app, vaadin-app, or backend) and the functional and/or technical area(s) it affects
  • +
+

Ideally, change and feature requests also have a story point estimation, specified in brackets after their title.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/DEMO_APP/index.html b/DEMO_APP/index.html new file mode 100644 index 000000000000..ef039add659f --- /dev/null +++ b/DEMO_APP/index.html @@ -0,0 +1,670 @@ + + + + + + + + + + + + + + + + + + + + + + Demo App - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Creating a demo app for a SORMAS demo server

+

Important: This only applies if you have setup your own SORMAS server for demo purposes and and want to give users easy access to it.

+

Step 1: Adjust the sormas-app.properties

+
    +
  1. Open the the apk file from the SORMAS release with a zip editor (e.g. 7zip).
  2. +
  3. Extract sormas-app.properties and open the the file for editing.
  4. +
  5. Set server.url.default to the URL of your SORMAS server's ReST interface.
  6. +
  7. Set user.name.default and user.password.default to the demo user (needs to be an informant or officer).
  8. +
  9. Overwrite the sormas-app.properties in the apk with your changed version.
  10. +
+

Step 2: Sign the changed apk file

+

Since the apk file has been changed it needs to be signed again.\ +Important: When you change and sign the apk file it is no longer compatible with the original apk file for automatic app update! If you still want to make this work you always have to sign new versions using the same keystore and put the changed apk-file into your SORMAS server for automatic app update.\

+
    +
  1. Create a keystore using keytool: keytool -genkey -v -keystore my-demo-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias
    +

    Note: keytool is located in the bin/ directory in your JDK. To locate your JDK from Android Studio, select File > Project Structure, and then click SDK Location and you will see the JDK location.

    +
    +
  2. +
  3. Download uber-apk-signer: https://github.com/patrickfav/uber-apk-signer/releases.
    +

    Note: this is the convenient way to do it. You can also get an Android SDK and follow the instructions given here

    +
    +
  4. +
  5. Sign the apk file: java -jar uber-apk-signer.jar --ks my-demo-key.jks -ksAlias my-alias --allowResign --apks sormas-version-demo.apk
    +

    See also: https://github.com/patrickfav/uber-apk-signer#command-line-interface

    +
    +
  6. +
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/DEVELOPMENT_ENVIRONMENT/index.html b/DEVELOPMENT_ENVIRONMENT/index.html new file mode 100644 index 000000000000..4520f9c04a58 --- /dev/null +++ b/DEVELOPMENT_ENVIRONMENT/index.html @@ -0,0 +1,1003 @@ + + + + + + + + + + + + + + + + + + + + + + Development Environment - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

Development Environment Setup

+

This step-by-step guide explains how to set up your development environment, using either Eclipse or IntelliJ for the backend and web UI and Android Studio for the mobile app. Please follow it strictly to make sure that development will run as smoothly as possible and your code adheres to our guidelines.

+

Please note that these instructions are optimized for Windows and Linux systems. If you're developing on a Mac and, we would be glad to get your feedback about how this guide can be extended with OS-specific instructions through our GitHub Discussions.

+

Step 1: Check Out the SORMAS Repository

+
    +
  • Download and install the latest Git version for your operating system
  • +
  • Optional: Install a Git client such as TortoiseGit or GitHub Desktop if you don't want to handle version control from the command-line or within your IDE
  • +
  • Optional: Clone the SORMAS-Project repository with git clone https://github.com/sormas-foundation/SORMAS-Project.git; if you want to use Git from within your IDE, you can also clone the repository in Step 4
  • +
  • Open Git Bash and execute the following command to ensure that rebase is used when pulling the development branch rather than merge: git config --global branch.development.rebase true
  • +
+

Step 2: Install Java

+

Download and install the Java 11 JDK (not JRE) for your operating system, which is also needed for the Server Setup. +We suggest using Zulu OpenJDK. If you're running Linux, please refer to the official documentation on how to install Zulu OpenJDK on your system.

+

Note: To work with the Android app JDK 17 is needed for the gradle build. The needed JDK is part of Android Studio, thus there is no need to manually install it.

+

The SORMAS CI is using JDK 17 to build all modules. The only known difference though are slight differences in the Java time API that affect unit tests, so again there is no need to setup two JDKs on your local system.

+

Step 3: Install Maven

+

The scripts in sormas-base/dev expect mvn as command-line tool. +Download and install Maven for your operating system, see binaries.

+

Step 4: Install a Local SORMAS Server

+

Please follow the Server Installation Instructions to set up a local SORMAS instance that you will use to test your code. Alternatively, you can also use Maven Cargo, or a Docker installation (not recommended at this time).

+

Step 5: Install and Configure Your IDE

+

IntelliJ

+
    +
  • Download and install the latest IntelliJ IDEA Ultimate; (newer than version of 2020-04-15 to enable debugging, see https://youtrack.jetbrains.com/issue/IDEA-216528)
  • +
  • Set the project SDK to the installed JDK
  • +
  • Optional: Clone the SORMAS-Project repository if you haven't done so already
  • +
  • Open the project in IntelliJ. Make sure the project is recognized by IntelliJ as a maven project; if not, right-click the pom.xml file in sormas-base and select Add as maven project.
  • +
  • Make sure that under File -> Project Structure -> Modules all modules EXCEPT sormas-app are recognized; if not, add the missing modules with the + button
  • +
  • Navigate to File -> Settings -> Plugins and make sure that Glassfish integration is enabled
  • +
  • Make a copy of sormas-base/dev.env.example, rename it to dev.env and set GLASSFISH_DOMAIN_ROOT to the location of the SORMAS domain inside your Payara installation
  • +
  • Run mvn install on the sormas-base project (e.g. by opening the Maven view and executing sormas-base -> Lifecycle -> install). \ + Alternatively, execute the dev/build.sh script. You can create a run configuration and use the Git bash executable as interpreter to directly run it from the IDE.
  • +
  • Execute dev/deploy-serverlibs.sh script
  • +
  • Add a Payara server to IntelliJ:
  • +
  • Open Run -> Edit Configurations, add a new configuration and choose the Glassfish server template
  • +
  • Click on Configure next to Application server and create a new server configuration by selecting your Payara installation directory
  • +
  • Check the After launch checkbox and specify the browser that you want SORMAS to open in once the server has been deployed
  • +
  • Enter http://localhost:6080/sormas-ui into the URL field
  • +
  • Make sure that the correct JRE is specified (your Java 11 JDK)
  • +
  • Enter the path to the SORMAS domain and the credentials that you've specified when setting up the server
  • +
  • Open the Deployment tab and add the artifacts sormas-ear, sormas-rest and sormas-ui (make sure to respect this order as there are dependencies between artifacts at startup)
  • +
  • Open the Logs tab and add a new log file pointing to the logs/server.log file in your SORMAS domain
  • +
  • Open the Startup/Connection tab and make sure that Pass environment variables is NOT checked; ignore warnings about the debug configuration not being correct
  • +
  • Open the config/domain.xml file in your domain directory and make sure that the java-config node contains the following code: <java-config classpath-suffix="" debug-enabled="true" debug-options="-agentlib:jdwp=transport=dt_socket,address=6009,server=n,suspend=y" ...
  • +
  • Set the default working directory for run configurations by navigating to Run -> Edit Configurations -> Templates -> Application and setting Working directory to $MODULE_WORKING_DIR$
  • +
  • Optional: Setup database access from Intellij: Open View -> Tool View -> Database, click on + icon and select DataSource -> PostgreSQL and configure the database (set user and password and download the missing driver files if needed)
  • +
+

Known issues

+
    +
  • The first time you build the project in IntelliJ, you have to switch the java compiler to "Eclipse" to workarround a dependency resolution problem in sormas-api.
  • +
+

Eclipse

+
    +
  • Download and install the latest Eclipse IDE for Enterprise Java and Web Developers
  • +
  • Set the default JRE of Eclipse to the installed JDK: Assigning the default JRE for the workbench
  • +
  • Optional: Clone the SORMAS-Project repository if you haven't done so already via File -> Import -> Git -> Projects from Git and cancel the process when you're asked to create a new project from the cloned repository
  • +
  • Import the projects from the SORMAS-Project repository into your workspace via File -> Import -> Maven -> Existing Maven Projects
  • +
  • Install the Payara Tools plugin
  • +
  • Install the Vaadin Plugin for Eclipse; the commercial UI designer is not needed
  • +
  • Add a Payara server to Eclipse and enter the credentials you specified when setting up the local SORMAS server
  • +
  • Make a copy of sormas-base/dev.env.example, rename it to dev.env and set GLASSFISH_DOMAIN_ROOT to the location of the SORMAS domain inside your Payara installation
  • +
  • Either run mvn install on the sormas-base project or execute the dev/build.sh script (for example with Git Bash)
  • +
  • Execute dev/deploy-serverlibs.sh script
  • +
  • Highlight all Eclipse projects and choose Maven -> Update Project from the right-click menu; perform the update for all projects
  • +
  • Start the Glassfish server and deploy sormas-ear, sormas-rest and sormas-ui by dragging the respective projects onto it, or use the Add and Remove... function by right-clicking on the server (make sure to respect this order as there are depdendencies between artifacts at startup)
  • +
  • Open your browser and type in http://localhost:6080/sormas-ui to test whether the server and IDE have been set up correctly
  • +
+

Android Studio

+

Please note: You only need to install Android Studio if you're developing code for the Android app. This is likely the case when you're adding new fields or entities to the system, or if you specifically want to work on the mobile app.

+
    +
  • Download and install the latest Android Studio version
  • +
  • Please make sure to run the installer with admin rights if you're using Windows
  • +
  • Ensure that the Android SDK installation path does not contain whitespaces; you can also change this later via Tools -> SDK Manager -> Android SDK Location
  • +
  • Open Android Studio and import the sormas-app module from the SORMAS-Project repository
  • +
  • Make a copy of keystore.properties.example and rename it to keystore.properties
  • +
  • Make sure to use the JDK version 11 (File -> Project Structure -> SDK Location -> JDK Location)
  • +
  • Build the Android Studio project by executing the Gradle build (this may be done automatically)
  • +
  • Add an emulator and set the SDK version to the minSdkVersion or targetSdkVersion from build.gradle; we suggest to test your code on both, but minSdkVersion should be preferred to ensure compatibility to the minimum supported SDK
  • +
  • Click on Run 'app' to install and run the app on your emulator; enter http://10.0.2.2:6080/sormas-rest as the server URL when you start the newly installed app for the first time
  • +
+

Important: Whenever you do or pull changes in the sormas-api project that you want to use in the mobile app or that are referenced there already, you need to execute the dev/build.sh script to notify the sormas-app project of the changes.

+

Step 6: Configure Code Formatting and Import Settings

+

In order to ensure a consistent code style and prevent so-called edit wars, we have set up custom configuration files for automatic code formatting and import ordering. Please make sure to adhere to the following steps for your IDE(s) before you start developing.

+

IntelliJ and Android Studio Settings

+
    +
  • Install the Eclipse Code Formatter for IntelliJ/Android Studio plugin
  • +
  • Open the plugin settings via File -> Settings -> Other Settings -> Eclipse Code Formatter and select Use the Eclipse Code Formatter
  • +
  • Under Eclipse formatter config, choose Eclipse workspace/project folder or config file and select sormas-base/java-formatter-profile.xml
  • +
  • Check Optimize Imports
  • +
  • Under Import order, choose From file and select sormas-base/java-importorder-profile.importorder
  • +
  • Make sure that Do not format other file types by IntelliJ formatter is selected
  • +
  • Go to Editor -> Code Style -> Java -> Imports and set Class count to use import with '*' and Names count to use static import with '*' to 99
  • +
  • Navigate to Editor -> General -> Auto Import and disable Optimize imports on the fly
  • +
+

Optional, but strongly recommended: +- Install the Save Actions plugin that automatically applies code formatting and import reordering whenever you save a file - otherwise you will manually have to do so (by default with Ctrl+Alt+L) +- Open the plugin settings via File -> Settings -> Other Settings -> Save Actions and make sure that the first three checkboxes under General and the first two checkboxes under Formatting Actions are selected

+

Eclipse Settings

+
    +
  • Open Window -> Preferences
  • +
  • Navigate to Java -> Code Style -> Formatter, import sormas-base/java-formatter-profile.xml and apply the changes
  • +
  • Navigate to Java -> Code Style -> Organize Imports and import sormas-base/java-importorder-profile.importorder
  • +
  • On the same screen, set Number of imports needed for .* and Number of static imports needed for .* to 99
  • +
  • On the same screen, make sure that Do not create import for types starting with a lowercase letter is checked and apply the changes
  • +
  • Navigate to Java -> Editor -> Save Actions and make sure that the following options are selected: Perform the selected actions on save, Format source code, Format all lines and Organize imports
  • +
+

Issues which can appear during installation process of the project

+
    +
  1. +

    If debug mode does not work: To replace opt\payara5\glassfish\modules\launcher.jar with sormas-base/setup/launcher.jar

    +
  2. +
  3. +

    For Windows: Please check your java_version. In case if you have the multiple java_versions installed on the system, it will always show to you the first version installed. + I had the java 8 instead of 11. + In order to fix it, go to environment variables, and move the 11 version up. And rerun the script. Seems that the console is reading those variables at the starting point, and the values of it can be updated only after console/script restart.

    +
  4. +
  5. +

    For Windows: Pay attention to the postgres SQL files rights permissions after unziping the downloaded ZIP archive. Files physically were present but next script error has been generated: + psql:setup.sql:7: ERROR: could not open extension control file "C:/Program Files/PostgreSQL/10/share/extension/temporal_tables.control": No such file or directory + -I checked the file rights, and under windows they has AV attribute, however, all others has only A attribute. When I was trying to open them with Notepad++ it was saying that such file does not exist. Do you want to create it? If yes will be pressed - another message saying that the file exists, appeared. Very strange scenario...

    +
  6. +
  7. +

    All the postgres commands (of added users, etc.) which were added at first startup of the application - will raise errors in case if such entity exists. Just ignore those errors at repeated installation of .\server-setup.sh

    +
  8. +
  9. +

    Check always the port number 6048 which can be occupied by an old instance of payara. + -> For every installation, kill all Java/javaw processes and check the availability of 6048 port number. + -> Delete files with generated domain folders and payara. In order to have a clean installation of each next ./server-setup.sh run.

    +
  10. +
  11. +

    For the sormas-base/dev scripts Maven needs to be installed as command-line tool or defined in sormas-base/dev.env as MVN_BIN which Maven to be used.

    +
  12. +
  13. +

    For eclipse formatted plugin, there is an issue for Idea: https://plugins.jetbrains.com/plugin/6546-eclipse-code-formatter - cannot save settings Path to custom eclipse folder is not valid - it works only when settings were saved from down to up. And not vice versa.

    +
  14. +
+

If something is still not working: + -> Stop the payara domain, run dev/deploy-serverlibs.sh to update libs + -> clean up (delete all from domains/sormas/autodeploy, domains/sormas/applications, domains/sormas/generated, and domains/sormas/osgi-cache) try to build again by executing mvn clean install -DskipTests on the sormas-base module + -> start the domain and deploy again

+

Avoid redeployment problems

+

Problem: Due to currently a not mitigated problem, it is only possible to deploy the sormas-ear.ear (contains sormas-backend) once without problems. If you undeploy it and deploy sormas-ear.ear again, the other artifacts sormas-uiand sormas-rest cannot successfully call the backend.

+

Workaround: Undeploy sormas-ear and all other sormas artifacts, restart the Payara domain, deploy sormas-ear again (the same or changed version).

+

Symptom: This exception occurs when sormas-ui or sormas-rest calls the sormas-backend.

+
Caused by: java.lang.IllegalArgumentException: Can not set java.util.Properties field de.symeda.sormas.backend.common.ConfigFacadeEjb.props to de.symeda.sormas.backend.common.ConfigFacadeEjb
+    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
+    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
+    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
+    at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
+    at java.base/java.lang.reflect.Field.set(Field.java:780)
+   at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl._inject(InjectionManagerImpl.java:594)
+
+

Additional info: +- You can undeploy and deploy all other modules without restarting the Payara domain, as long as nothing changes on sormas-ear (implicates sormas-api and sormas-backend). +- The problem occurs no matter if you deploy directly from your IDE or as packaged ears/wars into the autodeploy directory. +- Related ticket: #2511

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/EXTERNAL_MESSAGES_ADAPTER/index.html b/EXTERNAL_MESSAGES_ADAPTER/index.html new file mode 100644 index 000000000000..a6c95d0b9aa3 --- /dev/null +++ b/EXTERNAL_MESSAGES_ADAPTER/index.html @@ -0,0 +1,1041 @@ + + + + + + + + + + + + + + + + + + External Message Adapter Implementation Guide - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+ +
+
+ + + +
+
+ + + + + + + + +

External Message Adapter Implementation Guide

+

The project

+

A minimal maven project structure for an external message adapter module is shown below:

+
  my-message-adapter
+  |-- src
+    |   |-- main
+    |   |   |-- java
+    |   |   |   |-- my.project
+    |   |   |   |   |-- MyExternalMessageFacadeEjb.java
+    |   |   |-- resources
+    |   |   |   |-- version.txt
+    |   |-- test
+    |   |   |-- java
+    |   |   |   |-- my.project
+    |   |   |   |   |-- MyMessageAdapterTest.java
+    |   |   |-- resources
+    |-- pom.xml    
+
+
+

MyMessageAdapter.java

+

This is the main class of the external message adapter module. +It must implement the ExternalMessageAdapterFacade interface from the sormas-api module.

+

For more information please refer to the javadoc on the ExternalMessageAdapterFacade interface.

+

Example:

+
package my.project;
+
+import de.symeda.sormas.api.externalmessage.ExternalMessageAdapterFacade;
+import de.symeda.sormas.api.externalmessage.ExternalMessageDto;
+import de.symeda.sormas.api.externalmessage.ExternalMessageResult;
+
+import javax.ejb.Stateless;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Stateless(name = "MyExternalMessageFacade")
+public class MyExternalMessageFacadeEjb implements ExternalMessageAdapterFacade {
+
+    @Override
+    public ExternalMessageResult<List<ExternalMessageDto>> getExternalMessages(Date since) {
+        return new ExternalMessageResult<>(Collections.emptyList(), new Date(), true, null);
+    }
+
+    @Override
+    public ExternalMessageResult<String> convertToHTML(ExternalMessageDto message) {
+        return new ExternalMessageResult<>("", new Date(), true, null);
+    }
+
+    @Override
+    public ExternalMessageResult<byte[]> convertToPDF(ExternalMessageDto message) {
+        return new ExternalMessageResult<>(new byte[0], new Date(), true, null);
+    }
+
+    @Override
+    public String getVersion() {
+        return new BufferedReader(
+                new InputStreamReader(
+                        Objects.requireNonNull(MyExternalMessageFacadeEjb.class.getResourceAsStream("/version.txt")),
+                        StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n"));
+    }
+}
+
+

version.txt

+

This file contains the template string that describes this modules version. +It is used by the SORMAS server to display the version of configured external message module's version on the about page. +SORMAS also uses the version method for doing an availability check.

+

You can use the maven resource filtering to replace placeholders in the version.txt file with values from the pom.xml file.

+

Example:

+
${project.name} ${project.version}
+
+

pom.xml

+

This is the maven project configuration file.

+

The sormas-api must be added as a dependency to your project in order to be able to implement the ExternalMessageAdapterFacade interface.

+

Example with default dependencies for java-ee, sormas-api, logging and testing:

+
+Expand to see below the whole file content + + +
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+        <modelVersion>4.0.0</modelVersion>
+
+        <properties>
+            <sormas.version>1.85.0</sormas.version>
+            <warName>my-message-adapter</warName>
+
+            <javaee.version>8.0</javaee.version>
+            <slf4j.version>1.7.30</slf4j.version>
+            <junit.version>4.13.1</junit.version>
+        </properties>
+
+        <groupId>my.project</groupId>
+        <artifactId>my-message-adapter</artifactId>
+        <name>${project.artifactId}</name>
+        <packaging>war</packaging>
+        <version>0.1.0-SNAPSHOT</version>
+
+        <dependencies>
+            <dependency>
+                <groupId>javax</groupId>
+                <artifactId>javaee-web-api</artifactId>
+                <version>${javaee.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sormas-api</artifactId>
+                <version>${sormas.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-api</artifactId>
+                <version>${slf4j.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>${junit.version}</version>
+                <scope>test</scope>
+            </dependency>
+            ...
+        </dependencies>
+
+        <build>
+            <finalName>${project.artifactId}</finalName>
+            ...
+        </build>
+</project>
+
+ +
+ + +

+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <properties>
+        <sormas.version>1.85.0</sormas.version>
+        <warName>my-message-adapter</warName>
+
+        <junit.version>4.13.1</junit.version>
+        <mockito.version>3.6.0</mockito.version>
+        <assertj.version>3.18.1</assertj.version>
+
+        <commons-lang.version>3.11</commons-lang.version>
+        <commons-io.version>2.8.0</commons-io.version>
+        <slf4j.version>1.7.30</slf4j.version>
+        <javaee.version>8.0.1</javaee.version>
+
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <productionBranch>master</productionBranch>
+    </properties>
+
+    <groupId>my.project</groupId>
+    <artifactId>my-message-adapter</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>war</packaging>
+    <version>0.1.0-SNAPSHOT</version>
+
+    <repositories>
+        <repository>
+            <id>central</id>
+            <name>bintray</name>
+            <url>https://jcenter.bintray.com</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax</groupId>
+            <artifactId>javaee-web-api</artifactId>
+            <version>${javaee.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sormas-api</artifactId>
+            <version>${sormas.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.14.2</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-model</artifactId>
+            <version>3.8.4</version>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+
+        <!-- Provide version.txt that can be read for returning the desired version string of this module -->
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/version.txt</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>false</filtering>
+                <excludes>
+                    <exclude>**/version.txt</exclude>
+                </excludes>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>3.2.3</version>
+                <configuration>
+                    <warName>${warName}</warName>
+
+                    <filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
+                    <webResources>
+                        <resource>
+                            <directory>src/main/webapp</directory>
+                            <targetPath>/</targetPath>
+                            <filtering>false</filtering>
+                        </resource>
+                    </webResources>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>com.amashchenko.maven.plugin</groupId>
+                <artifactId>gitflow-maven-plugin</artifactId>
+                <version>1.15.1</version>
+                <configuration>
+                    <gitFlowConfig>
+                        <productionBranch>${productionBranch}</productionBranch>
+                        <developmentBranch>development</developmentBranch>
+                        <featureBranchPrefix>feature-</featureBranchPrefix>
+                        <releaseBranchPrefix>release-</releaseBranchPrefix>
+                        <hotfixBranchPrefix>hotfix-</hotfixBranchPrefix>
+                        <versionTagPrefix>v</versionTagPrefix>
+                    </gitFlowConfig>
+                    <commitMessagePrefix>[GITFLOW]</commitMessagePrefix>
+                    <useSnapshotInHotfix>true</useSnapshotInHotfix>
+                </configuration>
+            </plugin>
+
+            <!-- Code Coverage / activate Integration Tests -->
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>2.19.1</version>
+                <executions>
+                    <execution>
+                        <id>integration-test</id>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>integration-test</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>verify</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>0.8.5</version>
+                <inherited>true</inherited>
+                <executions>
+                    <execution>
+                        <id>prepare-coverage</id>
+                        <phase>generate-test-sources</phase>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>analyze-coverage</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>prepare-coverage-integration</id>
+                        <phase>pre-integration-test</phase>
+                        <goals>
+                            <goal>prepare-agent-integration</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>analyze-coverage-integration</id>
+                        <phase>post-integration-test</phase>
+                        <goals>
+                            <goal>report-integration</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
+
+ +
+ +

Deploying into the sormas domain

+

Set the interface.demis.jndiName in the sormas.properties of your SORMAS domain. +This JNDI name should match the location the external message ejb implemented in the module. +Following the above example project it would be:

+
interface.demis.jndiName=java:global/my-message-adapter/MyExternalMessageFacade
+
+

After changing the sormas.properties, restart the SORMAS domain then deploy your module.

+

Deploying in your local development environment

+
    +
  • in your IDE, import the module to your SORMAS-Project
  • +
  • make the module deploy on your glassfish server (add it just next to sormas-rest, sormas-ui and sormas-ear war files)
  • +
+

After that, you should be able to build and deploy SORMAS with your module. +If you want to exclude your module from the build, that can be done in IntelliJ as follows: +File -> Settings -> Build, Execution, Deployment -> Compiler -> Excludes

+

Deploying in your payara domain

+
    +
  • build the project with maven
  • +
  • deploy the built .war file in the sormas domain of your payara server
  • +
+

Testing the deployment

+

In the SORMAS web interface, go to the About menu, and you must see a green checkmark next to the External message adapter label and your module's version text provided by implementing the ExternalMessageAdapterFacade::getVersion method.

+

e.g.: +screenshot

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/I18N/index.html b/I18N/index.html new file mode 100644 index 000000000000..8bb375c98f47 --- /dev/null +++ b/I18N/index.html @@ -0,0 +1,688 @@ + + + + + + + + + + + + + + + + + + + + + + I18n - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + + + + + + +

SORMAS Translation FAQ

+

Who is translating SORMAS?

+

The translation of SORMAS relies on people from the community. If you would like to see SORMAS translated to your language, please read the points below and take part in the process.

+

How is SORMAS translated?

+

The official SORMAS translation is done using the Crowdin platform: https://crowdin.com/project/sormas

+

All translations done on the platform are automatically fed into SORMAS and are part of the bi-weekly release schedule. +This means that any text you translate will likely be part of the next SORMAS release. Once your server (or the server of your country) is updated you will be able to see the translations in the web app and mobile app.

+

How to participate in translating SORMAS

+

You can join the translation project by creating a free account on Crowdin. You can also use your existing GitHub account. Before starting to translate, you need to request write access to the project. A member of the project will either accept your request or get in touch with you as soon as possible.

+

Afterwards open the SORMAS project https://crowdin.com/project/sormas and select the language you would like to translate.

+

You will see all files that contain translatable texts:

+

SORMAS translation files

+

The most important one is captions.properties. It contains the captions for all the data entry fields of SORMAS. The captions are shared by the web app and mobile app.

+

Click on the file to open the Crowdin editor that allows you to go through all translation entries one by one as shown in the picture below:

+

SORMAS translation editor

+

How to add a new language to SORMAS

+

If the language you would like to translate is not available yet, please get in contact with us: https://github.com/sormas-foundation/SORMAS-Project

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/SECURITY/index.html b/SECURITY/index.html new file mode 100644 index 000000000000..ddae0ac23554 --- /dev/null +++ b/SECURITY/index.html @@ -0,0 +1,691 @@ + + + + + + + + + + + + + + + + + + + + + + Security - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Security Policies and Procedures

+

This document outlines security procedures and general policies for the SORMAS +project.

+ +

If you want to report a bug which is not security sensible, please submit an issue.

+

Reporting a Security Bug

+

Our team and community take all security bugs in SORMAS seriously. +Thank you for improving the security of SORMAS. We appreciate your efforts and +responsible disclosure and will make every effort to acknowledge your +contributions. +Unfortunately, SORMAS does not offer a paid bug bounty programme or other forms of compensation.

+

Report security bugs by emailing at security@sormas.org.

+

We will acknowledge your email and follow up with a response within 10 business days, or explain why a reply may take longer. The response will indicate the next steps in handling your report. +After the initial reply to your report, the security team will endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.

+

Report security bugs in third-party modules to the person or team maintaining +the module.

+

Disclosure Policy

+

When the security team receives a security bug report, they will assign it to a +primary handler. This person will coordinate the fix and release process, +involving the following steps:

+
    +
  • Confirm the problem and determine the affected versions.
  • +
  • Audit code to find any potential similar problems.
  • +
  • Prepare fixes for all releases still under maintenance. These fixes will be + released as fast as possible.
  • +
+

Comments on this Policy

+

If you have suggestions on how this process could be improved please submit a +pull request.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/SERVER_CUSTOMIZATION/index.html b/SERVER_CUSTOMIZATION/index.html new file mode 100644 index 000000000000..c54be5163c37 --- /dev/null +++ b/SERVER_CUSTOMIZATION/index.html @@ -0,0 +1,865 @@ + + + + + + + + + + + + + + + + + + + + + + Customization - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Configuring and Customizing a SORMAS Server

+

SORMAS has been created and is developed as an international system that can potentially be used everywhere in the world. However, every country naturally has its own requirements and processes, and in addition, there is a set of information that needs to be specified before a SORMAS instance can function properly. +For this reason, there are a number of ways in which such a SORMAS instance can be configured and customized:

+
    +
  • Server Configuration: The basic server configuration is available as a .properties file, needs to be adjusted for every SORMAS instance, and is relevant for both production and development. This should be edited directly after installing the server.
  • +
  • Feature Configuration: Most SORMAS features are optional and can be turned on or off directly in the database.
  • +
  • Disease Configuration: SORMAS supports a large number of infectious diseases which can be enabled or disabled and further customized directly in the database.
  • +
  • Deletion Configuration: SORMAS can be configured to automatically delete entities in the database.
  • +
  • Infrastructure Data: Most infrastructure data (except countries and continents) are not shipped with SORMAS because they are country-specific. Importing the infrastructure data of your country (or creating some dummy data) is one of the first things you should do after setting up a SORMAS server.
  • +
+

Beyond that, the Wiki contains even more customization options:

+ +

Server Configuration

+

The general SORMAS configuration is stored in the sormas.properties file that you can find in your payara domain folder. When you set up a new SORMAS server, please make sure to go over all entries in that file and adjust their values if necessary. +Each property has an explanation telling you its purpose, and some of them also have a default value that you can use to revert the changes you've made.

+

Most of these properties are commented (indicated by a # in front of their name and value), which means that the default value will automatically be used (e.g. the path to temporary files on the server) or the associated feature will not be used at all (e.g. the custom branding properties or the configuration of an external symptom journal). +Some properties however are not commented, but also don't have a default value. It is strongly recommended to enter values for these properties because they are required for some parts of SORMAS to work correctly, or because they are very specific to your individual SORMAS instance. +This especially applies to the country.locale and country.name properties, which will cause serious problems while using the application if left empty.

+

Important: The sormas.properties file contains all properties that existed in the SORMAS version that you initially installed on your server. New properties added in more recent SORMAS versions are not automatically added! If you're operating a SORMAS server, we strongly suggest to read the release notes of new versions. +If properties have been added to this file, they will be communicated in these notes so that you can add them to your properties file.

+

This Wiki page contains a list and explanation of all currently configurable properties.

+

Feature Configuration

+

Some of the features of SORMAS can be enabled or disabled to further customize the system. Right now, this is only possible directly in the featureconfiguration table in the database. This table contains one entry for every configurable feature and is automatically populated during server startup. Setting the enabled to true or false will enable or disable the feature, respectively. +Changes are immediately applied to the running system and don't require a server restart.

+

The columns region, district, disease and enddate are currently only applicable for the line listing feature and define the scope in which the line listing is used. Line listing is configurable from the user interface and does not need to be manually edited in the database.

+

Important: If you're using the mobile app, you also need to update the changedate to the current date and time whenever you enable or disable a feature! Otherwise the mobile applications will not be notified about the change.

+

This Wiki page contains a list and explanation of all currently configurable features.

+

Disease Configuration

+

SORMAS supports a wide range of diseases, and not all of those might be relevant to every SORMAS instance or might be used in a different context. As with features, configuring diseases is currently only possible directly in the database via the diseaseconfiguration table. All diseases have a default value for each of their properties that is applied when the respective database entry is empty. +Changing these entries overrides that default value. Unlike with features, disease configurations are cached and therefore require you to restart the server before they are applied.

+

Important: If you're using the mobile app, you also need to update the changedate to the current date and time whenever you change a disease configuration! Otherwise the mobile applications will not be notified about the change.

+

This Wiki page contains a list and explanation of all currently configurable disease properties.

+

Deletion Configuration

+

SORMAS can be set up to automatically delete entities after a specific time period. +There are seven core entities for which automatic deletion can be enabled and configured: Case, Contact, Event, Event Participant, Immunization, Travel Entry, and Campaign. +This configuration is currently only possible directly in the database via the deleteconfiguration table, which already contains rows for each of these entities. +The table consists of the following columns:

+
    +
  • entityType: The name of the entity that supports automatic deletion.
  • +
  • deletionReference: The reference date for the calculation of the date on which deletion takes place (see below).
  • +
  • deletionPeriod: The number of days after which an entity is deleted, starting with the deletion reference. The minimum is 7.
  • +
+

Both deletionReference and deletionPeriod need to be filled in order for the automatic deletion to take place. Entities for which at least one of these fields is left empty will not be automatically deleted. Deletion is executed via a nightly cron job and might therefore not happen immediately when the deletion date has been reached.

+

Deletion Reference

+

The deletionReference field has four possible values which define the date that is used to calculate whether an entity needs to be deleted (i.e., when the date calculated by subtracting the deletion period from the current date is before the deletion reference date, the entity is deleted). +A MANUAL_DELETION entry can exist in parallel to one of the other entries, and if both entries are configured, deletion is executed as soon as the threshold of one of these entries is met.

+
    +
  • CREATION: The creation date of the entity will be used.
  • +
  • END: The latest change date of the entity itself and any of its depending entities will be used. E.g. for cases, this includes but is not limited to its epi data, symptoms, or hospitalization.
  • +
  • ORIGIN: This is currently only implemented for travel entries and means that the arrival date of the entity will be used. If this is specified for any other entity, the deletion job will be stopped and throw an error.
  • +
  • REPORT: The report date of the entity will be used. This is currently not implemented for event participants and campaigns.
  • +
  • MANUAL_DELETION: The date on which the entity was manually deleted by a user.
  • +
+

Infrastructure Data

+

When you start a SORMAS server for the first time and the createDefaultEntities property is enabled, some default infrastructure data is generated to ensure that the server is usable and the default users can be created. +It is recommended (and, unless you're working on a demo server, necessary) to archive this default data and import the official infrastructure data of the country or part of the country that you intend to use SORMAS in instead.

+

SORMAS by default splits infrastructure data into four mandatory categories. Starting from the highest administrative division, these are Regions, Districts, Communities, and Health Facilities. +In addition, Points of Entry represent places like harbors and airports where people are frequently entering the country, while Laboratories are technically health facilities that are specifically used for sample testing purposes. The Area infrastructure type can be enabled in the feature configuration and adds another optional layer of infrastructure above Regions. +Finally, it is possible to add Countries, Subcontinents and Continents to your system if you also want to collect data from outside the country SORMAS is used in (e.g. because you want to record travels or events).

+

Importing Infrastructure

+

To import your data for one of the administrative divisions, log in as the default admin user (which is created even when createDefaultEntities is disabled) and open the Configuration menu. Open any of the tabs for the infrastructure data you want to import and click on the Import button on the top right. +You can download an import guide from within the popup window that will be opened, containing detailed instructions about the import process and the steps you need to go through in order to successfully import your data.

+

Make sure that you always start with the highest administrative division when importing (i.e. at least Countries if you want to collect data from other countries as well, Areas if enabled, or Regions otherwise) and work your way down to the lowest, because lower divisions typically contain mandatory references to higher divisions.

+

For Countries, Subcontinents and Continents, SORMAS provides a default import that allows you to automatically add a complete set of data to your system. For Countries, this default data equals to the official list of countries provided by the WHO. For Subcontinents and Continents, the list is based on the data used by the Robert Koch Institut.

+

User Role Configuration

+

A user is the user account for employees who have access to SORMAS. Similarly, there are technical users that allow external systems to access data. +Users have one or sometimes more user roles. The roles of a user can be chosen by an admin when creating a new user or editing an existing one. +Each user role is defined by a set of user rights, a jurisdiction that represents the area of responsibility and a set of notifications. For example, the jurisdiction of a surveillance officer is the district, which is defined on the user. User roles that have different areas of responsibility are therefore not combinable.

+

grafik

+

SORMAS comes with an extensive list of user rights that are used to check which data and functionality can be access by the user in the backend and the user interface. +To cover the typical use cases, SORMAS defines a set of default user roles that are automatically created when setting up a SORMAS instance. +Most user rights define an action related to an entity type, e.g. the user right CASE_EDIT allows users to edit case data. +Notifications define when the user with the given role should get an SMS and/or Email notification.

+

The following automatically generated documents list and describe all user rights, notifications and the default user roles: +* https://github.com/sormas-foundation/SORMAS-Project/blob/development/sormas-api/src/main/resources/doc/SORMAS_User_Roles.xlsx

+

User roles are fully configurable, allowing admins to create new user roles and edit existing ones, to customize the instance to the given needs and to make sure data protection requirements are fulfilled.

+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/SERVER_DOCKER_SETUP/index.html b/SERVER_DOCKER_SETUP/index.html new file mode 100644 index 000000000000..60d03d93e6db --- /dev/null +++ b/SERVER_DOCKER_SETUP/index.html @@ -0,0 +1,693 @@ + + + + + + + + + + + + + + + + + + Installing a SORMAS Server for development - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Installing a SORMAS Server for development

+

Note: This guide explains how to configure a SORMAS server on Linux and Windows systems for development. Please note that there is no database setup because the script supposes the use of the Docker Postgresql image (see SORMAS-Docker).

+

Content

+ + + +

Prerequisites

+

Java 11

+

See Installing Java

+

SORMAS just recently moved to Java 11. We still need to support Java 8 for a transition period. Therefore, please just +use Java 8 language features for now.

+

Ant

+

Download and install Ant, it can be done from Ant site or with packages from your Linux distribution.

+

Postgres Database

+

See Installing Postgresql

+

Alternatively you can use the Docker image available in SORMAS-Docker repository.

+

SORMAS Server

+

Install you own Payara server (see installing SORMAS Server).

+

This script will download Payara (if needed) and install SORMAS in the Payara server.

+

You can edit this script to change paths and ports.

+

Other steps : +* IMPORTANT: Adjust the SORMAS configuration for your country in /opt/domains/sormas/sormas.properties +* Adjust the logging configuration in ${HOME}/opt/domains/sormas/config/logback.xml based on your needs (e.g. configure and activate email appender) +* Build and deploy applications (ear and war) with you IDE.

+

Keycloak

+

See Keycloak for how to install Docker locally.

+

If you are doing active development on Keycloak (themes, authentication mechanisms, translations, etc.) it's recommended to install the standalone variant.

+

VAADIN Debug Mode

+

To enable VAADIN Debug Mode, go to sormas-ui/src/main/webapp/WEB-INF/web.xml and set productionMode to false. +Make sure not to commit your changes to these files, for example by using .gitignore. To access the debug Window, got to /sormas-ui/?debug. You may need to log in as admin once first.

+

Other components

+

See Installing a SORMAS Server

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/SERVER_SETUP/index.html b/SERVER_SETUP/index.html new file mode 100644 index 000000000000..054a5e3950a2 --- /dev/null +++ b/SERVER_SETUP/index.html @@ -0,0 +1,1442 @@ + + + + + + + + + + + + + + + + + + + + + + Setup - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

Installing a SORMAS Server

+

Note: This guide explains how to set up a SORMAS server on Linux and Windows systems, the latter only being intended for usage on development systems. Please also note that certain parts of the setup script will not be executed on Windows.

+

Content

+ + + +

Prerequisites

+

Java 11

+

Download and install the Java 11 JDK (not JRE) for your operating system. We suggest using the Zulu OpenJDK.

+

Linux

+
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9
+sudo apt-add-repository 'deb https://repos.azul.com/zulu/deb/ stable main'
+sudo apt-get update
+sudo apt-get install zulu11
+
+

Windows

+

For testing and development environments we suggest to download and run the installer of the Java 11 JDK for 32 or 64 bit client systems (depending on your system). +You can check your Java version from the shell/command-line using: java -version.

+

Postgres Database

+
    +
  • Install PostgreSQL (currently 14 to 15) on your system (manuals for all OS can be found here: https://www.postgresql.org/download)
  • +
  • Set max_connections = 288 and max_prepared_transactions = 256 (at least, sum of all connection pools) in postgresql.conf (e.g. /etc/postgresql/14.0/main/postgresql.conf; C:/Program Files/PostgreSQL/14.0/data) - make sure the property is uncommented and restart the service to apply the changes.
  • +
  • Install the "temporal tables" extension for Postgres (https://github.com/arkhipov/temporal_tables)
  • +
  • Windows: Download the latest version for your Postgres version: +
  • +
  • Linux (see https://github.com/arkhipov/temporal_tables#installation) + bash + sudo apt-get install libpq-dev + sudo apt-get install postgresql-server-dev-all + sudo apt install pgxnclient + #Check for GCC: + gcc --version # and install if missing + sudo pgxn install temporal_tables + # The packages can be removed afterward
      +
    • Add the PostgreSQL path (/etc/PostgreSQL/10/bin) to Environment Variables
    • +
    +
  • +
+

SORMAS Server

+

Get the latest SORMAS build by downloading the ZIP archive from the latest release on GitHub: https://github.com/sormas-foundation/SORMAS-Project/releases/latest

+

Install on Linux

+

Unzip the archive, copy/upload its contents to /root/deploy/sormas/$(date +%F) and make the setup script executable (as root user).

+
sudo su
+mkdir -p /root/deploy/sormas
+cd /root/deploy/sormas
+SORMAS_VERSION=1.y.z
+wget https://github.com/sormas-foundation/SORMAS-Project/releases/download/v${SORMAS_VERSION}/sormas_${SORMAS_VERSION}.zip
+unzip sormas_${SORMAS_VERSION}.zip
+mv deploy/ $(date +%F)
+rm sormas_${SORMAS_VERSION}.zip
+chmod +x $(date +%F)/server-setup.sh
+
+

Install on Windows

+
    +
  • Download & install Git for Windows. This will provide a bash emulation that you can use to run the setup script: https://gitforwindows.org/
  • +
  • Unzip the ZIP archive (e.g. into you download directory)
  • +
  • Open Git Bash and navigate to the setup sub-directory
  • +
+

Auditing

+

You can configure the audit logger of SORMAS by providing a Logback configuration file and setting the audit.logger.config property accordingly. An example is provided in sormas-base/setup/audit-logback.xml. Not specifying a value for the property will effectively disable the audit log.

+

Sormas installation

+
    +
  • Optional: Open server-setup.sh in a text editor to customize the install paths, database access and ports for the server. The default ports are 6080 (HTTP), 6081 (HTTPS) and 6048 (admin). Important: Do not change the name of the database user. The predefined name is used in the statements executed in the database.
  • +
  • Set up the database and a Payara domain for SORMAS by executing the setup script: sudo -s ./server-setup.sh Press enter whenever asked for it
  • +
  • IMPORTANT: Make sure the script executed successfully. If anything goes wrong you need to fix the problem (or ask for help), then delete the created domain directory and re-execute the script.
  • +
  • IMPORTANT: Adjust the SORMAS configuration for your country in /opt/domains/sormas/sormas.properties
  • +
  • Adjust the logging configuration in /opt/domains/sormas/config/logback.xml based on your needs (e.g. configure and activate email appender)
  • +
  • Linux: Update the SORMAS domain
  • +
+

Keycloak Server

+

Note: SORMAS also comes with a basic auth mechanism using an JEE authentication realm. This authentication mechanism should only be used for development environments. For production environments always use the keycloak authentication mechansim. See Authentication & Authorization.

+

Keycloak can be set up in two ways: +* as a Docker container (for just using Keycloak approach) +* as a Standalone installation (for doing development in Keycloak like themes, SPIs)

+

Keycloak as a Docker container

+

To be done only in the situation when SORMAS is already installed on the machine as a standalone installation.

+

For complete Docker setup see the SORMAS-Docker repository.

+

Prerequisites +* SORMAS Server is installed +* PostgreSQL is installed +* Docker is installed +* Open and edit sormas-base/setup/keycloak/keycloak-setup.sh with your system's actual values (on Windows use Git Bash).

+

Setup +* Run sormas-base/setup/keycloak/keycloak-setup.sh +* Update sormas.properties file in the SORMAS domain with the property authentication.provider=KEYCLOAK

+

Keycloak as a standalone installation

+

Prerequisites +* SORMAS Server is installed +* PostgreSQL is installed

+

Setup

+

Setting Keycloak up as a standalone installation as described in the Getting started section of the official guide +* Make sure to configure Keycloak with PostgreSQL Database +* Set up an Admin User +* Copy the themes folder content to ${KEYCLOAK_HOME}/themes as described here +* Deploy the sormas-keycloak-service-provider as described here +* Update the sormas-base/setup/keycloak/SORMAS.json file by replacing the following placeholders: ${SORMAS_SERVER_URL}, ${KEYCLOAK_SORMAS_UI_SECRET}, ${KEYCLOAK_SORMAS_BACKEND_SECRET}, ${KEYCLOAK_SORMAS_REST_SECRET} +* Create the SORMAS Realm by importing sormas-base/setup/keycloak/SORMAS.json see Create a New Realm +* Update the sormas-* clients by generating new secrets for them +* Update the realm's email settings to allow sending emails to users

+

To update the SORMAS Server run the following commands:

+
${ASADMIN} set-config-property --propertyName=payara.security.openid.clientSecret --propertyValue=${KEYCLOAK_SORMAS_UI_SECRET} --source=domain
+${ASADMIN} set-config-property --propertyName=payara.security.openid.clientId --propertyValue=sormas-ui --source=domain
+${ASADMIN} set-config-property --propertyName=payara.security.openid.scope --propertyValue=openid --source=domain
+${ASADMIN} set-config-property --propertyName=payara.security.openid.providerURI --propertyValue=http://localhost:${KEYCLOAK_PORT}/keycloak/auth/realms/SORMAS --source=domain
+${ASADMIN} set-config-property --propertyName=sormas.rest.security.oidc.json \
+        --propertyValue="{\"realm\":\"SORMAS\",\"auth-server-url\":\"http://localhost:${KEYCLOAK_PORT}/auth\",\"ssl-required\":\"external\",\"resource\":\"sormas-rest\",\"credentials\":{\"secret\":\"${KEYCLOAK_SORMAS_REST_SECRET}\"},\"confidential-port\":0,\"principal-attribute\":\"preferred_username\",\"enable-basic-auth\":true}" \
+        --source=domain
+${ASADMIN} set-config-property --propertyName=sormas.backend.security.oidc.json --propertyValue="{\"realm\":\"SORMAS\",\"auth-server-url\":\"http://localhost:${KEYCLOAK_PORT}/auth/\",\"ssl-required\":\"external\",\"resource\":\"sormas-backend\",\"credentials\":{\"secret\":\"${KEYCLOAK_SORMAS_BACKEND_SECRET}\"},\"confidential-port\":0}" --source=domain
+
+

where: +* ${ASADMIN} - represents the location to ${PAYARA_HOME}\bin\asadmin +* ${KEYCLOAK_PORT} - the port on which keycloak will run +* ${KEYCLOAK_SORMAS_UI_SECRET} - is the secret generated in Keycloak for the sormas-ui client +* ${KEYCLOAK_SORMAS_REST_SECRET} - is the secret generated in Keycloak for the sormas-rest client +* ${KEYCLOAK_SORMAS_BACKEND_SECRET} - is the secret generated in Keycloak for the sormas-backend client

+

Then update sormas.properties file in the SORMAS domain with the property authentication.provider=KEYCLOAK

+

Connect Keycloak to an already running instance of SORMAS

+

after setting up Keycloak as one of the described options above

+

In case Keycloak is set up alongside an already running instance of SORMAS, these are the steps to follow to make sure already existing users can access the system: +1. Manually create an admin user in Keycloak for the SORMAS realm Creating a user (username has to be the same as admin's username in SORMAS) +2. If your server is not using SSL (e.g. because it's a development environment), you need to update the Root URL of the sormas-ui client in the SORMAS realm to use http instead of https +3. Login to SORMAS and trigger the Sync Users button from the Users page +4. This will sync users to Keycloak keeping their original password - see SORMAS Keycloak Service Provider for more information about this

+

Keycloak configuration

+

More about the default configuration and how to customize can be found here Keycloak

+

Web Server Setup

+

Apache Web Server

+

Note: This is not necessary for development systems. When you are using SORMAS in a production environment you should use a http server like Apache 2 instead of putting the Payara server in the first line. +Here are some things that you should do to configure the Apache server as a proxy:

+

Activate all needed modules:

+
a2enmod ssl
+a2enmod rewrite
+a2enmod proxy
+a2enmod proxy_http
+a2enmod headers
+
+

Create a new site /etc/apache2/sites-available/your.sormas.server.url.conf (e.g. sormas.org.conf).

+

Force SSL secured connections: redirect from http to https:

+
<VirtualHost *:80>
+        ServerName your.sormas.server.url
+        RewriteEngine On
+        RewriteCond %{HTTPS} !=on
+        RewriteRule ^/(.*) https://your.sormas.server.url/$1 [R,L]
+</VirtualHost>
+<IfModule mod_ssl.c>
+<VirtualHost *:443>
+        ServerName your.sormas.server.url
+        ...
+</VirtualHost>
+</IfModule>
+
+

Configure logging:

+
ErrorLog /var/log/apache2/error.log
+LogLevel warn
+LogFormat "%h %l %u %t \"%r\" %>s %b _%D_ \"%{User}i\"  \"%{Connection}i\"  \"%{Referer}i\" \"%{User-agent}i\"" combined_ext
+CustomLog /var/log/apache2/access.log combined_ext
+
+

SSL key config:

+
SSLEngine on
+SSLCertificateFile    /etc/ssl/certs/your.sormas.server.url.crt
+SSLCertificateKeyFile /etc/ssl/private/your.sormas.server.url.key
+SSLCertificateChainFile /etc/ssl/certs/your.sormas.server.url.ca-bundle
+
+# disable weak ciphers and old TLS/SSL
+SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
+SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE$
+SSLHonorCipherOrder off
+
+

Add a proxy pass to the local port:

+
ProxyRequests Off
+ProxyPass /sormas-ui http://localhost:6080/sormas-ui
+ProxyPassReverse /sormas-ui http://localhost:6080/sormas-ui
+ProxyPass /sormas-rest http://localhost:6080/sormas-rest
+ProxyPassReverse /sormas-rest http://localhost:6080/sormas-rest
+
+

Configure security settings:

+
Header always set X-Content-Type-Options "nosniff"
+Header always set X-Xss-Protection "1; mode=block"
+# Disable Caching
+Header always set Cache-Control "no-cache, no-store, must-revalidate, private"
+Header always set Pragma "no-cache"
+
+Header always set Content-Security-Policy \
+        "default-src 'none'; \
+        object-src 'self'; \
+        script-src 'self' 'unsafe-inline' 'unsafe-eval'; \
+        connect-src https://fonts.googleapis.com https://fonts.gstatic.com 'self'; \
+        img-src *; \
+        style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; \
+        font-src https://fonts.gstatic.com 'self'; \
+        frame-src 'self'; \
+        worker-src 'self'; \
+        manifest-src 'self'; \
+        frame-ancestors 'self'
+
+# The Content-Type header was either missing or empty.
+# Ensure each page is setting the specific and appropriate content-type value for the content being delivered.
+AddType application/vnd.ms-fontobject    .eot
+AddType application/x-font-opentype      .otf
+AddType image/svg+xml                    .svg
+AddType application/x-font-ttf           .ttf
+AddType application/font-woff            .woff
+
+

Activate output compression (very important!):

+
<IfModule mod_deflate.c>
+        AddOutputFilterByType DEFLATE text/plain text/html text/xml
+        AddOutputFilterByType DEFLATE text/css text/javascript
+        AddOutputFilterByType DEFLATE application/json
+        AddOutputFilterByType DEFLATE application/xml application/xhtml+xml
+        AddOutputFilterByType DEFLATE application/javascript application/x-javascript
+        DeflateCompressionLevel 1
+</IfModule>
+
+

Provide the Android apk:

+
Options -Indexes
+AliasMatch "/downloads/sormas-(.*)" "/var/www/sormas/downloads/sormas-$1"
+
+

For the Apache 2 security configuration we suggest the following settings (/etc/apache2/conf-available/security.conf):

+
ServerTokens Prod
+ServerSignature Off
+TraceEnable Off
+
+Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
+Header unset X-Frame-Options
+Header always set X-Frame-Options SAMEORIGIN
+Header unset Referrer-Policy
+Header always set Referrer-Policy "same-origin"
+Header edit Set-Cookie "(?i)^((?:(?!;\s?HttpOnly).)+)$" "$1;HttpOnly"
+Header edit Set-Cookie "(?i)^((?:(?!;\s?Secure).)+)$" "$1;Secure"
+
+Header unset X-Powered-By
+Header unset Server
+
+
    +
  • In case you need to update the site config while the server is running, use the following command to publish the changes without the need for a reload:
  • +
+
apache2ctl graceful
+
+

Firewall

+
    +
  • The server should only publish the ports that are needed. For SORMAS this is port 80 (HTTP) and 443 (HTTPS). In addition you will need the SSH port to access the server for admin purposes.
  • +
  • We suggest to use UFW (Uncomplicated Firewall) which provides a simple interface to iptables:
  • +
+
sudo apt-get install ufw
+sudo ufw default deny incoming
+sudo ufw default allow outgoing
+sudo ufw allow ssh
+sudo ufw allow http
+sudo ufw allow https
+sudo ufw enable
+
+

Postfix Mail Server

+

Install postfix and mailutils

+
apt install aptitude
+aptitude install postfix
+-> choose "satelite system"
+apt install mailutils
+
+

Configure your system

+
nano /etc/aliases
+-> add "root: enter-your@support-email-here.com"
+nano /opt/domains/sormas/config/logback.xml
+-> make sure "EMAIL_ERROR" appender is active and sends out to your email address
+
+

Testing the Server Setup

+

Use SSL Labs to test your server security config: https://www.ssllabs.com/ssltest

+

R Software Environment

+

In order to enable disease network diagrams in the contact dashboard, R and several extension packages are required. +Then the Rscript executable has to be configured in the sormas.properties file. +This can be conveniently accomplished by executing the R setup script from the SORMAS ZIP archive (see SORMAS Server):

+
    +
  • If the SORMAS installation has been customized, r-setup.sh the install paths may have to be adjusted accordingly with a text editor.
  • +
  • Execute R setup script and follow its instructions.
  • +
+
chmod +x r-setup.sh
+./r-setup.sh
+
+

SORMAS to SORMAS Certificate Setup

+

To be able to communicate with other SORMAS instances, there are some additional steps which need to be taken, in order to set +up the certificate and the truststore. Please see the related guide for detailed instructions regarding SORMAS to SORMAS setup. +

+

Troubleshooting

+

Problem: Login fails

+

Check that the users table does have a corresponding entry. If not, the database initialization that is done when deploying sormas-ear.ear probably had an error.

+

Problem: Server is out of memory

+

Old servers were set up with a memory size of less than 2048MB. You can change this using the following commands:

+
/opt/payara-172/glassfish/bin/asadmin --port 6048 delete-jvm-options -Xmx512m
+/opt/payara-172/glassfish/bin/asadmin --port 6048 delete-jvm-options -Xmx1024m
+/opt/payara-172/glassfish/bin/asadmin --port 6048 create-jvm-options -Xmx2048m
+
+

Alternative: You can edit the settings directly in the domain.xml in the config directory of the SORMAS domain. Just search for Xmx - there should be two entries that need to be changed.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/SERVER_UPDATE/index.html b/SERVER_UPDATE/index.html new file mode 100644 index 000000000000..28e98f850acf --- /dev/null +++ b/SERVER_UPDATE/index.html @@ -0,0 +1,1104 @@ + + + + + + + + + + + + + + + + + + + + + + Update - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

Updating a SORMAS Server

+

SORMAS releases starting from 1.21.0 contain a script that automatically updates and deploys the server. If you are using an older version and therefore need to do a manual server update, please download the 1.21.0 release files and use the commands specified in the server-update.sh script.

+

Preparations

+
    +
  • Get the latest release files (deploy.zip) from https://github.com/sormas-foundation/SORMAS-Project/releases/latest
  • +
  • Unzip the archive and copy/upload its contents to /root/deploy/sormas/$(date +%F) + bash + cd /root/deploy/sormas + SORMAS_VERSION=1.y.z + wget https://github.com/sormas-foundation/SORMAS-Project/releases/download/v${SORMAS_VERSION}/sormas_${SORMAS_VERSION}.zip + unzip sormas_${SORMAS_VERSION}.zip + mv deploy/ $(date +%F) + rm sormas_${SORMAS_VERSION}.zip
  • +
+

Breaking Updates

+

The following is a list of version that have breaking changes in the update. You only have to consider this if you are coming from an earlier version. For fresh installs, this is not relevant.

+

1.67.0

+

Coming from 1.66.4 or earlier, you have to update the payara server, as explained in the Payara migration.

+

1.73.0

+

Deploying this release will clear the userrolesconfig and userrole_userrights tables and overwrite them with the default user role configurations of SORMAS. If you added entries to these tables in order to customize the user roles on your system, please run the following queries before deploying this release in order to prevent data loss:

+
-- Retrieve all customized roles
+SELECT * FROM userrolesconfig;
+
+-- Overridden rights for roles
+SELECT c.userrole, ur.userright FROM userroles_userrights ur LEFT JOIN userrolesconfig c (ON c.id = ur.userrole_id);
+
+

After deploying the new version, the information retrieved from these queries can be used to alter the new user role configurations accordingly.

+

1.81.0

+

The temporal tables extension is replaced during the deployment of the backend. As a preparation the following SQL needs to be executed on the SORMAS database using the postgres user.

+
-- versioning function will be replaced during server backend startup
+ALTER FUNCTION public.versioning() OWNER TO sormas_user;
+
+

After the successful deployment, you can run the following SQL to get rid of the no longer used extension:

+
-- needed because `ALTER FUNCTION ... NO DEPENDS ON EXTENSION` is not possible in Postgres 10 and below
+DELETE FROM pg_depend
+WHERE deptype = 'e'
+  AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'temporal_tables')
+  and objid = (SELECT oid FROM pg_proc WHERE proname = 'versioning');
+
+DROP EXTENSION IF EXISTS temporal_tables;
+
+

1.85.0

+

Payara is updated from 5.2021.10 to 5.2022.5. +If you are not using SORMAS-Docker, please follow the Payara migration guide.

+

1.89.0

+

When installing this version on new systems or upgrading postgres version on Ubuntu make sure you have at least Ubuntu LTS 20 with postgres 14 (or 18 with postgres 12 from APT).

+

Automatic Server Update

+
    +
  • Navigate to the folder containing the unzipped deploy files: + cd /root/deploy/sormas/$(date +%F)
  • +
  • Make the update script executable: + chmod +x server-update.sh
  • +
  • Optional: Open server-update.sh in a text editor to customize the values for e.g. the domain path or the database name. You only need to do this if you used custom values while setting up the server.
  • +
  • Execute the update script and follow the instructions: + ./server-update.sh
  • +
  • If anything goes wrong, open the latest update log file (by default located in the "update-logs" folder in the domain directory) and check it for errors.
  • +
+

Restoring the Database

+

If anything goes wrong during the automatic database update process when deploying the server, you can use the following command to restore the data:

+

pg_restore --clean -U postgres -Fc -d sormas_db sormas_db_....dump

+

Default Logins

+

These are the default users for most user roles, intended to be used on development or demo systems. In all cases except the admin user, the username and password are identical. Make sure to deactivate them or change the passwords on productive systems.

+

Admin

+

Username: admin +Password: sadmin

+

Web users

+

Surveillance Supervisor: SurvSup +Case Supervisor: CaseSup +Contact Supervisor: ContSup +Point of Entry Supervisor: PoeSup +Laboratory Officer: LabOff +Event Officer: EveOff +National User: NatUser +National Clinician: NatClin

+

Mobile app users

+

Surveillance Officer: SurvOff +Hospital Informant: HospInf +Point of Entry Informant: PoeInf

+

Updating Keycloak

+

Standalone installation

+

Follow the official Keycloak upgrade guide.

+

To update follow this steps:

+
    +
  1. Prerequisites
  2. +
  3. Backup the DB
  4. +
  5. Backup the current Keycloak configuration
  6. +
  7. Download the 18.0.1 zip from https://www.keycloak.org/downloads
  8. +
  9. +

    Extract everything from the archive somewhere on your disk (will call this KEYCLOAK_HOME_NEW)

    +
  10. +
  11. +

    From you current installation (will call this KEYCLOAK_HOME_OLD) directory copy the following into the new installation

    +
  12. +
  13. Copy directory KEYCLOAK_HOME_OLD/themes/sormas over to KEYCLOAK_HOME_NEW/themes
  14. +
  15. +

    Copy KEYCLOAK_HOME_OLD/providers/sormas-keycloak-service-provider-*.jar over to KEYCLOAK_HOME_16/providers

    +
  16. +
  17. +

    Setup Keycloak to use the Database

    +
  18. +
  19. Start Keycloak
  20. +
  21. Database will be migrated automatically
  22. +
+

Docker installation

+

The docker installation is automatically upgraded to the latest version specified in the Dockerfile.

+

Prerequisites: Make sure the DB is backed up, because once the upgrade is done the new DB won't be usable with the old version of Keycloak.

+

For more info see the Keycloak Docker Documentation.

+

How to migrate to new Payara Server

+

Step 1: Shutdown and backup existing domain

+
# Stop domain
+service payara-sormas stop
+
+# Move (backup) existing domain
+DOMAIN_PATH=/opt/domains
+DOMAIN_NAME="sormas"
+DOMAIN_BACKUP_NAME="sormas_backup"
+mv $DOMAIN_PATH/$DOMAIN_NAME $DOMAIN_PATH/$DOMAIN_BACKUP_NAME
+
+

Step 2: Setup Payara domain

+

Please follow the server setup: Create the payara domain under the same path as before, use the same directory paths and the same database settings.

+

Step 3: Apply your config file changes

+

Transfer your settings from sormas.properties, logback.xml or changes in the domain setup. Use the new provided files and copy your changes in, don't reuse old files!

+

Step 4: Install new SORMAS version

+

To install the new SORMAS version in the Payara domain, proceed with the automatic update or for developers: Deploy SORMAS via the IDE as usual.

+

Alternative for development systems

+

For minor updates of the payara version, you will most often be able to keep the existing domain and only replace the payara server.

+
    +
  1. Download the needed version of Payara server.
  2. +
  3. Undeploy all sormas modules from the payara domain and stop the domain.
  4. +
  5. Replace your payara server in /opt/payara5 (default path) with the downloaded one. Remove the default domain in opt/payara5/glassfish/domains as it is not needed.
  6. +
  7. Replace the application server in your IDE with the new server. See IDE setup guide
  8. +
  9. If you are facing any problems, restart your IDE and clean all generated files from the sormas domain.
  10. +
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/SOP_DISEASES/index.html b/SOP_DISEASES/index.html new file mode 100644 index 000000000000..266c95c14fb0 --- /dev/null +++ b/SOP_DISEASES/index.html @@ -0,0 +1,876 @@ + + + + + + + + + + + + + + + + + + + + + + Diseases - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

SOP for Adding New Diseases to SORMAS

+

This file defines the SOP (Standard Operating Procedure) that should be followed when requesting new diseases to be added to the system by the core development team. Answering all the questions asked in this guide will make sure that we will be able to integrate new diseases into SORMAS as quickly as possible.

+

Content

+ +

Guide

+

Step 1: Download the Data Dictionary

+

Download the latest Data Dictionary from this repository and open it. Please never use a version of the Data Dictionary that you downloaded earlier as it is very likely that its contents have changed in the meantime.

+

You will use the Data Dictionary to define all the details of the new disease. Please make sure to mark every addition or change (e.g. by colorizing the text or background of the row in a subtle red) so we don't miss any of the information you have provided.

+

Step 2: Define Basic Disease Details

+

Open the Case tab of the Data Dictionary and scroll down to the tables that have a blue background. These tables define enumerations, which are basically data types with fixed values. +Examples include the different case classifications, the gender of a person or the diseases that are used in SORMAS. Find the Disease enumeration table (refer to the Type column) and add a new row to it. Enter the following details:

+
    +
  • The name of the disease in the Caption column
  • +
  • Optionally, if the disease has a long name, a short name or abbreviation in the Short column
  • +
+

Use the Description column to answer the following question(s):

+
    +
  • Does the disease have contact follow-up?
  • +
  • If yes, for how many days should contact follow-up be done?
  • +
+

Step 3: Define Existing Case Fields

+

Look through the rows in the first table of the Case tab (which has a grey background). This table defines all the fields that are displayed in the Case Information tab in the SORMAS application. +The Caption column defines the name of the field as it is displayed in the user interface, while the Diseases column specifies which diseases use this field. Please add the name (or, if available, short name) of your new disease to the "New disease" column of every row that represents a field that is relevant for it and colorize it.

+

Step 4: Define Existing Person Fields

+

Open the Person tab and repeat step 3 for the first table containing the fields that define the details of a person in SORMAS.

+

Step 5: Define the Relevant Symptoms

+

Open the Symptoms tab which lists all the symptoms that are currently used in SORMAS. This is a very long list and you will have to go through every single row and define whether this symptom should be tracked for your new disease or not.

+

It's possible that your new disease uses one or more symptoms that are currently not part of SORMAS. In that case, you need to add a new row for each of these symptoms to the bottom of the table and provide the name of the symptom in the Caption column.

+

Most symptoms in SORMAS are simple Yes/No/Unknown fields where Yes means that the symptom is present, No that the symptom is not present and Unknown that there is no information about whether the symptom is present or not. If your symptom can simply be defined by this pattern, you don't have to specify anything else. +However, if your symptom is more complex (e.g. there are a number of predefined values that the user should choose from), please provide all the necessary details about how the symptom should be specified by users in the Description column.

+

Step 6: Define the Relevant Epidemiological Data

+

Open the Epidemiological data tab which lists all fields that are used to collect information about the epidemiological background of the case, e.g. whether they visited burials, had contact with a confirmed case or animals. Repeat step 3 for all rows in the first table, and add new rows if your new disease requires information that is not currently collected within SORMAS. +As new fields in this tab are likely to be more complex than basic symptoms, make sure to define as much information about how they should function in the Description column.

+

Step 7: Define Health Conditions

+

Open the Health conditions tab which contains a list of pre-existing conditions that are not symptoms of the disease, but are still relevant especially for case management purposes in a hospital. Repeat step 3 for all rows in the first table, and add new rows if there are health conditions relevant for your new disease that are not part of SORMAS yet. +As always with new fields, make sure to provide all relevant details in the Description column.

+

Step 8: Define New Fields in Other Areas

+

It is possible that your disease requires further information to be collected that is not supported by SORMAS yet, e.g. new details about the person, specific information about its hospitalization, or even very important fields that should directly go into the case information. +You can use the same process you used to define new symptoms, health conditions or epidemiological data fields by opening the tab in question and adding new rows to the topmost table.

+
+

At this point, you have finished all the necessary definitions in the Data Dictionary. Save your work and prepare an email with the Data Dictionary file attached to it. Don't send this email before working through the remaining steps though, as there are still a few details that are needed in order to finish the specification of your new disease.

+
+

Step 9: Provide Case Classification Criteria

+

Optimally, when defining a new disease, you should also specify the criteria SORMAS should use to automatically classify the case as suspect, probable or confirmed. In order to do this in a way that is compatible with the system we use, you will need access to a running SORMAS system (e.g. the play server you can find at https://sormas.org). +Log in as any user (e.g. the default user on the play server), open the About section from the main menu, and open the Case Classification Rules (HTML) document. Please define the classification criteria in a way that is similar to the system used in this document. If available, you can also send us an official document by WHO or your national CDC that specifies the classification criteria.

+

Step 10: Provide Additional Information

+

If there are still things that are necessary in order to properly implement the new disease in SORMAS (you might require us to create a whole new area for cases or there might be very complex mechanics that need a lot more specification), please give us as many details about them as possible. Just put all this information into your email.

+

Step 11: Send Your Disease Definition to the SORMAS Team

+

Send your email containing the updated Data Dictionary file, the case classification criteria and your additional notes to sormas@helmholtz-hzi.de. Congratulations, your work is done! We should now have all the information we need in order to integrate your disease into SORMAS. +If there is anything that is unclear or if we need additional details, we will get in touch with you as soon as possible. Thank you so much for contributing to SORMAS and helping us to fight the spread of as many diseases as possible!

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/SORMAS2SORMAS/index.html b/SORMAS2SORMAS/index.html new file mode 100644 index 000000000000..ea25f4621e3d --- /dev/null +++ b/SORMAS2SORMAS/index.html @@ -0,0 +1,885 @@ + + + + + + + + + + + + + + + + + + Sormas2Sormas Setup - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Sormas2Sormas Setup

+

Introduction

+

Sormas2Sormas (or S2S for short) is a feature that allows sharing of entities (e.g., cases, contacts, events, etc.) +between two or more SORMAS instances.

+

Components

+

SORMAS Instance

+

A SORMAS instance is a running instance of the SORMAS application. A SORMAS instance which is configured to participate +in S2S is obviously the main component of the Sormas2Sormas feature. It is the one that initiates the sharing process +and the one that receives the shared entities. Each instance has a unique S2S identifier which is an arbitrary string. +We do not enforce a certain scheme, but it is strongly encouraged to use a meaningful and consistent identifiers +(e.g., 2.sormas.id.sormas_a in Germany which is the dedicated SurvNet ID for the public health office).

+

Server Descriptors

+

A server descriptor is a struct which contains the information needed to connect to a SORMAS instance (see the +SormasServerDescriptor class). It consists of the S2S ID, a human-readable name used in the UI, and the hostname. +Descriptors centrally distributed via a dedicated etcd keyspace (e.g., /s2s/).

+
{
+  "id":"2.sormas.id.sormas_a",
+  "name":"sormas_a_org_name",
+  "hostName":"sormas_a:6080"
+}
+
+

Keystore

+

Each instance receives a dedicated x509 certificate and private key pair which is used to encrypt all S2S communication. +The certificate is signed by a common S2S CA. The certificate and private key are stored in a pkcs12 keystore under the +S2S ID of the instance. See here +for an example of how to generate a keystore.

+

Truststore

+

Each instance receives a truststore which contains the S2S CA certificate under the sormas2sormas.rootCaAlias. +See here for an example of +how to generate a truststore.

+

Properties

+

The following properties are required to be set in the sormas.properties file:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescription
central.oidc.urlURL of the OIDC server authenticating instances and authorizing S2S requests.
central.etcd.host=The hostname of the etcd instance providing data like server descriptors.
central.etcd.clientNameThe client name of the instance used in authentication towards etcd.
central.etcd.clientPasswordThe client password of the instance used in authentication towards etcd.
central.etcd.caPathThe full filesystem path to the CA trusted by etcd clients when creating a TLS connection to the etcd server.
central.location.syncIf set to true, all infrastructure data from the central server will be synchronized into the local SORMAS database at (re)start and on a daily basis.
sormas2sormas.pathFilesystem path to a directory where certificates and files related to SORMAS2SORMAS are stored.
sormas2sormas.id=The S2S ID of this instance. The identifier can be arbitrary, but should be meaningful.
sormas2sormas.keystoreNameName of the key store file relative to sormas2sormas.path.
sormas2sormas.keystorePassPassword of the key store file.
sormas2sormas.rootCaAliasThe alias of the trusted root CA which secures the S2S communication. This is NOT used for TLS.
sormas2sormas.truststoreNameName of the truststore file relative to sormas2sormas.path.
sormas2sormas.truststorePassPassword of the truststore.
sormas2sormas.oidc.realmName of our S2S authorization realm provided by the OIDC server.
sormas2sormas.oidc.clientIdThe client ID used in authentication of the instance towards the OIDC server.
sormas2sormas.oidc.clientSecretThe client secret used in authorization of the instance towards the OIDC server.
sormas2sormas.etcd.keyPrefixThe etcd key space prefix which is used to store s2s related information.
sormas2sormas.ignoreProperty.additionalDetailsControls whether the value in additionalDetails field should be sent or not to the other instances. Possible values: true/false(default).
sormas2sormas.ignoreProperty.externalIdControls whether the value in externalId field should be sent or not to the other instances. Possible values: true/false(default).
sormas2sormas.ignoreProperty.externalTokenControls whether the value in externalToken field should be sent or not to the other instances. Possible values: true/false(default).
sormas2sormas.ignoreProperty.internalTokenControls whether the value in internalToken field should be sent or not to the other instances. Possible values: true/false(default).
#sormas2sormas.districtExternalIdExternal ID of the district to which the cases/contacts to be assigned when accepting a share request
+

Keycloak

+

Keycloak is an open source identity and access management solution. It is used to manage the users and their permissions. +In our case, we use the OIDC client credentials flow +to authenticate the S2S enabled instances and to authorize the S2S share operations. It consists of a dedicated S2S +realm which contains all the necessary configurations, namely dedicated clients and client scopes. Connections to Keycloak +must be protected by TLS.

+

Clients

+

Each S2S instance receives a dedicated client in the S2S realm. An S2S instance is acting as client and is configured to +use the OIDC client credentials.

+

Client Scopes

+

For each client, a similar client scope is created. Each S2S instance A which should be able to send data to another +instance B must be assigned the client scope of instance B. This way, instance A is allowed to send data to instance B. +The client scope must be prefixed with s2s- and must contain the S2S ID of the instance it is assigned to (e.g., +s2s-2.sormas.id.sormas_b).

+

etcd

+

Server Descriptors stored in etcd

+

etcd is a distributed key-value store. It is used to store the server descriptors of all S2S enabled instances. They are +all stored under a dedicated S2S key space prefix (e.g., /s2s/) at a path like /s2s/2.sormas.id.sormas_b. +The etcd server is expected to be secured by TLS with a trusted CA announced to instances via central.etcd.caPath. +The etcd client name and password are used to authenticate the S2S instances towards the etcd server.

+

Infrastructure Data

+

etcd is also used to store the infrastructure data of the central server. This data is used to synchronize the local DB +to have a consistent view of the infrastructure data. The infrastructure data is stored under a dedicated key space of +the form /central/location/${entity} where entity is one of continent, subcontinent, country, region, district, +and community. A complete key could look like /central/location/continent/3fab2b69-31cd-5765-91e3-d68826a6dd48 where +the uuid is the uuid assigned to the continent in the central server. The values are expected to mirror the JSON values +of the DTOs (e.g., ContinentDto, etc.).

+

Secure Communication

+

The S2S communication is expected to be secured by TLS. For additional security, the S2S communication is also +encrypted. Each instance receives a dedicated keystore and truststore. The keystore contains the instance's private and +public key. The truststore contains a root CA certificate (i.e., sormas2sormas.rootCaAlias) which is used to sign all +instance certificates. The connection to Keycloak must be secured by TLS. The connection to etcd must be secured by +TLS. The CA trusted by etcd clients when creating a TLS connection to the server is set via central.etcd.caPath.

+

Deactivation

+
    +
  1. Stop the instance and create backup
  2. +
  3. The instance on which S2S will be deactivated has to revoke all outgoing pending requests AND has to accept or reject +all incoming pending request. Please note that as of now, users won't be able to see the whole sharebox or share directory. +Therefore, they cannot track if the case/contact was shared with/by another instance or not.
  4. +
  5. Deactivate the S2S feature by commenting all relevant properties in sormas.properties:
  6. +
  7. sormas2sormas.*
  8. +
  9. central.oidc.url
  10. +
  11. Optional if GA wants to disable central infra sync: central.*. Strongly discouraged.
  12. +
  13. Remove service descriptor of the deactivated instance from central etcd to prevent further discovery.
  14. +
  15. Reset feature configuration table.
  16. +
  17. Remove client and client scope of the instance from central Keycloak.
  18. +
  19. shred the instance's key material.
  20. +
  21. Restart the instance.
  22. +
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/TROUBLESHOOTING/index.html b/TROUBLESHOOTING/index.html new file mode 100644 index 000000000000..3d1f7c2a4eb9 --- /dev/null +++ b/TROUBLESHOOTING/index.html @@ -0,0 +1,863 @@ + + + + + + + + + + + + + + + + + + Troubleshooting - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

Troubleshooting

+

Please consult this collection of solutions to common problems if you have any issues before issuing a support request or asking developers for help. Also note that this resource has only been added recently and will be extended in the future. If you have encountered (and fixed) any issue that you think would be worth adding to this list, please don't hesitate to let us know!

+ +

Android Application FAQ

+

Q: I don't see a logout option anywhere in the mobile app. How can I change my user?
+A: The logout option is hidden by default because users in the field often don't know their own passwords, but their devices are instead set up by a supervisor. If you want to change your user, go to the Settings screen and tap the version number five times to bring up additional options, including the logout option.

+

Q: The app crashes. How can I get a log file?
+A: If you are using a release version of the app and need to get error logs, you can do the following:

+
    +
  1. Enable developer options in the Android device's settings
  2. +
  3. Use the "Take Bug Report" option. The full report is not needed.
  4. +
  5. The zip file that is created will have a dumpstate-.txt file that contains the log and some more information
  6. +
  7. Open it and search for de.symeda.sormas to identify the process ID. E.g. de.symeda.sormas.app/de.symeda.sormas.app.login.LoginActivity$_11109#0 -> 11109 is the ID
  8. +
  9. Search for all occurences of the process ID to filter the file down to lines that contain the actual log of sormas
  10. +
+

Identify Performance Problems

+

There are two main sources of bad performance in the application:

+
    +
  1. Slow database queries
  2. +
  3. Slow Java code
  4. +
+

Log Slow Database Queries

+

Possibly the most generally useful log setting for troubleshooting performance, especially on a production server.

+

SORMAS-Docker already logs slow SQL queries by default. You can view the log output on its host VM with docker logs sormas-docker_postgres_1.

+

You can enable the logging of slow SQL queries in your PostgreSQL server in postgresql.conf:

+
    +
  1. Change the value of log_min_duration_statement to a value that fits your need (e.g. 10000).
  2. +
  3. Restart the PostgreSQL service or reload the config.
  4. +
  5. Monitor the log file.
  6. +
+

Create Analysis of Slow Database Query (SORMAS-Docker)

+

You can provide an analysis of a slow running query to help the developers to see where the query is getting slow and how to fix it.

+
    +
  1. +

    Extract the slow SQL statement from slow query log.

    +
  2. +
  3. +

    Copy the SQL statement, replace all parameters ($x) with the values (see the following log statement) and place the SQL query on the system (outside Docker container on host):

    +
  4. +
+
sudo bash
+cd /var/lib/docker/psqldata
+vi explain.sql
+
+# hit i (INSERT)
+# Paste this into the file:  EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON) 
+# Paste the complete SQL statement
+# Hit ESC and :wq to save the file
+
+
    +
  1. Execute the SQL (inside Docker container):
  2. +
+
sudo bash
+docker exec -ti sormas-docker_postgres_1 bash
+cd /var/lib/postgresql/data
+su postgres
+psql -XqAt -d sormas -f explain.sql > analyze.json
+
+
    +
  1. +

    Copy the output to your home dir on the VM (not inside the Docker container) to be able to copy it from the VM to your local system: mv analyze.json /home/user.name/

    +
  2. +
  3. +

    Create a visual report at https://explain.dalibo.com/ in order to share the analysis. Make sure the raw query and timings are included.

    +
  4. +
+

How to analyze this is explained below.

+

Log Slow Java Code

+

If a specific view or action is facing bad performance it may be helpful to log the execution time of the responsible Java code.

+
    +
  1. Open the logback file located in your domain (default path: /opt/domains/sormas/config/logback.xml) and change the log level of PerformanceLoggingInterceptor to DEBUG or TRACE. The config change will be recognized during runtime within 30s. After that you will see detailed log entries in the SORMAS log.
  2. +
  3. Reproduce the scenario you want to investigate on the server instance.
  4. +
  5. Set the log level back to its default once the logging has been done since it can reduce the overall performance of SORMAS.
  6. +
+

Caution: Do not expose any private data! Whenever you debug problems on an instance with productive data, please make sure that the logged information does not contain any personal data like real person names, birth dates, etc. to the public. Never provide such data anywhere on GitHub or any other online tool!

+

How to analyze the log is explained below.

+

Analyze Performance Problems

+

Analyze a Slow Query

+

To get your hands on a specific query executed by a view or action, you can do the following:

+
    +
  1. Get an analysis from a production system
  2. +
  3. Create your own:
  4. +
  5. Identify or create a unit test that calls the related backend method
  6. +
  7. Set the 'hibernate.show_sql' config in the persistence.xml of the test resources to true
  8. +
  9. Run the unit test and extract the query from the log
  10. +
  11. Replace any parameters in the query
  12. +
  13. Execute the query on your local database or a test system pre-faced by 'EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON)'
  14. +
  15. Create a visual report at https://explain.dalibo.com/ in order to share the analysis. Make sure the raw query and timings are included.
  16. +
+

Important points when interpreting the visual report:

+
    +
  • Look for the red highlights in the report, especially for nodes that have a long runtime
  • +
  • Look for nodes that are processing a lot of rows (100+ millions). This is often the case for joins
  • +
  • When you are dealing with a lof of data, most often you only need to output a subset
  • +
+

How to improve the query is most often a process of trial and error. Here are some things to consider:

+
    +
  • Adding indices so postgres does not have to go through all data. You can use a btree index to include multiple columns and sorting
  • +
  • Getting rid of unnecessary joins. Example: Joining a region of a case to compare it to the region of the user, instead of directly doing this on the region_id field of the case
  • +
  • Using limit to reduce the output (e.g. the first 100). Make sure there is no sorting done close to the end of the query graph. This is often the case when 'DISTINCT' is used. +See Example
  • +
  • Splitting the query into separate queries when 'DISTINCT' has to be used / using distinct on a sub-query
  • +
  • Using sub-queries to influence the query planner. +See Example
  • +
+

How to save time when optimizing the query:

+
    +
  • Make sure you have easy access to a database that allows you to reproduce the bad performance of the query and to manipulate the query and re-run it directly on the database.
  • +
  • Use the explain feature of pgAdmin to quickly output the query graph for debugging purposes
  • +
  • Make sure you have a unit test (see above) that allows you to create the SQL query from a criteria query without having to re-deploy your server
  • +
+

Analyze Java Code Performance Logs

+

After Logging Slow Java Code the debug log file (default path: /opt/domains/sormas/logs/application.debug) can be analyzed in detail using the PerformanceLogAnalysisGenerator.

+

The log file's path is specified as the program argument when calling PerformanceLogAnalysisGenerator's main method. Processing the log file will +generate three files (<logfileName>.csv, <logfileName>.txt, <logfileName>.html) to further investigate method runtimes.

+

<logfileName>.html provides a navigable overview of methods along with runtime statistics (total, min, max and average time) and calls to sub methods.

+

Sometimes it is convenient to analyze a number of different scenarios in a row. To do so, produce snippets of the application.debug log using tail for each +of the scenarios to be investigated:

+
    +
  1. start tail -f <logfileName> > <snippetDirectory>/<snippet.debug>
  2. +
  3. replay the steps to be analyzed
  4. +
  5. stop tail -f
  6. +
+

The PerformanceLogAnalysisGenerator can now batch process all of the snippets by pointing to the directory instead of a log file. +Calling PerformanceLogAnalysisGenerator.main with argument <snippetDirectory> generates the analysis files (.csv, .txt, .html) +for each file *.debug in this directory. The generated files will be placed in <snippetDirectory>, too.

+

IDE Troubleshooting: Android Studio

+

If for some reason the Android App is not building correctly (for example due to unexpected ClassNotFoundExceptions), here is what you should try: +- Clean the Project (Build -> Clean Project) +- Invalidate Caches (File -> Invalidate Caches / Restart...) +- Wipe your Android VM (AVD Manager -> Wipe Data)

+

If you get this exception: Unable to load class 'javax.xml.bind.JAXBException', the reason is most likely a faulty JDK version. For the androidapp, you need Java JDK 8. To change the JDK, go to File -> Project Structure -> JDK Location and select a valid JDK (on Linux, check the folder /usr/lib/jvm and/or install if necessary: sudo apt install openjdk-8-jdk)

+

IDE Troubleshooting: eclipse

+

Deployment Problems

+

Unfortunately, when using eclipse together with the Payara Tools, there are a number of deployment problems that you might run into. Examples of these include:

+
    +
  • ClassDefNotFoundExceptions after deploying the artifacts and logging in to the web app
  • +
  • Error messages in eclipse telling you that the deployment failed
  • +
+

There are a couple of things you can do to fix these problems:

+
    +
  • Do a Maven update for all projects
  • +
  • Stop and restart the server
  • +
  • Re-deploy the server artifacts
  • +
+

If the problem occurred right after you've pulled new code from GitHub, your safest bet is probably to start with the Maven update. For most other problems, a simple re-deployment or, if necessary, server restart should suffice.

+

News Feeds Polling

+

When running eclipse with JDK 11, you might encounter the following error message: An internal error occurred during: "Polling news feeds". javax/xml/bind/JAXBContext. To fix it, disable Window --> Preferences --> General --> News --> "Enable automatic news polling".

+

Redeployment problems

+

If you face problems that sormas-ui or sormas-rest cannot call the backend anymore after redeploying, please follow this instruction.

+

Malware detection triggers

+

It might happen that a defensive program on your system falsely recognizes files needed to run SORMAS as vulnerability.

+

Please ignore the following known findings (no quarantine, no deletion): +* File: payara-5.2021.10.zip, Recognized: Trojan:Script/Oneeva.A!ml (found by Windows Defender). Has rarely happened when running server-setup.sh which downloads that file. The script subsequently fails because zip file cannot be extracted. +* File: glassfish/modules/war-util.jar, Recognized: Exploit:Java/CVE-2012-0507.D!ldr (found by Windows Defender in payara-5.2021.10). The deployed OSGi bundle might also be recognized, for example under this path: osgi-cache/felix/bundle365/version0.0/bundle.jar . If the file is quarantined, the paraya domain fails to start, without any exception in the log.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf13b9f9d978896599290a74f77d5dbe7d1655c GIT binary patch literal 1870 zcmV-U2eJ5xP)Gc)JR9QMau)O=X#!i9;T z37kk-upj^(fsR36MHs_+1RCI)NNu9}lD0S{B^g8PN?Ww(5|~L#Ng*g{WsqleV}|#l zz8@ri&cTzw_h33bHI+12+kK6WN$h#n5cD8OQt`5kw6p~9H3()bUQ8OS4Q4HTQ=1Ol z_JAocz`fLbT2^{`8n~UAo=#AUOf=SOq4pYkt;XbC&f#7lb$*7=$na!mWCQ`dBQsO0 zLFBSPj*N?#u5&pf2t4XjEGH|=pPQ8xh7tpx;US5Cx_Ju;!O`ya-yF`)b%TEt5>eP1ZX~}sjjA%FJF?h7cX8=b!DZl<6%Cv z*G0uvvU+vmnpLZ2paivG-(cd*y3$hCIcsZcYOGh{$&)A6*XX&kXZd3G8m)G$Zz-LV z^GF3VAW^Mdv!)4OM8EgqRiz~*Cji;uzl2uC9^=8I84vNp;ltJ|q-*uQwGp2ma6cY7 z;`%`!9UXO@fr&Ebapfs34OmS9^u6$)bJxrucutf>`dKPKT%%*d3XlFVKunp9 zasduxjrjs>f8V=D|J=XNZp;_Zy^WgQ$9WDjgY=z@stwiEBm9u5*|34&1Na8BMjjgf3+SHcr`5~>oz1Y?SW^=K z^bTyO6>Gar#P_W2gEMwq)ot3; zREHn~U&Dp0l6YT0&k-wLwYjb?5zGK`W6S2v+K>AM(95m2C20L|3m~rN8dprPr@t)5lsk9Hu*W z?pS990s;Ez=+Rj{x7p``4>+c0G5^pYnB1^!TL=(?HLHZ+HicG{~4F1d^5Awl_2!1jICM-!9eoLhbbT^;yHcefyTAaqRcY zmuctDopPT!%k+}x%lZRKnzykr2}}XfG_ne?nRQO~?%hkzo;@RN{P6o`&mMUWBYMTe z6i8ChtjX&gXl`nvrU>jah)2iNM%JdjqoaeaU%yVn!^70x-flljp6Q5tK}5}&X8&&G zX3fpb3E(!rH=zVI_9Gjl45w@{(ITqngWFe7@9{mX;tO25Z_8 zQHEpI+FkTU#4xu>RkN>b3Tnc3UpWzPXWm#o55GKF09j^Mh~)K7{QqbO_~(@CVq! zS<8954|P8mXN2MRs86xZ&Q4EfM@JB94b=(YGuk)s&^jiSF=t3*oNK3`rD{H`yQ?d; ztE=laAUoZx5?RC8*WKOj`%LXEkgDd>&^Q4M^z`%u0rg-It=hLCVsq!Z%^6eB-OvOT zFZ28TN&cRmgU}Elrnk43)!>Z1FCPL2K$7}gwzIc48NX}#!A1BpJP?#v5wkNprhV** z?Cpalt1oH&{r!o3eSKc&ap)iz2BTn_VV`4>9M^b3;(YY}4>#ML6{~(4mH+?%07*qo IM6N<$f(jP3KmY&$ literal 0 HcmV?d00001 diff --git a/assets/javascripts/bundle.51d95adb.min.js b/assets/javascripts/bundle.51d95adb.min.js new file mode 100644 index 000000000000..b20ec6835bb7 --- /dev/null +++ b/assets/javascripts/bundle.51d95adb.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Hi=Object.create;var xr=Object.defineProperty;var Pi=Object.getOwnPropertyDescriptor;var $i=Object.getOwnPropertyNames,kt=Object.getOwnPropertySymbols,Ii=Object.getPrototypeOf,Er=Object.prototype.hasOwnProperty,an=Object.prototype.propertyIsEnumerable;var on=(e,t,r)=>t in e?xr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Er.call(t,r)&&on(e,r,t[r]);if(kt)for(var r of kt(t))an.call(t,r)&&on(e,r,t[r]);return e};var sn=(e,t)=>{var r={};for(var n in e)Er.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&kt)for(var n of kt(e))t.indexOf(n)<0&&an.call(e,n)&&(r[n]=e[n]);return r};var Ht=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Fi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of $i(t))!Er.call(e,o)&&o!==r&&xr(e,o,{get:()=>t[o],enumerable:!(n=Pi(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Hi(Ii(e)):{},Fi(t||!e||!e.__esModule?xr(r,"default",{value:e,enumerable:!0}):r,e));var fn=Ht((wr,cn)=>{(function(e,t){typeof wr=="object"&&typeof cn!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(wr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(T){return!!(T&&T!==document&&T.nodeName!=="HTML"&&T.nodeName!=="BODY"&&"classList"in T&&"contains"in T.classList)}function f(T){var Ke=T.type,We=T.tagName;return!!(We==="INPUT"&&a[Ke]&&!T.readOnly||We==="TEXTAREA"&&!T.readOnly||T.isContentEditable)}function c(T){T.classList.contains("focus-visible")||(T.classList.add("focus-visible"),T.setAttribute("data-focus-visible-added",""))}function u(T){T.hasAttribute("data-focus-visible-added")&&(T.classList.remove("focus-visible"),T.removeAttribute("data-focus-visible-added"))}function p(T){T.metaKey||T.altKey||T.ctrlKey||(s(r.activeElement)&&c(r.activeElement),n=!0)}function m(T){n=!1}function d(T){s(T.target)&&(n||f(T.target))&&c(T.target)}function h(T){s(T.target)&&(T.target.classList.contains("focus-visible")||T.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(T.target))}function v(T){document.visibilityState==="hidden"&&(o&&(n=!0),B())}function B(){document.addEventListener("mousemove",z),document.addEventListener("mousedown",z),document.addEventListener("mouseup",z),document.addEventListener("pointermove",z),document.addEventListener("pointerdown",z),document.addEventListener("pointerup",z),document.addEventListener("touchmove",z),document.addEventListener("touchstart",z),document.addEventListener("touchend",z)}function re(){document.removeEventListener("mousemove",z),document.removeEventListener("mousedown",z),document.removeEventListener("mouseup",z),document.removeEventListener("pointermove",z),document.removeEventListener("pointerdown",z),document.removeEventListener("pointerup",z),document.removeEventListener("touchmove",z),document.removeEventListener("touchstart",z),document.removeEventListener("touchend",z)}function z(T){T.target.nodeName&&T.target.nodeName.toLowerCase()==="html"||(n=!1,re())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),B(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var un=Ht(Sr=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},a=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(re,z){d.append(z,re)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(T){throw new Error("URL unable to set base "+c+" due to "+T)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,B=!0,re=this;["append","delete","set"].forEach(function(T){var Ke=h[T];h[T]=function(){Ke.apply(h,arguments),v&&(B=!1,re.search=h.toString(),B=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var z=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==z&&(z=this.search,B&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},a=i.prototype,s=function(f){Object.defineProperty(a,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){s(f)}),Object.defineProperty(a,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(a,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr)});var Qr=Ht((Lt,Kr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Lt=="object"&&typeof Kr=="object"?Kr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Lt=="object"?Lt.ClipboardJS=r():t.ClipboardJS=r()})(Lt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return ki}});var a=i(279),s=i.n(a),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(O){return!1}}var d=function(O){var w=p()(O);return m("cut"),w},h=d;function v(j){var O=document.documentElement.getAttribute("dir")==="rtl",w=document.createElement("textarea");w.style.fontSize="12pt",w.style.border="0",w.style.padding="0",w.style.margin="0",w.style.position="absolute",w.style[O?"right":"left"]="-9999px";var k=window.pageYOffset||document.documentElement.scrollTop;return w.style.top="".concat(k,"px"),w.setAttribute("readonly",""),w.value=j,w}var B=function(O,w){var k=v(O);w.container.appendChild(k);var F=p()(k);return m("copy"),k.remove(),F},re=function(O){var w=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},k="";return typeof O=="string"?k=B(O,w):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?k=B(O.value,w):(k=p()(O),m("copy")),k},z=re;function T(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?T=function(w){return typeof w}:T=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},T(j)}var Ke=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},w=O.action,k=w===void 0?"copy":w,F=O.container,q=O.target,Le=O.text;if(k!=="copy"&&k!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&T(q)==="object"&&q.nodeType===1){if(k==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(k==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Le)return z(Le,{container:F});if(q)return k==="cut"?h(q):z(q,{container:F})},We=Ke;function Ie(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(w){return typeof w}:Ie=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},Ie(j)}function Ti(j,O){if(!(j instanceof O))throw new TypeError("Cannot call a class as a function")}function nn(j,O){for(var w=0;w0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof F.action=="function"?F.action:this.defaultAction,this.target=typeof F.target=="function"?F.target:this.defaultTarget,this.text=typeof F.text=="function"?F.text:this.defaultText,this.container=Ie(F.container)==="object"?F.container:document.body}},{key:"listenClick",value:function(F){var q=this;this.listener=c()(F,"click",function(Le){return q.onClick(Le)})}},{key:"onClick",value:function(F){var q=F.delegateTarget||F.currentTarget,Le=this.action(q)||"copy",Rt=We({action:Le,container:this.container,target:this.target(q),text:this.text(q)});this.emit(Rt?"success":"error",{action:Le,text:Rt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(F){return yr("action",F)}},{key:"defaultTarget",value:function(F){var q=yr("target",F);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(F){return yr("text",F)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(F){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return z(F,q)}},{key:"cut",value:function(F){return h(F)}},{key:"isSupported",value:function(){var F=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof F=="string"?[F]:F,Le=!!document.queryCommandSupported;return q.forEach(function(Rt){Le=Le&&!!document.queryCommandSupported(Rt)}),Le}}]),w}(s()),ki=Ri},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,f){for(;s&&s.nodeType!==o;){if(typeof s.matches=="function"&&s.matches(f))return s;s=s.parentNode}}n.exports=a},438:function(n,o,i){var a=i(828);function s(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?s.apply(null,arguments):typeof m=="function"?s.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return s(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=a(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(n,o,i){var a=i(879),s=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(h))throw new TypeError("Third argument must be a Function");if(a.node(m))return c(m,d,h);if(a.nodeList(m))return u(m,d,h);if(a.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return s(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),a=f.toString()}return a}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,a,s){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var f=this;function c(){f.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=s.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var is=/["'&<>]/;Jo.exports=as;function as(e){var t=""+e,r=is.exec(t);if(!r)return t;var n,o="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],a;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(s){a={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||s(m,d)})})}function s(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof Xe?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){s("next",m)}function u(m){s("throw",m)}function p(m,d){m(d),i.shift(),i.length&&s(i[0][0],i[0][1])}}function mn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof xe=="function"?xe(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(a){return new Promise(function(s,f){a=e[i](a),o(s,f,a.done,a.value)})}}function o(i,a,s,f){Promise.resolve(f).then(function(c){i({value:c,done:s})},a)}}function A(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var $t=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Fe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=xe(a),f=s.next();!f.done;f=s.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var u=this.initialTeardown;if(A(u))try{u()}catch(v){i=v instanceof $t?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=xe(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{dn(h)}catch(v){i=i!=null?i:[],v instanceof $t?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new $t(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)dn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Or=Fe.EMPTY;function It(e){return e instanceof Fe||e&&"closed"in e&&A(e.remove)&&A(e.add)&&A(e.unsubscribe)}function dn(e){A(e)?e():e.unsubscribe()}var Ae={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,a=o.isStopped,s=o.observers;return i||a?Or:(this.currentObservers=null,s.push(r),new Fe(function(){n.currentObservers=null,De(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,a=n.isStopped;o?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new U;return r.source=this,r},t.create=function(r,n){return new wn(r,n)},t}(U);var wn=function(e){ne(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Or},t}(E);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ne(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,a=n._infiniteTimeWindow,s=n._timestampProvider,f=n._windowTime;o||(i.push(r),!a&&i.push(s.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,a=o._buffer,s=a.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var a=r.actions;n!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Ut);var On=function(e){ne(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Wt);var we=new On(Tn);var R=new U(function(e){return e.complete()});function Dt(e){return e&&A(e.schedule)}function kr(e){return e[e.length-1]}function Qe(e){return A(kr(e))?e.pop():void 0}function Se(e){return Dt(kr(e))?e.pop():void 0}function Vt(e,t){return typeof kr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function zt(e){return A(e==null?void 0:e.then)}function Nt(e){return A(e[ft])}function qt(e){return Symbol.asyncIterator&&A(e==null?void 0:e[Symbol.asyncIterator])}function Kt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ki(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Qt=Ki();function Yt(e){return A(e==null?void 0:e[Qt])}function Gt(e){return ln(this,arguments,function(){var r,n,o,i;return Pt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,Xe(r.read())];case 3:return n=a.sent(),o=n.value,i=n.done,i?[4,Xe(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,Xe(o)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Bt(e){return A(e==null?void 0:e.getReader)}function $(e){if(e instanceof U)return e;if(e!=null){if(Nt(e))return Qi(e);if(pt(e))return Yi(e);if(zt(e))return Gi(e);if(qt(e))return _n(e);if(Yt(e))return Bi(e);if(Bt(e))return Ji(e)}throw Kt(e)}function Qi(e){return new U(function(t){var r=e[ft]();if(A(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Yi(e){return new U(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?_(function(o,i){return e(o,i,n)}):me,Oe(1),r?He(t):zn(function(){return new Xt}))}}function Nn(){for(var e=[],t=0;t=2,!0))}function fe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new E}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,f=s===void 0?!0:s;return function(c){var u,p,m,d=0,h=!1,v=!1,B=function(){p==null||p.unsubscribe(),p=void 0},re=function(){B(),u=m=void 0,h=v=!1},z=function(){var T=u;re(),T==null||T.unsubscribe()};return g(function(T,Ke){d++,!v&&!h&&B();var We=m=m!=null?m:r();Ke.add(function(){d--,d===0&&!v&&!h&&(p=jr(z,f))}),We.subscribe(Ke),!u&&d>0&&(u=new et({next:function(Ie){return We.next(Ie)},error:function(Ie){v=!0,B(),p=jr(re,o,Ie),We.error(Ie)},complete:function(){h=!0,B(),p=jr(re,a),We.complete()}}),$(T).subscribe(u))})(c)}}function jr(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function V(e,t=document){let r=se(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function se(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),N(e===_e()),Y())}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function Yn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,we),l(()=>Be(e)),N(Be(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,we),l(()=>rr(e)),N(rr(e)))}var Bn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!zr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),xa?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!zr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ya.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Jn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Zn=typeof WeakMap!="undefined"?new WeakMap:new Bn,eo=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=Ea.getInstance(),n=new Ra(t,r,this);Zn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){eo.prototype[e]=function(){var t;return(t=Zn.get(this))[e].apply(t,arguments)}});var ka=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:eo}(),to=ka;var ro=new E,Ha=I(()=>H(new to(e=>{for(let t of e)ro.next(t)}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){return Ha.pipe(S(t=>t.observe(e)),x(t=>ro.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(()=>de(e)))),N(de(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var no=new E,Pa=I(()=>H(new IntersectionObserver(e=>{for(let t of e)no.next(t)},{threshold:0}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function sr(e){return Pa.pipe(S(t=>t.observe(e)),x(t=>no.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function oo(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=de(e),o=bt(e);return r>=o.height-n.height-t}),Y())}var cr={drawer:V("[data-md-toggle=drawer]"),search:V("[data-md-toggle=search]")};function io(e){return cr[e].checked}function qe(e,t){cr[e].checked!==t&&cr[e].click()}function je(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),N(t.checked))}function $a(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ia(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(N(!1))}function ao(){let e=b(window,"keydown").pipe(_(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:io("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),_(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!$a(n,r)}return!0}),fe());return Ia().pipe(x(t=>t?R:e))}function Me(){return new URL(location.href)}function ot(e){location.href=e.href}function so(){return new E}function co(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)co(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)co(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function fo(){return location.hash.substring(1)}function uo(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Fa(){return b(window,"hashchange").pipe(l(fo),N(fo()),_(e=>e.length>0),J(1))}function po(){return Fa().pipe(l(e=>se(`[id="${e}"]`)),_(e=>typeof e!="undefined"))}function Nr(e){let t=matchMedia(e);return Zt(r=>t.addListener(()=>r(t.matches))).pipe(N(t.matches))}function lo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(N(e.matches))}function qr(e,t){return e.pipe(x(r=>r?t():R))}function ur(e,t={credentials:"same-origin"}){return ve(fetch(`${e}`,t)).pipe(ce(()=>R),x(r=>r.status!==200?Tt(()=>new Error(r.statusText)):H(r)))}function Ue(e,t){return ur(e,t).pipe(x(r=>r.json()),J(1))}function mo(e,t){let r=new DOMParser;return ur(e,t).pipe(x(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),J(1))}function pr(e){let t=M("script",{src:e});return I(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(x(()=>Tt(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),C(()=>document.head.removeChild(t)),Oe(1))))}function ho(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function bo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(ho),N(ho()))}function vo(){return{width:innerWidth,height:innerHeight}}function go(){return b(window,"resize",{passive:!0}).pipe(l(vo),N(vo()))}function yo(){return Q([bo(),go()]).pipe(l(([e,t])=>({offset:e,size:t})),J(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(X("size")),o=Q([n,r]).pipe(l(()=>Be(e)));return Q([r,t,o]).pipe(l(([{height:i},{offset:a,size:s},{x:f,y:c}])=>({offset:{x:a.x-f,y:a.y-c+i},size:s})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(a=>{let s=document.createElement("script");s.src=i,s.onload=a,document.body.appendChild(s)})),Promise.resolve())}var r=class{constructor(n){this.url=n,this.onerror=null,this.onmessage=null,this.onmessageerror=null,this.m=a=>{a.source===this.w&&(a.stopImmediatePropagation(),this.dispatchEvent(new MessageEvent("message",{data:a.data})),this.onmessage&&this.onmessage(a))},this.e=(a,s,f,c,u)=>{if(s===this.url.toString()){let p=new ErrorEvent("error",{message:a,filename:s,lineno:f,colno:c,error:u});this.dispatchEvent(p),this.onerror&&this.onerror(p)}};let o=new EventTarget;this.addEventListener=o.addEventListener.bind(o),this.removeEventListener=o.removeEventListener.bind(o),this.dispatchEvent=o.dispatchEvent.bind(o);let i=document.createElement("iframe");i.width=i.height=i.frameBorder="0",document.body.appendChild(this.iframe=i),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

+ + SORMAS - Surveillance, Outbreak Response Management and Analysis System + +
+ License + Latest Release + Development Build Status +

+


+

SORMAS

+

SORMAS (Surveillance Outbreak Response Management and Analysis System) is an open source eHealth system - consisting of separate web and mobile apps - that is geared towards optimizing the processes used in monitoring the spread of infectious diseases and responding to outbreak situations.

+

FAQ (Frequently Asked Questions)

+

How Does it Work?

+

You can give SORMAS a try on our demo server at https://demo.sormas.org!

+

How Can I Get Involved?

+

Read through our Contributing Readme and contact us at info@sormas.org to learn how you can help to drive the development of SORMAS forward, +or check out our Discussions to get development support from the core developers and other community members. +SORMAS is a community-driven project, and we'd love to have you on board!

+

If you want to contribute to the code, please strictly adhere to the Development Environment guide to ensure that everything is set up correctly. +Please also make sure that you've read the Development Contributing Guidelines before you start to develop.

+

How Can I Report a Bug or Request a Feature?

+

If you want to report a security issue, please read and follow our Security Policies. For bugs without security implications, change and feature requests, please create a new issue and +read the Submitting an Issue guide for more detailed instructions. We appreciate your help!

+

Which Browsers and Android Versions are Supported?

+

SORMAS officially supports and is tested on Chromium-based browsers (like Google Chrome) and Mozilla Firefox, and all Android versions starting from Android 7.0 (Nougat). In principle, SORMAS should be usable with all web browsers that are supported by Vaadin 8 (Chrome, Firefox, Safari, Edge, Internet Explorer 11; see https://vaadin.com/faq).

+

Making use of the SORMAS web application through a mobile device web browser is possible and acceptable also in countries that are subject to the General Data Protection Regulation (GDPR) as enforced by the European Union. However, in such countries that are subject to the GDPR, the Android application (.apk file) for SORMAS should not be used on mobile devices until further notice.

+

Is there a ReST API documentation?

+

Yes! Please download the latest release and copy the content of /deploy/openapi/sormas-rest.yaml to an editor that generates a visual API documentation(e.g. https://editor.swagger.io/). +A runtime Swagger documentation of the External Visits Resource (used by external symptom journals such as CLIMEDO or PIA) is available at <<host>>/sormas-rest/openapi.json or <<host>>/sormas-rest/openapi.yaml

+

Who is responsible for Data Protection and Data Security?

+

We herewith explicitly would like to draw your attention to the fact, that the respective public health agency running SORMAS is in charge of data security and data protection and has to ensure compliance with national data protection and data security regulations in their respective jurisdiction. +It has to ensure that state-of-the art requirements for data protection and data security are fulfilled. All those prerequisites and examinations have to be done in the context of the country and its respective legal framework. +For these reasons, HZI cannot take the responsibility from the respective public health agency running the SORMAS systems and is not liable for any violation of data protection of the agency as the data generated by SORMAS belong to that very agency.

+

+ +

Guidelines and Resources

+

If you want to learn more about the development and contribution process, setting up or customizing your own system, or technical details, please consider the following guides and resources available in this repository. You can also view this readme and all guides outside the Wiki with a full table of content and search functionality here: https://sormas-foundation.github.io/SORMAS-Project/

+
    +
  • GitHub Wiki - Our wiki contains additional guides for server customization and development instructions. Please have a look at it if you need information on anything that this readme does not contain.
  • +
  • Contributing Guidelines - These are mandatory literature if you want to contribute to this repository in any way (e.g. by submitting issues, developing code, or translating SORMAS into new languages).
  • +
  • Development Environment Setup Instructions - If you want to get involved with development, this guide tells you how to correctly set up your system in order to contribute to the code in adherence with codestyle guidelines, development practices, etc.
  • +
  • Troubleshooting - A collection of solutions to common (mostly development) problems. Please consult this readme when encountering issues before issuing a support request.
  • +
  • Server Customization - If you are maintaining a SORMAS server or are a developer, this guide explains core concepts such as turning features on or off, importing infrastructure data or adjusting the configuration file.
  • +
  • Internationalization - SORMAS can be translated in any language by using the open source tool Crowdin; this resource explains how this process is working.
  • +
  • Disease Definition Instructions - We already support a large number of diseases, but not all of them are fully configured for case-based surveillance, and some might not be part of SORMAS at all yet; if you need SORMAS to support a specific disease, please use these instructions to give us all the information we need in order to extend the software with your requested disease.
  • +
  • +

    Sormas2Sormas - The Sormas2Sormas API is used to share entities between SORMAS instances.

    +
  • +
  • +

    Security Policies - These contain important information about how to report security problems and the processes we are using to take care of them.

    +
  • +
  • Third-Party License Acknowledgement - This resource contains the names and license copies of external resources that SORMAS is using.
  • +
+

If you want to set up a SORMAS instance for production, testing or development purposes, please refer to the following guides: +* Installing a SORMAS Server +* Updating a SORMAS Server +* Setup Development environment +* Creating a Demo Android App

+

Project Structure

+

The project consists of the following modules:

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 000000000000..2c33f4a11d52 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#sormas","title":"SORMAS","text":"

SORMAS (Surveillance Outbreak Response Management and Analysis System) is an open source eHealth system - consisting of separate web and mobile apps - that is geared towards optimizing the processes used in monitoring the spread of infectious diseases and responding to outbreak situations.

"},{"location":"#faq-frequently-asked-questions","title":"FAQ (Frequently Asked Questions)","text":""},{"location":"#how-does-it-work","title":"How Does it Work?","text":"

You can give SORMAS a try on our demo server at https://demo.sormas.org!

"},{"location":"#how-can-i-get-involved","title":"How Can I Get Involved?","text":"

Read through our Contributing Readme and contact us at info@sormas.org to learn how you can help to drive the development of SORMAS forward, or check out our Discussions to get development support from the core developers and other community members. SORMAS is a community-driven project, and we'd love to have you on board!

If you want to contribute to the code, please strictly adhere to the Development Environment guide to ensure that everything is set up correctly. Please also make sure that you've read the Development Contributing Guidelines before you start to develop.

"},{"location":"#how-can-i-report-a-bug-or-request-a-feature","title":"How Can I Report a Bug or Request a Feature?","text":"

If you want to report a security issue, please read and follow our Security Policies. For bugs without security implications, change and feature requests, please create a new issue and read the Submitting an Issue guide for more detailed instructions. We appreciate your help!

"},{"location":"#which-browsers-and-android-versions-are-supported","title":"Which Browsers and Android Versions are Supported?","text":"

SORMAS officially supports and is tested on Chromium-based browsers (like Google Chrome) and Mozilla Firefox, and all Android versions starting from Android 7.0 (Nougat). In principle, SORMAS should be usable with all web browsers that are supported by Vaadin 8 (Chrome, Firefox, Safari, Edge, Internet Explorer 11; see https://vaadin.com/faq).

Making use of the SORMAS web application through a mobile device web browser is possible and acceptable also in countries that are subject to the General Data Protection Regulation (GDPR) as enforced by the European Union. However, in such countries that are subject to the GDPR, the Android application (.apk file) for SORMAS should not be used on mobile devices until further notice.

"},{"location":"#is-there-a-rest-api-documentation","title":"Is there a ReST API documentation?","text":"

Yes! Please download the latest release and copy the content of /deploy/openapi/sormas-rest.yaml to an editor that generates a visual API documentation(e.g. https://editor.swagger.io/). A runtime Swagger documentation of the External Visits Resource (used by external symptom journals such as CLIMEDO or PIA) is available at <<host>>/sormas-rest/openapi.json or <<host>>/sormas-rest/openapi.yaml

"},{"location":"#who-is-responsible-for-data-protection-and-data-security","title":"Who is responsible for Data Protection and Data Security?","text":"

We herewith explicitly would like to draw your attention to the fact, that the respective public health agency running SORMAS is in charge of data security and data protection and has to ensure compliance with national data protection and data security regulations in their respective jurisdiction. It has to ensure that state-of-the art requirements for data protection and data security are fulfilled. All those prerequisites and examinations have to be done in the context of the country and its respective legal framework. For these reasons, HZI cannot take the responsibility from the respective public health agency running the SORMAS systems and is not liable for any violation of data protection of the agency as the data generated by SORMAS belong to that very agency.

"},{"location":"#guidelines-and-resources","title":"Guidelines and Resources","text":"

If you want to learn more about the development and contribution process, setting up or customizing your own system, or technical details, please consider the following guides and resources available in this repository. You can also view this readme and all guides outside the Wiki with a full table of content and search functionality here: https://sormas-foundation.github.io/SORMAS-Project/

  • GitHub Wiki - Our wiki contains additional guides for server customization and development instructions. Please have a look at it if you need information on anything that this readme does not contain.
  • Contributing Guidelines - These are mandatory literature if you want to contribute to this repository in any way (e.g. by submitting issues, developing code, or translating SORMAS into new languages).
  • Development Environment Setup Instructions - If you want to get involved with development, this guide tells you how to correctly set up your system in order to contribute to the code in adherence with codestyle guidelines, development practices, etc.
  • Troubleshooting - A collection of solutions to common (mostly development) problems. Please consult this readme when encountering issues before issuing a support request.
  • Server Customization - If you are maintaining a SORMAS server or are a developer, this guide explains core concepts such as turning features on or off, importing infrastructure data or adjusting the configuration file.
  • Internationalization - SORMAS can be translated in any language by using the open source tool Crowdin; this resource explains how this process is working.
  • Disease Definition Instructions - We already support a large number of diseases, but not all of them are fully configured for case-based surveillance, and some might not be part of SORMAS at all yet; if you need SORMAS to support a specific disease, please use these instructions to give us all the information we need in order to extend the software with your requested disease.
  • Sormas2Sormas - The Sormas2Sormas API is used to share entities between SORMAS instances.

  • Security Policies - These contain important information about how to report security problems and the processes we are using to take care of them.

  • Third-Party License Acknowledgement - This resource contains the names and license copies of external resources that SORMAS is using.

If you want to set up a SORMAS instance for production, testing or development purposes, please refer to the following guides: * Installing a SORMAS Server * Updating a SORMAS Server * Setup Development environment * Creating a Demo Android App

"},{"location":"#project-structure","title":"Project Structure","text":"

The project consists of the following modules:

  • sormas-api: General business logic and definitions for data exchange between app and server
  • sormas-app: The Android app
  • sormas-backend: Server entity services, facades, etc.
  • sormas-base: Base project that also contains build scripts
  • sormas-cargoserver: Setup for a local dev server using maven-cargo
  • sormas-e2e-performance-tests: Automated performance tests addressing the ReST interface (sormas-rest)
  • sormas-e2e-tests: Automated frontend tests addressing sormas-ui and API tests against sormas-rest. The API steps are partly used to prepare data for UI tests.
  • sormas-ear: The ear needed to build the application
  • sormas-keycloak-service-provider: Custom Keycloak SPI for SORMAS
  • sormas-rest: The REST interface; see sormas-rest/README.md
  • sormas-serverlibs: Dependencies to be deployed with the payara server
  • sormas-ui: The web application
  • sormas-widgetset: The GWT widgetset generated by Vaadin
  • sormas-e2e-tests: Automated tests addressing the sormas-ui, and the ReST interface
"},{"location":"3RD_PARTY_ACK/","title":"3rd Party License Acknowledgement","text":"

Nearform temporal tables postgres function is licensed under MIT: sormas-backend/src/main/resources/sql/temporal_tables/LICENSE

Country flags in sormas-ui/src/main/webapp/VAADIN/themes/sormas/img/flag-icons/ are licensed under MIT:

Copyright (c) 2017 Go Squared Ltd. http://www.gosquared.com/\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and \nassociated documentation files (the \"Software\"), to deal in the Software without restriction, including without \nlimitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, \nand to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO \nTHE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL \nTHE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, \nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n
"},{"location":"ADDING_LICENSE/","title":"Adding License Headers","text":""},{"location":"ADDING_LICENSE/#license-header","title":"License Header","text":"

Use the following header for all newly created source files:

SORMAS\u00ae - Surveillance Outbreak Response Management & Analysis System\nCopyright \u00a9 2016-2023 Helmholtz-Zentrum f\u00fcr Infektionsforschung GmbH (HZI)\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see <https://www.gnu.org/licenses/>.\n
"},{"location":"ADDING_LICENSE/#eclipse","title":"Eclipse","text":"
  • Use eclipse's Releng tool to automatically add license headers to all relevant source files (see here for a usage guide)
  • After installing the tool from the marketplace, open Window > Preferences > Copyright Tool and paste the license header from above into the template text area
  • Make sure to select \"Replace all existing copyright comments with this copyright template\" and especially \"Skip over XML files\" (to make sure that headers don't get added to e.g. build files)
  • Whenever you create a new source file: Right click on the file and select \"Fix Copyrights\"
"},{"location":"ADDING_LICENSE/#android-studiointellij","title":"Android Studio/IntelliJ","text":"
  • Open File > Settings > Editor > Copyright > Copyright Profiles
  • Create a new profile and paste the license header from above into the Copyright text area
  • Head back to the general Copyright settings and select the new copyright profile as the \"Default project copyright\"
  • (Optional: If the year has changed, right click on all projects containing manual code and select \"Update Copyright...\", select \"Custom Scope\" and in the dropdown, select \"Project Source Files\"; Click \"Ok\" and wait until the copyright has been added to/changed for all files)
  • Android Studio automatically adds the copyright to newly created files afterwards
"},{"location":"CONTRIBUTING/","title":"Contributing Guidelines","text":"

If you want to contribute to SORMAS by any means - for example by submitting a bug report, requesting a new feature, translating the application into a new language, or actively contributing to the source code - please make sure to read through and follow these guidelines. This allows us to consider and process your contribution as quickly and smoothly as possible. If there is anything unclear to you or you think that this guide is lacking coverage of a specific topic, please get in touch with us through our GitHub Discussions.

"},{"location":"CONTRIBUTING/#table-of-contents","title":"Table of Contents","text":"
  • Submitting an Issue
  • Security Issues
  • Issue Types
  • Bug Report
  • Finding
  • Change Request
  • Feature Request
  • Tasks
  • Epic
  • Contributing to the Project
  • Contributing to the Code
  • Development Contributing Guidelines
  • Picking Issues for Development
  • Submitting Pull Requests
  • Development Workflow
  • Versioning
  • Branches
  • Dependency Management
"},{"location":"CONTRIBUTING/#submitting-an-issue","title":"Submitting an Issue","text":"

Before creating a new issue, please search the repository for similar issues first to avoid duplicates! You can do this by using the search field in the menu bar. If you find an issue that already covers your request or seems very similar, please comment on that issue.

We are currently distinguishing the following issue types: bug, change, feature, finding, task and epic. Please make sure to always use one of the templates that are automatically presented to you when creating a new issue because it ensures that your issue is structured and contains all the information that we need. Issues that have not been created like this will be closed and you will be asked to submit a new issue that adheres to these guidelines.

Please add information to all mandatory sections of the issue templates, and try to fill in the optional sections if possible. Do not remove any section after submitting the issue because they might be filled later by the development team. While moving forward in the development process, developers might also extend or alter the initial issue description, e.g. to adjust it according to the outcome of a refinement process.

"},{"location":"CONTRIBUTING/#security-issues","title":"Security Issues","text":"

If you want to report a security issue, please follow our guidelines for Responsible Disclosure.

"},{"location":"CONTRIBUTING/#issue-types","title":"Issue Types","text":""},{"location":"CONTRIBUTING/#bug-report","title":"Bug Report","text":"

Bug reports cover everything that leads to the application behaving in an unintended way, including crashes, unexpected error messages, data loss, etc.

Before creating a bug report, please check the following rules:

  1. If something seems to be working correctly but does not necessarily match your expectations of how it should be working, please consider opening a change request instead. This applies also for performance issues.
  2. If you have more than one possible bug, especially when you are tempted to create a list of independent findings in the Bug Description, please document each bug as separate issue.
  3. Bugs should generally only be reported as new issues if they occur on a released version. If a bug only occurs on the current development version, it has very likely been caused by implementation done in the current iteration; in this case, the issue responsible for introducing the bug has to be reopened instead of creating a new issue. This process helps to keep both the commit history and the release notes clean.

The development team defines a severity for bugs to distinguish the consequences for affected users:

  • critical: The system is unusable as a whole or on critical functionality with no reasonable workaround available.
  • major: A functional requirement is incorrect or incomplete and leads to errors or undesirable behavior.
  • minor: Text issues or grammatical mistakes, layouting or cosmetic problems that do not affect the functionality.
"},{"location":"CONTRIBUTING/#change-request","title":"Change Request","text":"

Change requests cover features that are already part of SORMAS. This primarily includes aspects (or whole features) for which you would like to request an altered behavior, but also small extensions (e.g. additional values being added to a dropdown field).

A change request (excluding the optional fields) could look like this:

"},{"location":"CONTRIBUTING/#problem-description","title":"Problem Description","text":"

The \"Test Results\" card on the surveillance dashboard has a misleading caption. It suggests that it counts the results of all pathogen tests related to samples reported in the chosen time period while it actually refers to the final laboratory result of the latest sample associated to each case.

"},{"location":"CONTRIBUTING/#proposed-change","title":"Proposed Change","text":"

Change the caption to \"Final Laboratory Results\" and add a tooltip that explains how the numbers are collected.

"},{"location":"CONTRIBUTING/#added-valuebenefit","title":"Added Value/Benefit","text":"

Users will have a better intuitive understanding of what the card shows, and misunderstandings are further prevented by adding the tooltip.

"},{"location":"CONTRIBUTING/#acceptance-criteria","title":"Acceptance Criteria","text":"
  • [ ] The \"Test Results\" caption on the surveillance dashboard has been renamed to \"Pathogen Test Results\"
  • [ ] An info icon has been added to the right of the caption; should look like the info icon in the case directory filters next to the case reference date dropdown
  • [ ] Hovering over the icon displays the following tooltip: \"When a case has multiple samples, only the final laboratory result of the sample with the latest sample collection date is considered.\"
"},{"location":"CONTRIBUTING/#feature-request","title":"Feature Request","text":"

Feature requests cover everything that involves adding new features to SORMAS. This includes both very large additions like completely new app sections, but also smaller ones like adding a new field to an existing form.

"},{"location":"CONTRIBUTING/#finding","title":"Finding","text":"

Findings are used to document unexpected behavior, mostly encountered during the development process. You can also use this issue type if you're not sure whether the behavior is a bug or not.

The development team will investigate the finding and add more details when needed. The goal is to either - convert it to a bug if it is considered to be a bug on a released version. - convert it to a change or feature request if there is something to improve. - dismiss it as a duplicate if a responsible issue in the current iteration is found and reopened or fixed. - dismiss it as discarded if the finding is not an issue.

A severity as for bugs can also be used for findings.

"},{"location":"CONTRIBUTING/#task","title":"Task","text":"

Tasks are things that need to be done but do not directly change anything about the product. This could for example be the preparation of an upcoming feature/change, optimization of processes on GitHub, working on automated tests, or the update of one of the guides or Wiki articles in this repository.

"},{"location":"CONTRIBUTING/#epic","title":"Epic","text":"

The development team uses an epic as an umbrella for large change or feature streams that are linked together. Within the epic the included issues are linked in the Tasks section.

"},{"location":"CONTRIBUTING/#contributing-to-the-project","title":"Contributing to the Project","text":"

There are many ways in which you can contribute to this project as a non-developer. If there is something you would like to do that you don't find instructions about here - or if you want to learn how you can get involved - please contact us at sormas@helmholtz-hzi.de or through our GitHub Discussions and let us know how we can assist you!

Some possibilities to contribute to SORMAS are:

  • Helping with translation
  • Defining new diseases
"},{"location":"CONTRIBUTING/#contributing-to-the-code","title":"Contributing to the Code","text":"

If you're interested in participating in the development of SORMAS, please follow the Development Environment Setup Instructions before you start developing. If you have problems setting up your development environment or need assistance in choosing the first issue to work on, please get in touch with us through our GitHub Discussions or by contacting us at sormas@helmholtz-hzi.de.

Additionally, our Wiki contains some specific development guides that cover common issues like adding new fields to an entity that we suggest to check out before you start implementing something related to those topics:

  • Technical User Guides
  • Development Guides
"},{"location":"CONTRIBUTING/#development-contributing-guidelines","title":"Development Contributing Guidelines","text":"

In addition to the guidelines covered by the Development Environment Setup Instructions, please ensure to adhere to the following principles and procedures while developing code for SORMAS.

"},{"location":"CONTRIBUTING/#source-code","title":"Source Code","text":"
  1. Remember to always apply code formatting and import reordering for all classes you work on; we recommend to use the Save Actions plugin as described in the setup instructions instead of manually executing these steps.
  2. Some code formatting rules can't be enforced by the code formatter. Please make sure to write your code in accordance to the following rules:
    • When defining a method, enter a blank line before starting to write its body (except for methods with only one line of code, e.g. most getters and setters).
    • Use a blank line to separate logical blocks of code within a method.
    • Apart from those, don't use blank lines where they are not necessarily needed to keep the code compact and readable.
    • Don't use blank lines after the last statement of a block, but a closing } with proper indentation in the next line instead.
    • Don't use blank lines between two closing }.
  3. You can use //@formatter:off and //@formatter:on to encapsulate code blocks that you don't want automatic code formatting to be applied to, e.g. because it would mess up readability. Please only use this if really needed and try to use proper indentation nonetheless.
  4. Separate code and comments, i.e. write the comment in a separate line before the statement that you want to explain.
  5. When you create new classes, please add license headers to them according to the Adding License Headers guide.
"},{"location":"CONTRIBUTING/#commits","title":"Commits","text":"
  1. Commit messages of every commit have to be related to a specific issue on GitHub and reference its issue number as well as include a short description on what has been done in the commit. We will reject pull requests that violate this principle and ask you to re-submit it with proper commit messages. An acceptable commit message could look like this:
  2. A common practice for Git commits is to keep the first line with 50 characters as a meaningful short description, and to deliver additional details following in line 3 onwards. Most git viewers abbreviate the first line after 50 characters.
  3. Keep changes in bugfixes clean and compact to be able to easily review them and leave the possibility to cherry-pick the changes to another branch to fix a previous version. Don't clean dirty code along the way if not really needed to fix the problem.
  4. If an issue requires a lot of code changes, consider breaking down these changes in logical steps. They are then easier to review, have more meaningful commit messages and partly deliver the intended value.
  5. Don't mix refactoring with functional changes (new functionality, changes on features, bugfixes) within the same commit, since it makes reviewing the changes much harder. Usually the refactoring of existing code has to happen beforehand in at least one separate commit. Refactoring includes e.g. cleaning up and restructuring code or renaming.
  6. If it helps, it is okay to have several branches and pull requests for the same ticket (usually one after another, sometimes to work in parallel or to prepare changes in advance).
  7. If there is a finding concerning an issue which has been already closed, it will be reopened if that version is not yet released. You (or someone else) will have to quickly fix the finding before the version is released. If the version has been released already, the issue will stay closed and a new issue has to be filed.
"},{"location":"CONTRIBUTING/#1234-added-model-to-define-classification","title":"1234 - Added model to define classification","text":""},{"location":"CONTRIBUTING/#1234-added-model-to-define-classification_1","title":"1234 - Added model to define classification","text":"
  • Apply automatic case classification whenever a field value changes
  • Show classification in lists
"},{"location":"CONTRIBUTING/#picking-issues-for-development","title":"Picking Issues for Development","text":"

When picking tasks for development, you can either search the repository for existing issues that you would like to work on, or you can submit your own issues if you don't find any that cover your topic of interest (see the \"Submitting an Issue\" section of this guide). However, please note that issues need to fit our overall vision of the project.

Once you have chosen an issue that you want to work on, please adhere to the following process and ideally only start developing once it has been completed. This ensures that your development work is perfectly aligned with the current state and vision of the project, and will make the pull request review process as smooth as possible.

  1. Leave a comment on the issue about your intention to implement it.
  2. A member of the core development team will check the issue and might:
    • Turn the issue into a discussion if they're not sure whether it fits the overall SORMAS vision or there are a lot of discussion points.
    • Ask for additional details to be added to the issue description if you have created the issue yourself and there are questions e.g. concerning its added value or the proposed solution.
    • Take the issue into internal refinement if it doesn't meet the Definition of Ready yet.
  3. If the issue meets the Definition of Ready, a member of the core development team adds the ready label and notifies you that you can start to implement it.

Issues that are already marked with the ready label can theoretically be picked up for development immediately. However, please note that these issues will usually already be planned for one of the upcoming iterations and it's possible that we'll start development on it ourselves. This is especially true for issues that are assigned to one of the sprint backlog projects.

"},{"location":"CONTRIBUTING/#submitting-pull-requests","title":"Submitting Pull Requests","text":"

Contributing to the SORMAS code requires you to submit pull requests that will be reviewed by one of our core developers. Once a pull request has been submitted to our repository, a developer will either assign themselves as its reviewer or we will get back to you in case we won't be able to review it in time. This may e.g. happen if your pull request involves a lot of technical changes that we would like to merge together with other issues of the same nature or that could potentially break a lot of logic. Usually though, the general process looks like this:

  1. A developer assigns themselves as the reviewer of your pull request (core developers assign each other). Please wait until the review is done; if you think that the review is taking too long, feel free to add a comment to the pull request as a reminder to the developer.
  2. The developer might request changes to the code. If that's the case, please implement the requested changes or answer to their change request if you have questions or don't agree with a specific part of the request.
  3. Once you've implemented all requested changes, please request another review by the assigned developer by clicking on the \"Re-request review\" icon next to their name. This step is necessary in order for the reviewer to be properly notified and for the pull request to show up in their review list again.
  4. As soon as the developer is happy with the submitted code (which might require multiple iterations of step 2 and 3, especially for larger pull requests), they will merge it into the development branch and close the pull request.

Please adhere to the following principles when submitting pull requests:

  1. Only submit pull requests that are directly associated with one specific issue in our GitHub repository. If there is no issue for the development work that you would like to do, create one before you start working on it.
  2. Link your pull request to the issue(s) that they are associated with. This can be done either by using the \"Linked issues\" section at the right side when viewing a pull request, or by adding the keyword \"Closes\" or \"Fixes\" followed by the issue number to the pull request description (e.g. \"Fixes #1234\").
  3. Make sure that your pull request has a meaningful title. By default, GitHub will use your commit message as the title which might not be appropriate. In general, using the same name as the linked issue is a good rule of thumb.
  4. Try to not use force-push when updating an existing pull request (e.g. after changes have been requested or because you need to resolve merge conflicts).
  5. Ideally, your pull request should pass the checks done by the automatic CI pipeline before it gets reviewed. If that's not the case, please make sure that your branch is up-to-date with the current development branch. If the checks also fail for the development branch, you're not required to do anything. In any other case, please fix the issues (most likely failed unit tests) before requesting another review.
"},{"location":"CONTRIBUTING/#development-workflow","title":"Development Workflow","text":"

For SORMAS we use the Gitflow development workflow.

"},{"location":"CONTRIBUTING/#versioning","title":"Versioning","text":"

For version numbers we use semantic versioning. The meaning of a given version number X.Y.Z is:

  • X: Major version: Major changes, severe changes in API or technical architecture.
  • Y: Minor version: Usually a new release of a development iteration of a few weeks, containing new features and changes.
  • Z: Micro version: Fixing problems in the last minor version to make it properly or better to use. Usually contains only bugfixes.

Versions are defined as Git tags with release notes attached to the tag.

An unstable version currently under development is denoted as X.Y.Z-SNAPSHOT.

"},{"location":"CONTRIBUTING/#branches","title":"Branches","text":""},{"location":"CONTRIBUTING/#permanent-branches","title":"Permanent branches","text":"
  • development: This is where the changes are commited/merged to by the developers.
  • master: In regular intervals, the changes from development are merged to master with an identifiable version (tag). On top of this branch is always the latest released version.
  • master-: In case an older version than the head version on master needs to be fixed (a new micro release), a dedicated master-<version> branch is split from master to manage the following micro releases (Example: master-1.75.x). Changes made on this branch are usually cherry-picked from a newer version (on master or development).
  • l10n_development: Incoming changes on translation files from Crowdin that are regularly merged into development.
  • "},{"location":"CONTRIBUTING/#supporting-branches","title":"Supporting branches","text":"
    • release-: To manage changes when merging from development to master. Once the new version is merged to master, the release-<version> branch is automatically removed.
    • hotfix-: To manage changes that are needed on an already released version (on any master branch) that need to be fixed with a new micro release.

      Some branches contain the concerned version in its name, examples: release-1.75.0, hotfix-1.75.1. To manage new versions, tools are used to automatically merge between branches and tag the new version. Once the new version is merged to master/master-<version, the release-/hotfix- branch are automatically deleted. There is only one release- and only hotfix- branch allowed at same time (enforced by the used Maven plugin).

      "},{"location":"CONTRIBUTING/#implementation-branches","title":"Implementation branches","text":"

      These kind of branches are manually created and maintained by developers who work on an issue. Such branches are used to create pull requests or to review the changes before merged into a permanent or supporting branch. * feature-1234_short_description: Any branch that is supposed to contribute to development or a hotfix branch.

      "},{"location":"CONTRIBUTING/#dependency-management","title":"Dependency Management","text":"

      Dependencies are managed in Maven POM files. For most dependencies the version is defined in sormas-base/pom.xml. \\ sormas-app and sormas-e2e-tests use Gradle instead.

      "},{"location":"CONTRIBUTING/#automatic-updates","title":"Automatic updates","text":"

      Process: At the beginning of each iteration, look over all dependency update PRs created by dependabot and process them according to the following rules: 1. Always use \"Rebase and merge\" instead of creating a merge commit! If needed use the comment @dependabot rebase to rebase the PR to the latest development commit. 2. If all CI checks are successful, minor and micro version updates can be merged 3. For major version updates read the changelog to understand implications and possible migration steps of the update. \\ Decide on you own wheter it is necessary to manually test the PR before merging. 4. If CI checks are negative or migration steps are needed, decide whether the update is important enough to justify the effort of additional work. \\ If the PR is not needed, you can use @dependabot ignore this major version or @dependabot ignore this minor version.

      "},{"location":"CONTRIBUTING/#payara","title":"Payara","text":"

      All required dependencies provided by the Payara application server are set to \"provided\" in maven and may only be updated in combination with an update of the Payara version. \\ These dependencies have been added to the dependabot ignore list, so no dependency update PR is automatically created.

      "},{"location":"CONTRIBUTING/#keycloak","title":"Keycloak","text":"

      The Keycloak version in SORMAS-Project only defines the version of the Keycloak admin client library. When a new version of Keycloak is available this should be updated after the Keycloak version is SORMAS-Docker has been updated. \\ This can be tested locally by building the updated Keycloak docker image and then using it in a container together with a local Payara instance.

      "},{"location":"DEFINITION_OF_READY/","title":"Definition of Ready","text":"

      In order to be eligible for being implemented in the current iteration, an issue has to be marked with the ready label. This label indicates that the issue fulfills our Definition of Ready, which consists of the following requirements:

      • A representative title that summarizes what the issue is about
      • A dedicated ticket type according to the content of the issue:\u00a0feature, change, bug, or task
      • All mandatory sections of the issue template are filled in with unambiguous information, i.e. without any points of discussion
      • Features/changes that involve significant innovative UI/UX work (e.g. new sections, new UI elements, new workflows) provide mockups that visually describe how the feature/change is supposed to look like
      • Issues that are technically complex have at least rough implementation details
      • The issue is appropriately labeled, i.e. with the affected part of the application (android-app, vaadin-app, or backend) and the functional and/or technical area(s) it affects

      Ideally, change and feature requests also have a story point estimation, specified in brackets after their title.

      "},{"location":"DEMO_APP/","title":"Creating a demo app for a SORMAS demo server","text":"

      Important: This only applies if you have setup your own SORMAS server for demo purposes and and want to give users easy access to it.

      "},{"location":"DEMO_APP/#step-1-adjust-the-sormas-appproperties","title":"Step 1: Adjust the sormas-app.properties","text":"
      1. Open the the apk file from the SORMAS release with a zip editor (e.g. 7zip).
      2. Extract sormas-app.properties and open the the file for editing.
      3. Set server.url.default to the URL of your SORMAS server's ReST interface.
      4. Set user.name.default and user.password.default to the demo user (needs to be an informant or officer).
      5. Overwrite the sormas-app.properties in the apk with your changed version.
      "},{"location":"DEMO_APP/#step-2-sign-the-changed-apk-file","title":"Step 2: Sign the changed apk file","text":"

      Since the apk file has been changed it needs to be signed again.\\ Important: When you change and sign the apk file it is no longer compatible with the original apk file for automatic app update! If you still want to make this work you always have to sign new versions using the same keystore and put the changed apk-file into your SORMAS server for automatic app update.\\

      1. Create a keystore using keytool: keytool -genkey -v -keystore my-demo-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias

        Note: keytool is located in the bin/ directory in your JDK. To locate your JDK from Android Studio, select File > Project Structure, and then click SDK Location and you will see the JDK location.

      2. Download uber-apk-signer: https://github.com/patrickfav/uber-apk-signer/releases.

        Note: this is the convenient way to do it. You can also get an Android SDK and follow the instructions given here

      3. Sign the apk file: java -jar uber-apk-signer.jar --ks my-demo-key.jks -ksAlias my-alias --allowResign --apks sormas-version-demo.apk

        See also: https://github.com/patrickfav/uber-apk-signer#command-line-interface

      "},{"location":"DEVELOPMENT_ENVIRONMENT/","title":"Development Environment Setup","text":"

      This step-by-step guide explains how to set up your development environment, using either Eclipse or IntelliJ for the backend and web UI and Android Studio for the mobile app. Please follow it strictly to make sure that development will run as smoothly as possible and your code adheres to our guidelines.

      Please note that these instructions are optimized for Windows and Linux systems. If you're developing on a Mac and, we would be glad to get your feedback about how this guide can be extended with OS-specific instructions through our GitHub Discussions.

      "},{"location":"DEVELOPMENT_ENVIRONMENT/#step-1-check-out-the-sormas-repository","title":"Step 1: Check Out the SORMAS Repository","text":"
      • Download and install the latest Git version for your operating system
      • Optional: Install a Git client such as TortoiseGit or GitHub Desktop if you don't want to handle version control from the command-line or within your IDE
      • Optional: Clone the SORMAS-Project repository with git clone https://github.com/sormas-foundation/SORMAS-Project.git; if you want to use Git from within your IDE, you can also clone the repository in Step 4
      • Open Git Bash and execute the following command to ensure that rebase is used when pulling the development branch rather than merge: git config --global branch.development.rebase true
      "},{"location":"DEVELOPMENT_ENVIRONMENT/#step-2-install-java","title":"Step 2: Install Java","text":"

      Download and install the Java 11 JDK (not JRE) for your operating system, which is also needed for the Server Setup. We suggest using Zulu OpenJDK. If you're running Linux, please refer to the official documentation on how to install Zulu OpenJDK on your system.

      Note: To work with the Android app JDK 17 is needed for the gradle build. The needed JDK is part of Android Studio, thus there is no need to manually install it.

      The SORMAS CI is using JDK 17 to build all modules. The only known difference though are slight differences in the Java time API that affect unit tests, so again there is no need to setup two JDKs on your local system.

      "},{"location":"DEVELOPMENT_ENVIRONMENT/#step-3-install-maven","title":"Step 3: Install Maven","text":"

      The scripts in sormas-base/dev expect mvn as command-line tool. Download and install Maven for your operating system, see binaries.

      "},{"location":"DEVELOPMENT_ENVIRONMENT/#step-4-install-a-local-sormas-server","title":"Step 4: Install a Local SORMAS Server","text":"

      Please follow the Server Installation Instructions to set up a local SORMAS instance that you will use to test your code. Alternatively, you can also use Maven Cargo, or a Docker installation (not recommended at this time).

      "},{"location":"DEVELOPMENT_ENVIRONMENT/#step-5-install-and-configure-your-ide","title":"Step 5: Install and Configure Your IDE","text":""},{"location":"DEVELOPMENT_ENVIRONMENT/#intellij","title":"IntelliJ","text":"
      • Download and install the latest IntelliJ IDEA Ultimate; (newer than version of 2020-04-15 to enable debugging, see https://youtrack.jetbrains.com/issue/IDEA-216528)
      • Set the project SDK to the installed JDK
      • Optional: Clone the SORMAS-Project repository if you haven't done so already
      • Open the project in IntelliJ. Make sure the project is recognized by IntelliJ as a maven project; if not, right-click the pom.xml file in sormas-base and select Add as maven project.
      • Make sure that under File -> Project Structure -> Modules all modules EXCEPT sormas-app are recognized; if not, add the missing modules with the + button
      • Navigate to File -> Settings -> Plugins and make sure that Glassfish integration is enabled
      • Make a copy of sormas-base/dev.env.example, rename it to dev.env and set GLASSFISH_DOMAIN_ROOT to the location of the SORMAS domain inside your Payara installation
      • Run mvn install on the sormas-base project (e.g. by opening the Maven view and executing sormas-base -> Lifecycle -> install). \\ Alternatively, execute the dev/build.sh script. You can create a run configuration and use the Git bash executable as interpreter to directly run it from the IDE.
      • Execute dev/deploy-serverlibs.sh script
      • Add a Payara server to IntelliJ:
      • Open Run -> Edit Configurations, add a new configuration and choose the Glassfish server template
      • Click on Configure next to Application server and create a new server configuration by selecting your Payara installation directory
      • Check the After launch checkbox and specify the browser that you want SORMAS to open in once the server has been deployed
      • Enter http://localhost:6080/sormas-ui into the URL field
      • Make sure that the correct JRE is specified (your Java 11 JDK)
      • Enter the path to the SORMAS domain and the credentials that you've specified when setting up the server
      • Open the Deployment tab and add the artifacts sormas-ear, sormas-rest and sormas-ui (make sure to respect this order as there are dependencies between artifacts at startup)
      • Open the Logs tab and add a new log file pointing to the logs/server.log file in your SORMAS domain
      • Open the Startup/Connection tab and make sure that Pass environment variables is NOT checked; ignore warnings about the debug configuration not being correct
      • Open the config/domain.xml file in your domain directory and make sure that the java-config node contains the following code: <java-config classpath-suffix=\"\" debug-enabled=\"true\" debug-options=\"-agentlib:jdwp=transport=dt_socket,address=6009,server=n,suspend=y\" ...
      • Set the default working directory for run configurations by navigating to Run -> Edit Configurations -> Templates -> Application and setting Working directory to $MODULE_WORKING_DIR$
      • Optional: Setup database access from Intellij: Open View -> Tool View -> Database, click on + icon and select DataSource -> PostgreSQL and configure the database (set user and password and download the missing driver files if needed)
      "},{"location":"DEVELOPMENT_ENVIRONMENT/#known-issues","title":"Known issues","text":"
      • The first time you build the project in IntelliJ, you have to switch the java compiler to \"Eclipse\" to workarround a dependency resolution problem in sormas-api.
      "},{"location":"DEVELOPMENT_ENVIRONMENT/#eclipse","title":"Eclipse","text":"
      • Download and install the latest Eclipse IDE for Enterprise Java and Web Developers
      • Set the default JRE of Eclipse to the installed JDK: Assigning the default JRE for the workbench
      • Optional: Clone the SORMAS-Project repository if you haven't done so already via File -> Import -> Git -> Projects from Git and cancel the process when you're asked to create a new project from the cloned repository
      • Import the projects from the SORMAS-Project repository into your workspace via File -> Import -> Maven -> Existing Maven Projects
      • Install the Payara Tools plugin
      • Install the Vaadin Plugin for Eclipse; the commercial UI designer is not needed
      • Add a Payara server to Eclipse and enter the credentials you specified when setting up the local SORMAS server
      • Make a copy of sormas-base/dev.env.example, rename it to dev.env and set GLASSFISH_DOMAIN_ROOT to the location of the SORMAS domain inside your Payara installation
      • Either run mvn install on the sormas-base project or execute the dev/build.sh script (for example with Git Bash)
      • Execute dev/deploy-serverlibs.sh script
      • Highlight all Eclipse projects and choose Maven -> Update Project from the right-click menu; perform the update for all projects
      • Start the Glassfish server and deploy sormas-ear, sormas-rest and sormas-ui by dragging the respective projects onto it, or use the Add and Remove... function by right-clicking on the server (make sure to respect this order as there are depdendencies between artifacts at startup)
      • Open your browser and type in http://localhost:6080/sormas-ui to test whether the server and IDE have been set up correctly
      "},{"location":"DEVELOPMENT_ENVIRONMENT/#android-studio","title":"Android Studio","text":"

      Please note: You only need to install Android Studio if you're developing code for the Android app. This is likely the case when you're adding new fields or entities to the system, or if you specifically want to work on the mobile app.

      • Download and install the latest Android Studio version
      • Please make sure to run the installer with admin rights if you're using Windows
      • Ensure that the Android SDK installation path does not contain whitespaces; you can also change this later via Tools -> SDK Manager -> Android SDK Location
      • Open Android Studio and import the sormas-app module from the SORMAS-Project repository
      • Make a copy of keystore.properties.example and rename it to keystore.properties
      • Make sure to use the JDK version 11 (File -> Project Structure -> SDK Location -> JDK Location)
      • Build the Android Studio project by executing the Gradle build (this may be done automatically)
      • Add an emulator and set the SDK version to the minSdkVersion or targetSdkVersion from build.gradle; we suggest to test your code on both, but minSdkVersion should be preferred to ensure compatibility to the minimum supported SDK
      • Click on Run 'app' to install and run the app on your emulator; enter http://10.0.2.2:6080/sormas-rest as the server URL when you start the newly installed app for the first time

      Important: Whenever you do or pull changes in the sormas-api project that you want to use in the mobile app or that are referenced there already, you need to execute the dev/build.sh script to notify the sormas-app project of the changes.

      "},{"location":"DEVELOPMENT_ENVIRONMENT/#step-6-configure-code-formatting-and-import-settings","title":"Step 6: Configure Code Formatting and Import Settings","text":"

      In order to ensure a consistent code style and prevent so-called edit wars, we have set up custom configuration files for automatic code formatting and import ordering. Please make sure to adhere to the following steps for your IDE(s) before you start developing.

      "},{"location":"DEVELOPMENT_ENVIRONMENT/#intellij-and-android-studio-settings","title":"IntelliJ and Android Studio Settings","text":"
      • Install the Eclipse Code Formatter for IntelliJ/Android Studio plugin
      • Open the plugin settings via File -> Settings -> Other Settings -> Eclipse Code Formatter and select Use the Eclipse Code Formatter
      • Under Eclipse formatter config, choose Eclipse workspace/project folder or config file and select sormas-base/java-formatter-profile.xml
      • Check Optimize Imports
      • Under Import order, choose From file and select sormas-base/java-importorder-profile.importorder
      • Make sure that Do not format other file types by IntelliJ formatter is selected
      • Go to Editor -> Code Style -> Java -> Imports and set Class count to use import with '*' and Names count to use static import with '*' to 99
      • Navigate to Editor -> General -> Auto Import and disable Optimize imports on the fly

      Optional, but strongly recommended: - Install the Save Actions plugin that automatically applies code formatting and import reordering whenever you save a file - otherwise you will manually have to do so (by default with Ctrl+Alt+L) - Open the plugin settings via File -> Settings -> Other Settings -> Save Actions and make sure that the first three checkboxes under General and the first two checkboxes under Formatting Actions are selected

      "},{"location":"DEVELOPMENT_ENVIRONMENT/#eclipse-settings","title":"Eclipse Settings","text":"
      • Open Window -> Preferences
      • Navigate to Java -> Code Style -> Formatter, import sormas-base/java-formatter-profile.xml and apply the changes
      • Navigate to Java -> Code Style -> Organize Imports and import sormas-base/java-importorder-profile.importorder
      • On the same screen, set Number of imports needed for .* and Number of static imports needed for .* to 99
      • On the same screen, make sure that Do not create import for types starting with a lowercase letter is checked and apply the changes
      • Navigate to Java -> Editor -> Save Actions and make sure that the following options are selected: Perform the selected actions on save, Format source code, Format all lines and Organize imports
      "},{"location":"DEVELOPMENT_ENVIRONMENT/#issues-which-can-appear-during-installation-process-of-the-project","title":"Issues which can appear during installation process of the project","text":"
      1. If debug mode does not work: To replace opt\\payara5\\glassfish\\modules\\launcher.jar with sormas-base/setup/launcher.jar

      2. For Windows: Please check your java_version. In case if you have the multiple java_versions installed on the system, it will always show to you the first version installed. I had the java 8 instead of 11. In order to fix it, go to environment variables, and move the 11 version up. And rerun the script. Seems that the console is reading those variables at the starting point, and the values of it can be updated only after console/script restart.

      3. For Windows: Pay attention to the postgres SQL files rights permissions after unziping the downloaded ZIP archive. Files physically were present but next script error has been generated: psql:setup.sql:7: ERROR: could not open extension control file \"C:/Program Files/PostgreSQL/10/share/extension/temporal_tables.control\": No such file or directory -I checked the file rights, and under windows they has AV attribute, however, all others has only A attribute. When I was trying to open them with Notepad++ it was saying that such file does not exist. Do you want to create it? If yes will be pressed - another message saying that the file exists, appeared. Very strange scenario...

      4. All the postgres commands (of added users, etc.) which were added at first startup of the application - will raise errors in case if such entity exists. Just ignore those errors at repeated installation of .\\server-setup.sh

      5. Check always the port number 6048 which can be occupied by an old instance of payara. -> For every installation, kill all Java/javaw processes and check the availability of 6048 port number. -> Delete files with generated domain folders and payara. In order to have a clean installation of each next ./server-setup.sh run.

      6. For the sormas-base/dev scripts Maven needs to be installed as command-line tool or defined in sormas-base/dev.env as MVN_BIN which Maven to be used.

      7. For eclipse formatted plugin, there is an issue for Idea: https://plugins.jetbrains.com/plugin/6546-eclipse-code-formatter - cannot save settings Path to custom eclipse folder is not valid - it works only when settings were saved from down to up. And not vice versa.

      If something is still not working: -> Stop the payara domain, run dev/deploy-serverlibs.sh to update libs -> clean up (delete all from domains/sormas/autodeploy, domains/sormas/applications, domains/sormas/generated, and domains/sormas/osgi-cache) try to build again by executing mvn clean install -DskipTests on the sormas-base module -> start the domain and deploy again

      "},{"location":"DEVELOPMENT_ENVIRONMENT/#avoid-redeployment-problems","title":"Avoid redeployment problems","text":"

      Problem: Due to currently a not mitigated problem, it is only possible to deploy the sormas-ear.ear (contains sormas-backend) once without problems. If you undeploy it and deploy sormas-ear.ear again, the other artifacts sormas-uiand sormas-rest cannot successfully call the backend.

      Workaround: Undeploy sormas-ear and all other sormas artifacts, restart the Payara domain, deploy sormas-ear again (the same or changed version).

      Symptom: This exception occurs when sormas-ui or sormas-rest calls the sormas-backend.

      Caused by: java.lang.IllegalArgumentException: Can not set java.util.Properties field de.symeda.sormas.backend.common.ConfigFacadeEjb.props to de.symeda.sormas.backend.common.ConfigFacadeEjb\n    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)\n    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)\n    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)\n    at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)\n    at java.base/java.lang.reflect.Field.set(Field.java:780)\n   at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl._inject(InjectionManagerImpl.java:594)\n

      Additional info: - You can undeploy and deploy all other modules without restarting the Payara domain, as long as nothing changes on sormas-ear (implicates sormas-api and sormas-backend). - The problem occurs no matter if you deploy directly from your IDE or as packaged ears/wars into the autodeploy directory. - Related ticket: #2511

      "},{"location":"EXTERNAL_MESSAGES_ADAPTER/","title":"External Message Adapter Implementation Guide","text":""},{"location":"EXTERNAL_MESSAGES_ADAPTER/#the-project","title":"The project","text":"

      A minimal maven project structure for an external message adapter module is shown below:

        my-message-adapter\n  |-- src\n    |   |-- main\n    |   |   |-- java\n    |   |   |   |-- my.project\n    |   |   |   |   |-- MyExternalMessageFacadeEjb.java\n    |   |   |-- resources\n    |   |   |   |-- version.txt\n    |   |-- test\n    |   |   |-- java\n    |   |   |   |-- my.project\n    |   |   |   |   |-- MyMessageAdapterTest.java\n    |   |   |-- resources\n    |-- pom.xml    \n\n
      "},{"location":"EXTERNAL_MESSAGES_ADAPTER/#mymessageadapterjava","title":"MyMessageAdapter.java","text":"

      This is the main class of the external message adapter module. It must implement the ExternalMessageAdapterFacade interface from the sormas-api module.

      For more information please refer to the javadoc on the ExternalMessageAdapterFacade interface.

      Example:

      package my.project;\n\nimport de.symeda.sormas.api.externalmessage.ExternalMessageAdapterFacade;\nimport de.symeda.sormas.api.externalmessage.ExternalMessageDto;\nimport de.symeda.sormas.api.externalmessage.ExternalMessageResult;\n\nimport javax.ejb.Stateless;\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\n@Stateless(name = \"MyExternalMessageFacade\")\npublic class MyExternalMessageFacadeEjb implements ExternalMessageAdapterFacade {\n\n    @Override\n    public ExternalMessageResult<List<ExternalMessageDto>> getExternalMessages(Date since) {\n        return new ExternalMessageResult<>(Collections.emptyList(), new Date(), true, null);\n    }\n\n    @Override\n    public ExternalMessageResult<String> convertToHTML(ExternalMessageDto message) {\n        return new ExternalMessageResult<>(\"\", new Date(), true, null);\n    }\n\n    @Override\n    public ExternalMessageResult<byte[]> convertToPDF(ExternalMessageDto message) {\n        return new ExternalMessageResult<>(new byte[0], new Date(), true, null);\n    }\n\n    @Override\n    public String getVersion() {\n        return new BufferedReader(\n                new InputStreamReader(\n                        Objects.requireNonNull(MyExternalMessageFacadeEjb.class.getResourceAsStream(\"/version.txt\")),\n                        StandardCharsets.UTF_8)).lines().collect(Collectors.joining(\"\\n\"));\n    }\n}\n
      "},{"location":"EXTERNAL_MESSAGES_ADAPTER/#versiontxt","title":"version.txt","text":"

      This file contains the template string that describes this modules version. It is used by the SORMAS server to display the version of configured external message module's version on the about page. SORMAS also uses the version method for doing an availability check.

      You can use the maven resource filtering to replace placeholders in the version.txt file with values from the pom.xml file.

      Example:

      ${project.name} ${project.version}\n
      "},{"location":"EXTERNAL_MESSAGES_ADAPTER/#pomxml","title":"pom.xml","text":"

      This is the maven project configuration file.

      The sormas-api must be added as a dependency to your project in order to be able to implement the ExternalMessageAdapterFacade interface.

      Example with default dependencies for java-ee, sormas-api, logging and testing:

      Expand to see below the whole file content
          <project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n        <modelVersion>4.0.0</modelVersion>\n\n        <properties>\n            <sormas.version>1.85.0</sormas.version>\n            <warName>my-message-adapter</warName>\n\n            <javaee.version>8.0</javaee.version>\n            <slf4j.version>1.7.30</slf4j.version>\n            <junit.version>4.13.1</junit.version>\n        </properties>\n\n        <groupId>my.project</groupId>\n        <artifactId>my-message-adapter</artifactId>\n        <name>${project.artifactId}</name>\n        <packaging>war</packaging>\n        <version>0.1.0-SNAPSHOT</version>\n\n        <dependencies>\n            <dependency>\n                <groupId>javax</groupId>\n                <artifactId>javaee-web-api</artifactId>\n                <version>${javaee.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>${project.groupId}</groupId>\n                <artifactId>sormas-api</artifactId>\n                <version>${sormas.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>slf4j-api</artifactId>\n                <version>${slf4j.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>junit</groupId>\n                <artifactId>junit</artifactId>\n                <version>${junit.version}</version>\n                <scope>test</scope>\n            </dependency>\n            ...\n        </dependencies>\n\n        <build>\n            <finalName>${project.artifactId}</finalName>\n            ...\n        </build>\n</project>\n
      \n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <properties>\n        <sormas.version>1.85.0</sormas.version>\n        <warName>my-message-adapter</warName>\n\n        <junit.version>4.13.1</junit.version>\n        <mockito.version>3.6.0</mockito.version>\n        <assertj.version>3.18.1</assertj.version>\n\n        <commons-lang.version>3.11</commons-lang.version>\n        <commons-io.version>2.8.0</commons-io.version>\n        <slf4j.version>1.7.30</slf4j.version>\n        <javaee.version>8.0.1</javaee.version>\n\n        <maven.compiler.source>11</maven.compiler.source>\n        <maven.compiler.target>11</maven.compiler.target>\n        <productionBranch>master</productionBranch>\n    </properties>\n\n    <groupId>my.project</groupId>\n    <artifactId>my-message-adapter</artifactId>\n    <name>${project.artifactId}</name>\n    <packaging>war</packaging>\n    <version>0.1.0-SNAPSHOT</version>\n\n    <repositories>\n        <repository>\n            <id>central</id>\n            <name>bintray</name>\n            <url>https://jcenter.bintray.com</url>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </repository>\n    </repositories>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>javax</groupId>\n            <artifactId>javaee-web-api</artifactId>\n            <version>${javaee.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <version>${slf4j.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>${project.groupId}</groupId>\n            <artifactId>sormas-api</artifactId>\n            <version>${sormas.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>${junit.version}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-core</artifactId>\n            <version>${mockito.version}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-inline</artifactId>\n            <version>${mockito.version}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <version>${assertj.version}</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.jsoup</groupId>\n            <artifactId>jsoup</artifactId>\n            <version>1.14.2</version>\n            <scope>compile</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.hamcrest</groupId>\n            <artifactId>hamcrest-library</artifactId>\n            <version>1.3</version>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.maven</groupId>\n            <artifactId>maven-model</artifactId>\n            <version>3.8.4</version>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <finalName>${project.artifactId}</finalName>\n\n        <!-- Provide version.txt that can be read for returning the desired version string of this module -->\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n                <filtering>true</filtering>\n                <includes>\n                    <include>**/version.txt</include>\n                </includes>\n            </resource>\n            <resource>\n                <directory>src/main/resources</directory>\n                <filtering>false</filtering>\n                <excludes>\n                    <exclude>**/version.txt</exclude>\n                </excludes>\n            </resource>\n        </resources>\n\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-war-plugin</artifactId>\n                <version>3.2.3</version>\n                <configuration>\n                    <warName>${warName}</warName>\n\n                    <filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>\n                    <webResources>\n                        <resource>\n                            <directory>src/main/webapp</directory>\n                            <targetPath>/</targetPath>\n                            <filtering>false</filtering>\n                        </resource>\n                    </webResources>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>com.amashchenko.maven.plugin</groupId>\n                <artifactId>gitflow-maven-plugin</artifactId>\n                <version>1.15.1</version>\n                <configuration>\n                    <gitFlowConfig>\n                        <productionBranch>${productionBranch}</productionBranch>\n                        <developmentBranch>development</developmentBranch>\n                        <featureBranchPrefix>feature-</featureBranchPrefix>\n                        <releaseBranchPrefix>release-</releaseBranchPrefix>\n                        <hotfixBranchPrefix>hotfix-</hotfixBranchPrefix>\n                        <versionTagPrefix>v</versionTagPrefix>\n                    </gitFlowConfig>\n                    <commitMessagePrefix>[GITFLOW]</commitMessagePrefix>\n                    <useSnapshotInHotfix>true</useSnapshotInHotfix>\n                </configuration>\n            </plugin>\n\n            <!-- Code Coverage / activate Integration Tests -->\n            <plugin>\n                <artifactId>maven-failsafe-plugin</artifactId>\n                <version>2.19.1</version>\n                <executions>\n                    <execution>\n                        <id>integration-test</id>\n                        <phase>integration-test</phase>\n                        <goals>\n                            <goal>integration-test</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>verify</id>\n                        <phase>verify</phase>\n                        <goals>\n                            <goal>verify</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n\n            <plugin>\n                <groupId>org.jacoco</groupId>\n                <artifactId>jacoco-maven-plugin</artifactId>\n                <version>0.8.5</version>\n                <inherited>true</inherited>\n                <executions>\n                    <execution>\n                        <id>prepare-coverage</id>\n                        <phase>generate-test-sources</phase>\n                        <goals>\n                            <goal>prepare-agent</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>analyze-coverage</id>\n                        <phase>verify</phase>\n                        <goals>\n                            <goal>report</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>prepare-coverage-integration</id>\n                        <phase>pre-integration-test</phase>\n                        <goals>\n                            <goal>prepare-agent-integration</goal>\n                        </goals>\n                    </execution>\n                    <execution>\n                        <id>analyze-coverage-integration</id>\n                        <phase>post-integration-test</phase>\n                        <goals>\n                            <goal>report-integration</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n
      "},{"location":"EXTERNAL_MESSAGES_ADAPTER/#deploying-into-the-sormas-domain","title":"Deploying into the sormas domain","text":"

      Set the interface.demis.jndiName in the sormas.properties of your SORMAS domain. This JNDI name should match the location the external message ejb implemented in the module. Following the above example project it would be:

      interface.demis.jndiName=java:global/my-message-adapter/MyExternalMessageFacade\n

      After changing the sormas.properties, restart the SORMAS domain then deploy your module.

      "},{"location":"EXTERNAL_MESSAGES_ADAPTER/#deploying-in-your-local-development-environment","title":"Deploying in your local development environment","text":"
      • in your IDE, import the module to your SORMAS-Project
      • make the module deploy on your glassfish server (add it just next to sormas-rest, sormas-ui and sormas-ear war files)

      After that, you should be able to build and deploy SORMAS with your module. If you want to exclude your module from the build, that can be done in IntelliJ as follows: File -> Settings -> Build, Execution, Deployment -> Compiler -> Excludes

      "},{"location":"EXTERNAL_MESSAGES_ADAPTER/#deploying-in-your-payara-domain","title":"Deploying in your payara domain","text":"
      • build the project with maven
      • deploy the built .war file in the sormas domain of your payara server
      "},{"location":"EXTERNAL_MESSAGES_ADAPTER/#testing-the-deployment","title":"Testing the deployment","text":"

      In the SORMAS web interface, go to the About menu, and you must see a green checkmark next to the External message adapter label and your module's version text provided by implementing the ExternalMessageAdapterFacade::getVersion method.

      e.g.:

      "},{"location":"I18N/","title":"SORMAS Translation FAQ","text":""},{"location":"I18N/#who-is-translating-sormas","title":"Who is translating SORMAS?","text":"

      The translation of SORMAS relies on people from the community. If you would like to see SORMAS translated to your language, please read the points below and take part in the process.

      "},{"location":"I18N/#how-is-sormas-translated","title":"How is SORMAS translated?","text":"

      The official SORMAS translation is done using the Crowdin platform: https://crowdin.com/project/sormas

      All translations done on the platform are automatically fed into SORMAS and are part of the bi-weekly release schedule. This means that any text you translate will likely be part of the next SORMAS release. Once your server (or the server of your country) is updated you will be able to see the translations in the web app and mobile app.

      "},{"location":"I18N/#how-to-participate-in-translating-sormas","title":"How to participate in translating SORMAS","text":"

      You can join the translation project by creating a free account on Crowdin. You can also use your existing GitHub account. Before starting to translate, you need to request write access to the project. A member of the project will either accept your request or get in touch with you as soon as possible.

      Afterwards open the SORMAS project https://crowdin.com/project/sormas and select the language you would like to translate.

      You will see all files that contain translatable texts:

      The most important one is captions.properties. It contains the captions for all the data entry fields of SORMAS. The captions are shared by the web app and mobile app.

      Click on the file to open the Crowdin editor that allows you to go through all translation entries one by one as shown in the picture below:

      "},{"location":"I18N/#how-to-add-a-new-language-to-sormas","title":"How to add a new language to SORMAS","text":"

      If the language you would like to translate is not available yet, please get in contact with us: https://github.com/sormas-foundation/SORMAS-Project

      "},{"location":"SECURITY/","title":"Security Policies and Procedures","text":"

      This document outlines security procedures and general policies for the SORMAS project.

      • Security Policies and Procedures
      • Reporting a Security Bug
      • Disclosure Policy
      • Comments on this Policy

      If you want to report a bug which is not security sensible, please submit an issue.

      "},{"location":"SECURITY/#reporting-a-security-bug","title":"Reporting a Security Bug","text":"

      Our team and community take all security bugs in SORMAS seriously. Thank you for improving the security of SORMAS. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. Unfortunately, SORMAS does not offer a paid bug bounty programme or other forms of compensation.

      Report security bugs by emailing at security@sormas.org.

      We will acknowledge your email and follow up with a response within 10 business days, or explain why a reply may take longer. The response will indicate the next steps in handling your report. After the initial reply to your report, the security team will endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.

      Report security bugs in third-party modules to the person or team maintaining the module.

      "},{"location":"SECURITY/#disclosure-policy","title":"Disclosure Policy","text":"

      When the security team receives a security bug report, they will assign it to a primary handler. This person will coordinate the fix and release process, involving the following steps:

      • Confirm the problem and determine the affected versions.
      • Audit code to find any potential similar problems.
      • Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible.
      "},{"location":"SECURITY/#comments-on-this-policy","title":"Comments on this Policy","text":"

      If you have suggestions on how this process could be improved please submit a pull request.

      "},{"location":"SERVER_CUSTOMIZATION/","title":"Configuring and Customizing a SORMAS Server","text":"

      SORMAS has been created and is developed as an international system that can potentially be used everywhere in the world. However, every country naturally has its own requirements and processes, and in addition, there is a set of information that needs to be specified before a SORMAS instance can function properly. For this reason, there are a number of ways in which such a SORMAS instance can be configured and customized:

      • Server Configuration: The basic server configuration is available as a .properties file, needs to be adjusted for every SORMAS instance, and is relevant for both production and development. This should be edited directly after installing the server.
      • Feature Configuration: Most SORMAS features are optional and can be turned on or off directly in the database.
      • Disease Configuration: SORMAS supports a large number of infectious diseases which can be enabled or disabled and further customized directly in the database.
      • Deletion Configuration: SORMAS can be configured to automatically delete entities in the database.
      • Infrastructure Data: Most infrastructure data (except countries and continents) are not shipped with SORMAS because they are country-specific. Importing the infrastructure data of your country (or creating some dummy data) is one of the first things you should do after setting up a SORMAS server.

      Beyond that, the Wiki contains even more customization options:

      • Adding Disease Variants to a SORMAS Server
      • Customizing the Login and About Pages
      • Customizing the Name and Icon of the Android App
      • Creating a SORMAS2SORMAS Certificate
      "},{"location":"SERVER_CUSTOMIZATION/#server-configuration","title":"Server Configuration","text":"

      The general SORMAS configuration is stored in the sormas.properties file that you can find in your payara domain folder. When you set up a new SORMAS server, please make sure to go over all entries in that file and adjust their values if necessary. Each property has an explanation telling you its purpose, and some of them also have a default value that you can use to revert the changes you've made.

      Most of these properties are commented (indicated by a # in front of their name and value), which means that the default value will automatically be used (e.g. the path to temporary files on the server) or the associated feature will not be used at all (e.g. the custom branding properties or the configuration of an external symptom journal). Some properties however are not commented, but also don't have a default value. It is strongly recommended to enter values for these properties because they are required for some parts of SORMAS to work correctly, or because they are very specific to your individual SORMAS instance. This especially applies to the country.locale and country.name properties, which will cause serious problems while using the application if left empty.

      Important: The sormas.properties file contains all properties that existed in the SORMAS version that you initially installed on your server. New properties added in more recent SORMAS versions are not automatically added! If you're operating a SORMAS server, we strongly suggest to read the release notes of new versions. If properties have been added to this file, they will be communicated in these notes so that you can add them to your properties file.

      This Wiki page contains a list and explanation of all currently configurable properties.

      "},{"location":"SERVER_CUSTOMIZATION/#feature-configuration","title":"Feature Configuration","text":"

      Some of the features of SORMAS can be enabled or disabled to further customize the system. Right now, this is only possible directly in the featureconfiguration table in the database. This table contains one entry for every configurable feature and is automatically populated during server startup. Setting the enabled to true or false will enable or disable the feature, respectively. Changes are immediately applied to the running system and don't require a server restart.

      The columns region, district, disease and enddate are currently only applicable for the line listing feature and define the scope in which the line listing is used. Line listing is configurable from the user interface and does not need to be manually edited in the database.

      Important: If you're using the mobile app, you also need to update the changedate to the current date and time whenever you enable or disable a feature! Otherwise the mobile applications will not be notified about the change.

      This Wiki page contains a list and explanation of all currently configurable features.

      "},{"location":"SERVER_CUSTOMIZATION/#disease-configuration","title":"Disease Configuration","text":"

      SORMAS supports a wide range of diseases, and not all of those might be relevant to every SORMAS instance or might be used in a different context. As with features, configuring diseases is currently only possible directly in the database via the diseaseconfiguration table. All diseases have a default value for each of their properties that is applied when the respective database entry is empty. Changing these entries overrides that default value. Unlike with features, disease configurations are cached and therefore require you to restart the server before they are applied.

      Important: If you're using the mobile app, you also need to update the changedate to the current date and time whenever you change a disease configuration! Otherwise the mobile applications will not be notified about the change.

      This Wiki page contains a list and explanation of all currently configurable disease properties.

      "},{"location":"SERVER_CUSTOMIZATION/#deletion-configuration","title":"Deletion Configuration","text":"

      SORMAS can be set up to automatically delete entities after a specific time period. There are seven core entities for which automatic deletion can be enabled and configured: Case, Contact, Event, Event Participant, Immunization, Travel Entry, and Campaign. This configuration is currently only possible directly in the database via the deleteconfiguration table, which already contains rows for each of these entities. The table consists of the following columns:

      • entityType: The name of the entity that supports automatic deletion.
      • deletionReference: The reference date for the calculation of the date on which deletion takes place (see below).
      • deletionPeriod: The number of days after which an entity is deleted, starting with the deletion reference. The minimum is 7.

      Both deletionReference and deletionPeriod need to be filled in order for the automatic deletion to take place. Entities for which at least one of these fields is left empty will not be automatically deleted. Deletion is executed via a nightly cron job and might therefore not happen immediately when the deletion date has been reached.

      "},{"location":"SERVER_CUSTOMIZATION/#deletion-reference","title":"Deletion Reference","text":"

      The deletionReference field has four possible values which define the date that is used to calculate whether an entity needs to be deleted (i.e., when the date calculated by subtracting the deletion period from the current date is before the deletion reference date, the entity is deleted). A MANUAL_DELETION entry can exist in parallel to one of the other entries, and if both entries are configured, deletion is executed as soon as the threshold of one of these entries is met.

      • CREATION: The creation date of the entity will be used.
      • END: The latest change date of the entity itself and any of its depending entities will be used. E.g. for cases, this includes but is not limited to its epi data, symptoms, or hospitalization.
      • ORIGIN: This is currently only implemented for travel entries and means that the arrival date of the entity will be used. If this is specified for any other entity, the deletion job will be stopped and throw an error.
      • REPORT: The report date of the entity will be used. This is currently not implemented for event participants and campaigns.
      • MANUAL_DELETION: The date on which the entity was manually deleted by a user.
      "},{"location":"SERVER_CUSTOMIZATION/#infrastructure-data","title":"Infrastructure Data","text":"

      When you start a SORMAS server for the first time and the createDefaultEntities property is enabled, some default infrastructure data is generated to ensure that the server is usable and the default users can be created. It is recommended (and, unless you're working on a demo server, necessary) to archive this default data and import the official infrastructure data of the country or part of the country that you intend to use SORMAS in instead.

      SORMAS by default splits infrastructure data into four mandatory categories. Starting from the highest administrative division, these are Regions, Districts, Communities, and Health Facilities. In addition, Points of Entry represent places like harbors and airports where people are frequently entering the country, while Laboratories are technically health facilities that are specifically used for sample testing purposes. The Area infrastructure type can be enabled in the feature configuration and adds another optional layer of infrastructure above Regions. Finally, it is possible to add Countries, Subcontinents and Continents to your system if you also want to collect data from outside the country SORMAS is used in (e.g. because you want to record travels or events).

      "},{"location":"SERVER_CUSTOMIZATION/#importing-infrastructure","title":"Importing Infrastructure","text":"

      To import your data for one of the administrative divisions, log in as the default admin user (which is created even when createDefaultEntities is disabled) and open the Configuration menu. Open any of the tabs for the infrastructure data you want to import and click on the Import button on the top right. You can download an import guide from within the popup window that will be opened, containing detailed instructions about the import process and the steps you need to go through in order to successfully import your data.

      Make sure that you always start with the highest administrative division when importing (i.e. at least Countries if you want to collect data from other countries as well, Areas if enabled, or Regions otherwise) and work your way down to the lowest, because lower divisions typically contain mandatory references to higher divisions.

      For Countries, Subcontinents and Continents, SORMAS provides a default import that allows you to automatically add a complete set of data to your system. For Countries, this default data equals to the official list of countries provided by the WHO. For Subcontinents and Continents, the list is based on the data used by the Robert Koch Institut.

      "},{"location":"SERVER_CUSTOMIZATION/#user-role-configuration","title":"User Role Configuration","text":"

      A user is the user account for employees who have access to SORMAS. Similarly, there are technical users that allow external systems to access data. Users have one or sometimes more user roles. The roles of a user can be chosen by an admin when creating a new user or editing an existing one. Each user role is defined by a set of user rights, a jurisdiction that represents the area of responsibility and a set of notifications. For example, the jurisdiction of a surveillance officer is the district, which is defined on the user. User roles that have different areas of responsibility are therefore not combinable.

      SORMAS comes with an extensive list of user rights that are used to check which data and functionality can be access by the user in the backend and the user interface. To cover the typical use cases, SORMAS defines a set of default user roles that are automatically created when setting up a SORMAS instance. Most user rights define an action related to an entity type, e.g. the user right CASE_EDIT allows users to edit case data. Notifications define when the user with the given role should get an SMS and/or Email notification.

      The following automatically generated documents list and describe all user rights, notifications and the default user roles: * https://github.com/sormas-foundation/SORMAS-Project/blob/development/sormas-api/src/main/resources/doc/SORMAS_User_Roles.xlsx

      User roles are fully configurable, allowing admins to create new user roles and edit existing ones, to customize the instance to the given needs and to make sure data protection requirements are fulfilled.

      "},{"location":"SERVER_CUSTOMIZATION/#related-topics","title":"Related topics","text":"
      • Epic that introduced configurable user roles: #898
      • Data access based on user rights and user's jurisdiction: https://github.com/sormas-foundation/SORMAS-Project/blob/development/sormas-backend/doc/UserDataAccess.md
      • Using keycloak as authentication provider: https://github.com/sormas-foundation/SORMAS-Project/blob/development/docs/SERVER_SETUP.md#keycloak-server
      • The available jurisdiction levels (nation, region, district, health facility, etc.) are defined in Java code https://github.com/sormas-foundation/SORMAS-Project/blob/development/sormas-api/src/main/java/de/symeda/sormas/api/user/JurisdictionLevel.java
      "},{"location":"SERVER_DOCKER_SETUP/","title":"Installing a SORMAS Server for development","text":"

      Note: This guide explains how to configure a SORMAS server on Linux and Windows systems for development. Please note that there is no database setup because the script supposes the use of the Docker Postgresql image (see SORMAS-Docker).

      "},{"location":"SERVER_DOCKER_SETUP/#content","title":"Content","text":"
      • Prerequisites
      • Java 11
      • ant
      • Postgres Database
      • SORMAS Server
      "},{"location":"SERVER_DOCKER_SETUP/#related","title":"Related","text":"
      • Installing a SORMAS Server
      "},{"location":"SERVER_DOCKER_SETUP/#prerequisites","title":"Prerequisites","text":""},{"location":"SERVER_DOCKER_SETUP/#java-11","title":"Java 11","text":"

      See Installing Java

      SORMAS just recently moved to Java 11. We still need to support Java 8 for a transition period. Therefore, please just use Java 8 language features for now.

      "},{"location":"SERVER_DOCKER_SETUP/#ant","title":"Ant","text":"

      Download and install Ant, it can be done from Ant site or with packages from your Linux distribution.

      "},{"location":"SERVER_DOCKER_SETUP/#postgres-database","title":"Postgres Database","text":"

      See Installing Postgresql

      Alternatively you can use the Docker image available in SORMAS-Docker repository.

      "},{"location":"SERVER_DOCKER_SETUP/#sormas-server","title":"SORMAS Server","text":"

      Install you own Payara server (see installing SORMAS Server).

      This script will download Payara (if needed) and install SORMAS in the Payara server.

      You can edit this script to change paths and ports.

      Other steps : * IMPORTANT: Adjust the SORMAS configuration for your country in /opt/domains/sormas/sormas.properties * Adjust the logging configuration in ${HOME}/opt/domains/sormas/config/logback.xml based on your needs (e.g. configure and activate email appender) * Build and deploy applications (ear and war) with you IDE.

      "},{"location":"SERVER_DOCKER_SETUP/#keycloak","title":"Keycloak","text":"

      See Keycloak for how to install Docker locally.

      If you are doing active development on Keycloak (themes, authentication mechanisms, translations, etc.) it's recommended to install the standalone variant.

      "},{"location":"SERVER_DOCKER_SETUP/#vaadin-debug-mode","title":"VAADIN Debug Mode","text":"

      To enable VAADIN Debug Mode, go to sormas-ui/src/main/webapp/WEB-INF/web.xml and set productionMode to false. Make sure not to commit your changes to these files, for example by using .gitignore. To access the debug Window, got to /sormas-ui/?debug. You may need to log in as admin once first."},{"location":"SERVER_DOCKER_SETUP/#other-components","title":"Other components","text":"

      See Installing a SORMAS Server

      "},{"location":"SERVER_SETUP/","title":"Installing a SORMAS Server","text":"

      Note: This guide explains how to set up a SORMAS server on Linux and Windows systems, the latter only being intended for usage on development systems. Please also note that certain parts of the setup script will not be executed on Windows.

      "},{"location":"SERVER_SETUP/#content","title":"Content","text":"
      • Installing a SORMAS Server
      • Content
      • Related
      • Prerequisites
        • Java 11
        • Linux
        • Windows
        • Postgres Database
      • SORMAS Server
        • Install on Linux
        • Install on Windows
        • Auditing
        • Sormas installation
      • Keycloak Server
        • Keycloak as a Docker container
        • Keycloak as a standalone installation
        • Connect Keycloak to an already running instance of SORMAS
        • Keycloak configuration
      • Web Server Setup
        • Apache Web Server
        • Firewall
        • Postfix Mail Server
        • Install postfix and mailutils
        • Configure your system
        • Testing the Server Setup
      • R Software Environment
      • SORMAS to SORMAS Certificate Setup
      • Troubleshooting
        • Problem: Login fails
        • Problem: Server is out of memory
      "},{"location":"SERVER_SETUP/#related","title":"Related","text":"
      • Creating an App for a Demo Server
      • SORMAS Docker Repository
      "},{"location":"SERVER_SETUP/#prerequisites","title":"Prerequisites","text":""},{"location":"SERVER_SETUP/#java-11","title":"Java 11","text":"

      Download and install the Java 11 JDK (not JRE) for your operating system. We suggest using the Zulu OpenJDK.

      "},{"location":"SERVER_SETUP/#linux","title":"Linux","text":"
      sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0xB1998361219BD9C9\nsudo apt-add-repository 'deb https://repos.azul.com/zulu/deb/ stable main'\nsudo apt-get update\nsudo apt-get install zulu11\n
      "},{"location":"SERVER_SETUP/#windows","title":"Windows","text":"

      For testing and development environments we suggest to download and run the installer of the Java 11 JDK for 32 or 64 bit client systems (depending on your system). You can check your Java version from the shell/command-line using: java -version.

      "},{"location":"SERVER_SETUP/#postgres-database","title":"Postgres Database","text":"
      • Install PostgreSQL (currently 14 to 15) on your system (manuals for all OS can be found here: https://www.postgresql.org/download)
      • Set max_connections = 288 and max_prepared_transactions = 256 (at least, sum of all connection pools) in postgresql.conf (e.g. /etc/postgresql/14.0/main/postgresql.conf; C:/Program Files/PostgreSQL/14.0/data) - make sure the property is uncommented and restart the service to apply the changes.
      • Install the \"temporal tables\" extension for Postgres (https://github.com/arkhipov/temporal_tables)
      • Windows: Download the latest version for your Postgres version:
        • https://github.com/arkhipov/temporal_tables/releases/latest, then copy the DLL from the project into the PostgreSQL's lib directory and the .sql and .control files into the directory share\\extension.
      • Linux (see https://github.com/arkhipov/temporal_tables#installation) bash sudo apt-get install libpq-dev sudo apt-get install postgresql-server-dev-all sudo apt install pgxnclient #Check for GCC: gcc --version # and install if missing sudo pgxn install temporal_tables # The packages can be removed afterward
        • Add the PostgreSQL path (/etc/PostgreSQL/10/bin) to Environment Variables
      "},{"location":"SERVER_SETUP/#sormas-server","title":"SORMAS Server","text":"

      Get the latest SORMAS build by downloading the ZIP archive from the latest release on GitHub: https://github.com/sormas-foundation/SORMAS-Project/releases/latest

      "},{"location":"SERVER_SETUP/#install-on-linux","title":"Install on Linux","text":"

      Unzip the archive, copy/upload its contents to /root/deploy/sormas/$(date +%F) and make the setup script executable (as root user).

      sudo su\nmkdir -p /root/deploy/sormas\ncd /root/deploy/sormas\nSORMAS_VERSION=1.y.z\nwget https://github.com/sormas-foundation/SORMAS-Project/releases/download/v${SORMAS_VERSION}/sormas_${SORMAS_VERSION}.zip\nunzip sormas_${SORMAS_VERSION}.zip\nmv deploy/ $(date +%F)\nrm sormas_${SORMAS_VERSION}.zip\nchmod +x $(date +%F)/server-setup.sh\n
      "},{"location":"SERVER_SETUP/#install-on-windows","title":"Install on Windows","text":"
      • Download & install Git for Windows. This will provide a bash emulation that you can use to run the setup script: https://gitforwindows.org/
      • Unzip the ZIP archive (e.g. into you download directory)
      • Open Git Bash and navigate to the setup sub-directory
      "},{"location":"SERVER_SETUP/#auditing","title":"Auditing","text":"

      You can configure the audit logger of SORMAS by providing a Logback configuration file and setting the audit.logger.config property accordingly. An example is provided in sormas-base/setup/audit-logback.xml. Not specifying a value for the property will effectively disable the audit log.

      "},{"location":"SERVER_SETUP/#sormas-installation","title":"Sormas installation","text":"
      • Optional: Open server-setup.sh in a text editor to customize the install paths, database access and ports for the server. The default ports are 6080 (HTTP), 6081 (HTTPS) and 6048 (admin). Important: Do not change the name of the database user. The predefined name is used in the statements executed in the database.
      • Set up the database and a Payara domain for SORMAS by executing the setup script: sudo -s ./server-setup.sh Press enter whenever asked for it
      • IMPORTANT: Make sure the script executed successfully. If anything goes wrong you need to fix the problem (or ask for help), then delete the created domain directory and re-execute the script.
      • IMPORTANT: Adjust the SORMAS configuration for your country in /opt/domains/sormas/sormas.properties
      • Adjust the logging configuration in /opt/domains/sormas/config/logback.xml based on your needs (e.g. configure and activate email appender)
      • Linux: Update the SORMAS domain
      "},{"location":"SERVER_SETUP/#keycloak-server","title":"Keycloak Server","text":"

      Note: SORMAS also comes with a basic auth mechanism using an JEE authentication realm. This authentication mechanism should only be used for development environments. For production environments always use the keycloak authentication mechansim. See Authentication & Authorization.

      Keycloak can be set up in two ways: * as a Docker container (for just using Keycloak approach) * as a Standalone installation (for doing development in Keycloak like themes, SPIs)

      "},{"location":"SERVER_SETUP/#keycloak-as-a-docker-container","title":"Keycloak as a Docker container","text":"

      To be done only in the situation when SORMAS is already installed on the machine as a standalone installation.

      For complete Docker setup see the SORMAS-Docker repository.

      Prerequisites * SORMAS Server is installed * PostgreSQL is installed * Docker is installed * Open and edit sormas-base/setup/keycloak/keycloak-setup.sh with your system's actual values (on Windows use Git Bash).

      Setup * Run sormas-base/setup/keycloak/keycloak-setup.sh * Update sormas.properties file in the SORMAS domain with the property authentication.provider=KEYCLOAK

      "},{"location":"SERVER_SETUP/#keycloak-as-a-standalone-installation","title":"Keycloak as a standalone installation","text":"

      Prerequisites * SORMAS Server is installed * PostgreSQL is installed

      Setup

      Setting Keycloak up as a standalone installation as described in the Getting started section of the official guide * Make sure to configure Keycloak with PostgreSQL Database * Set up an Admin User * Copy the themes folder content to ${KEYCLOAK_HOME}/themes as described here * Deploy the sormas-keycloak-service-provider as described here * Update the sormas-base/setup/keycloak/SORMAS.json file by replacing the following placeholders: ${SORMAS_SERVER_URL}, ${KEYCLOAK_SORMAS_UI_SECRET}, ${KEYCLOAK_SORMAS_BACKEND_SECRET}, ${KEYCLOAK_SORMAS_REST_SECRET} * Create the SORMAS Realm by importing sormas-base/setup/keycloak/SORMAS.json see Create a New Realm * Update the sormas-* clients by generating new secrets for them * Update the realm's email settings to allow sending emails to users

      To update the SORMAS Server run the following commands:

      ${ASADMIN} set-config-property --propertyName=payara.security.openid.clientSecret --propertyValue=${KEYCLOAK_SORMAS_UI_SECRET} --source=domain\n${ASADMIN} set-config-property --propertyName=payara.security.openid.clientId --propertyValue=sormas-ui --source=domain\n${ASADMIN} set-config-property --propertyName=payara.security.openid.scope --propertyValue=openid --source=domain\n${ASADMIN} set-config-property --propertyName=payara.security.openid.providerURI --propertyValue=http://localhost:${KEYCLOAK_PORT}/keycloak/auth/realms/SORMAS --source=domain\n${ASADMIN} set-config-property --propertyName=sormas.rest.security.oidc.json \\\n        --propertyValue=\"{\\\"realm\\\":\\\"SORMAS\\\",\\\"auth-server-url\\\":\\\"http://localhost:${KEYCLOAK_PORT}/auth\\\",\\\"ssl-required\\\":\\\"external\\\",\\\"resource\\\":\\\"sormas-rest\\\",\\\"credentials\\\":{\\\"secret\\\":\\\"${KEYCLOAK_SORMAS_REST_SECRET}\\\"},\\\"confidential-port\\\":0,\\\"principal-attribute\\\":\\\"preferred_username\\\",\\\"enable-basic-auth\\\":true}\" \\\n        --source=domain\n${ASADMIN} set-config-property --propertyName=sormas.backend.security.oidc.json --propertyValue=\"{\\\"realm\\\":\\\"SORMAS\\\",\\\"auth-server-url\\\":\\\"http://localhost:${KEYCLOAK_PORT}/auth/\\\",\\\"ssl-required\\\":\\\"external\\\",\\\"resource\\\":\\\"sormas-backend\\\",\\\"credentials\\\":{\\\"secret\\\":\\\"${KEYCLOAK_SORMAS_BACKEND_SECRET}\\\"},\\\"confidential-port\\\":0}\" --source=domain\n

      where: * ${ASADMIN} - represents the location to ${PAYARA_HOME}\\bin\\asadmin * ${KEYCLOAK_PORT} - the port on which keycloak will run * ${KEYCLOAK_SORMAS_UI_SECRET} - is the secret generated in Keycloak for the sormas-ui client * ${KEYCLOAK_SORMAS_REST_SECRET} - is the secret generated in Keycloak for the sormas-rest client * ${KEYCLOAK_SORMAS_BACKEND_SECRET} - is the secret generated in Keycloak for the sormas-backend client

      Then update sormas.properties file in the SORMAS domain with the property authentication.provider=KEYCLOAK

      "},{"location":"SERVER_SETUP/#connect-keycloak-to-an-already-running-instance-of-sormas","title":"Connect Keycloak to an already running instance of SORMAS","text":"

      after setting up Keycloak as one of the described options above

      In case Keycloak is set up alongside an already running instance of SORMAS, these are the steps to follow to make sure already existing users can access the system: 1. Manually create an admin user in Keycloak for the SORMAS realm Creating a user (username has to be the same as admin's username in SORMAS) 2. If your server is not using SSL (e.g. because it's a development environment), you need to update the Root URL of the sormas-ui client in the SORMAS realm to use http instead of https 3. Login to SORMAS and trigger the Sync Users button from the Users page 4. This will sync users to Keycloak keeping their original password - see SORMAS Keycloak Service Provider for more information about this

      "},{"location":"SERVER_SETUP/#keycloak-configuration","title":"Keycloak configuration","text":"

      More about the default configuration and how to customize can be found here Keycloak

      "},{"location":"SERVER_SETUP/#web-server-setup","title":"Web Server Setup","text":""},{"location":"SERVER_SETUP/#apache-web-server","title":"Apache Web Server","text":"

      Note: This is not necessary for development systems. When you are using SORMAS in a production environment you should use a http server like Apache 2 instead of putting the Payara server in the first line. Here are some things that you should do to configure the Apache server as a proxy:

      Activate all needed modules:

      a2enmod ssl\na2enmod rewrite\na2enmod proxy\na2enmod proxy_http\na2enmod headers\n

      Create a new site /etc/apache2/sites-available/your.sormas.server.url.conf (e.g. sormas.org.conf).

      Force SSL secured connections: redirect from http to https:

      <VirtualHost *:80>\n        ServerName your.sormas.server.url\n        RewriteEngine On\n        RewriteCond %{HTTPS} !=on\n        RewriteRule ^/(.*) https://your.sormas.server.url/$1 [R,L]\n</VirtualHost>\n<IfModule mod_ssl.c>\n<VirtualHost *:443>\n        ServerName your.sormas.server.url\n        ...\n</VirtualHost>\n</IfModule>\n

      Configure logging:

      ErrorLog /var/log/apache2/error.log\nLogLevel warn\nLogFormat \"%h %l %u %t \\\"%r\\\" %>s %b _%D_ \\\"%{User}i\\\"  \\\"%{Connection}i\\\"  \\\"%{Referer}i\\\" \\\"%{User-agent}i\\\"\" combined_ext\nCustomLog /var/log/apache2/access.log combined_ext\n

      SSL key config:

      SSLEngine on\nSSLCertificateFile    /etc/ssl/certs/your.sormas.server.url.crt\nSSLCertificateKeyFile /etc/ssl/private/your.sormas.server.url.key\nSSLCertificateChainFile /etc/ssl/certs/your.sormas.server.url.ca-bundle\n\n# disable weak ciphers and old TLS/SSL\nSSLProtocol all -SSLv3 -TLSv1 -TLSv1.1\nSSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE$\nSSLHonorCipherOrder off\n

      Add a proxy pass to the local port:

      ProxyRequests Off\nProxyPass /sormas-ui http://localhost:6080/sormas-ui\nProxyPassReverse /sormas-ui http://localhost:6080/sormas-ui\nProxyPass /sormas-rest http://localhost:6080/sormas-rest\nProxyPassReverse /sormas-rest http://localhost:6080/sormas-rest\n

      Configure security settings:

      Header always set X-Content-Type-Options \"nosniff\"\nHeader always set X-Xss-Protection \"1; mode=block\"\n# Disable Caching\nHeader always set Cache-Control \"no-cache, no-store, must-revalidate, private\"\nHeader always set Pragma \"no-cache\"\n\nHeader always set Content-Security-Policy \\\n        \"default-src 'none'; \\\n        object-src 'self'; \\\n        script-src 'self' 'unsafe-inline' 'unsafe-eval'; \\\n        connect-src https://fonts.googleapis.com https://fonts.gstatic.com 'self'; \\\n        img-src *; \\\n        style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; \\\n        font-src https://fonts.gstatic.com 'self'; \\\n        frame-src 'self'; \\\n        worker-src 'self'; \\\n        manifest-src 'self'; \\\n        frame-ancestors 'self'\n\n# The Content-Type header was either missing or empty.\n# Ensure each page is setting the specific and appropriate content-type value for the content being delivered.\nAddType application/vnd.ms-fontobject    .eot\nAddType application/x-font-opentype      .otf\nAddType image/svg+xml                    .svg\nAddType application/x-font-ttf           .ttf\nAddType application/font-woff            .woff\n

      Activate output compression (very important!):

      <IfModule mod_deflate.c>\n        AddOutputFilterByType DEFLATE text/plain text/html text/xml\n        AddOutputFilterByType DEFLATE text/css text/javascript\n        AddOutputFilterByType DEFLATE application/json\n        AddOutputFilterByType DEFLATE application/xml application/xhtml+xml\n        AddOutputFilterByType DEFLATE application/javascript application/x-javascript\n        DeflateCompressionLevel 1\n</IfModule>\n

      Provide the Android apk:

      Options -Indexes\nAliasMatch \"/downloads/sormas-(.*)\" \"/var/www/sormas/downloads/sormas-$1\"\n

      For the Apache 2 security configuration we suggest the following settings (/etc/apache2/conf-available/security.conf):

      ServerTokens Prod\nServerSignature Off\nTraceEnable Off\n\nHeader always set Strict-Transport-Security \"max-age=15768000; includeSubDomains; preload\"\nHeader unset X-Frame-Options\nHeader always set X-Frame-Options SAMEORIGIN\nHeader unset Referrer-Policy\nHeader always set Referrer-Policy \"same-origin\"\nHeader edit Set-Cookie \"(?i)^((?:(?!;\\s?HttpOnly).)+)$\" \"$1;HttpOnly\"\nHeader edit Set-Cookie \"(?i)^((?:(?!;\\s?Secure).)+)$\" \"$1;Secure\"\n\nHeader unset X-Powered-By\nHeader unset Server\n
      • In case you need to update the site config while the server is running, use the following command to publish the changes without the need for a reload:
      apache2ctl graceful\n
      "},{"location":"SERVER_SETUP/#firewall","title":"Firewall","text":"
      • The server should only publish the ports that are needed. For SORMAS this is port 80 (HTTP) and 443 (HTTPS). In addition you will need the SSH port to access the server for admin purposes.
      • We suggest to use UFW (Uncomplicated Firewall) which provides a simple interface to iptables:
      sudo apt-get install ufw\nsudo ufw default deny incoming\nsudo ufw default allow outgoing\nsudo ufw allow ssh\nsudo ufw allow http\nsudo ufw allow https\nsudo ufw enable\n
      "},{"location":"SERVER_SETUP/#postfix-mail-server","title":"Postfix Mail Server","text":""},{"location":"SERVER_SETUP/#install-postfix-and-mailutils","title":"Install postfix and mailutils","text":"
      apt install aptitude\naptitude install postfix\n-> choose \"satelite system\"\napt install mailutils\n
      "},{"location":"SERVER_SETUP/#configure-your-system","title":"Configure your system","text":"
      nano /etc/aliases\n-> add \"root: enter-your@support-email-here.com\"\nnano /opt/domains/sormas/config/logback.xml\n-> make sure \"EMAIL_ERROR\" appender is active and sends out to your email address\n
      "},{"location":"SERVER_SETUP/#testing-the-server-setup","title":"Testing the Server Setup","text":"

      Use SSL Labs to test your server security config: https://www.ssllabs.com/ssltest

      "},{"location":"SERVER_SETUP/#r-software-environment","title":"R Software Environment","text":"

      In order to enable disease network diagrams in the contact dashboard, R and several extension packages are required. Then the Rscript executable has to be configured in the sormas.properties file. This can be conveniently accomplished by executing the R setup script from the SORMAS ZIP archive (see SORMAS Server):

      • If the SORMAS installation has been customized, r-setup.sh the install paths may have to be adjusted accordingly with a text editor.
      • Execute R setup script and follow its instructions.
      chmod +x r-setup.sh\n./r-setup.sh\n
      "},{"location":"SERVER_SETUP/#sormas-to-sormas-certificate-setup","title":"SORMAS to SORMAS Certificate Setup","text":"

      To be able to communicate with other SORMAS instances, there are some additional steps which need to be taken, in order to set up the certificate and the truststore. Please see the related guide for detailed instructions regarding SORMAS to SORMAS setup.

      "},{"location":"SERVER_SETUP/#troubleshooting","title":"Troubleshooting","text":""},{"location":"SERVER_SETUP/#problem-login-fails","title":"Problem: Login fails","text":"

      Check that the users table does have a corresponding entry. If not, the database initialization that is done when deploying sormas-ear.ear probably had an error.

      "},{"location":"SERVER_SETUP/#problem-server-is-out-of-memory","title":"Problem: Server is out of memory","text":"

      Old servers were set up with a memory size of less than 2048MB. You can change this using the following commands:

      /opt/payara-172/glassfish/bin/asadmin --port 6048 delete-jvm-options -Xmx512m\n/opt/payara-172/glassfish/bin/asadmin --port 6048 delete-jvm-options -Xmx1024m\n/opt/payara-172/glassfish/bin/asadmin --port 6048 create-jvm-options -Xmx2048m\n

      Alternative: You can edit the settings directly in the domain.xml in the config directory of the SORMAS domain. Just search for Xmx - there should be two entries that need to be changed.

      "},{"location":"SERVER_UPDATE/","title":"Updating a SORMAS Server","text":"

      SORMAS releases starting from 1.21.0 contain a script that automatically updates and deploys the server. If you are using an older version and therefore need to do a manual server update, please download the 1.21.0 release files and use the commands specified in the server-update.sh script.

      "},{"location":"SERVER_UPDATE/#preparations","title":"Preparations","text":"
      • Get the latest release files (deploy.zip) from https://github.com/sormas-foundation/SORMAS-Project/releases/latest
      • Unzip the archive and copy/upload its contents to /root/deploy/sormas/$(date +%F) bash cd /root/deploy/sormas SORMAS_VERSION=1.y.z wget https://github.com/sormas-foundation/SORMAS-Project/releases/download/v${SORMAS_VERSION}/sormas_${SORMAS_VERSION}.zip unzip sormas_${SORMAS_VERSION}.zip mv deploy/ $(date +%F) rm sormas_${SORMAS_VERSION}.zip
      "},{"location":"SERVER_UPDATE/#breaking-updates","title":"Breaking Updates","text":"

      The following is a list of version that have breaking changes in the update. You only have to consider this if you are coming from an earlier version. For fresh installs, this is not relevant.

      "},{"location":"SERVER_UPDATE/#1670","title":"1.67.0","text":"

      Coming from 1.66.4 or earlier, you have to update the payara server, as explained in the Payara migration.

      "},{"location":"SERVER_UPDATE/#1730","title":"1.73.0","text":"

      Deploying this release will clear the userrolesconfig and userrole_userrights tables and overwrite them with the default user role configurations of SORMAS. If you added entries to these tables in order to customize the user roles on your system, please run the following queries before deploying this release in order to prevent data loss:

      -- Retrieve all customized roles\nSELECT * FROM userrolesconfig;\n\n-- Overridden rights for roles\nSELECT c.userrole, ur.userright FROM userroles_userrights ur LEFT JOIN userrolesconfig c (ON c.id = ur.userrole_id);\n

      After deploying the new version, the information retrieved from these queries can be used to alter the new user role configurations accordingly.

      "},{"location":"SERVER_UPDATE/#1810","title":"1.81.0","text":"

      The temporal tables extension is replaced during the deployment of the backend. As a preparation the following SQL needs to be executed on the SORMAS database using the postgres user.

      -- versioning function will be replaced during server backend startup\nALTER FUNCTION public.versioning() OWNER TO sormas_user;\n

      After the successful deployment, you can run the following SQL to get rid of the no longer used extension:

      -- needed because `ALTER FUNCTION ... NO DEPENDS ON EXTENSION` is not possible in Postgres 10 and below\nDELETE FROM pg_depend\nWHERE deptype = 'e'\n  AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'temporal_tables')\n  and objid = (SELECT oid FROM pg_proc WHERE proname = 'versioning');\n\nDROP EXTENSION IF EXISTS temporal_tables;\n
      "},{"location":"SERVER_UPDATE/#1850","title":"1.85.0","text":"

      Payara is updated from 5.2021.10 to 5.2022.5. If you are not using SORMAS-Docker, please follow the Payara migration guide.

      "},{"location":"SERVER_UPDATE/#1890","title":"1.89.0","text":"

      When installing this version on new systems or upgrading postgres version on Ubuntu make sure you have at least Ubuntu LTS 20 with postgres 14 (or 18 with postgres 12 from APT).

      "},{"location":"SERVER_UPDATE/#automatic-server-update","title":"Automatic Server Update","text":"
      • Navigate to the folder containing the unzipped deploy files: cd /root/deploy/sormas/$(date +%F)
      • Make the update script executable: chmod +x server-update.sh
      • Optional: Open server-update.sh in a text editor to customize the values for e.g. the domain path or the database name. You only need to do this if you used custom values while setting up the server.
      • Execute the update script and follow the instructions: ./server-update.sh
      • If anything goes wrong, open the latest update log file (by default located in the \"update-logs\" folder in the domain directory) and check it for errors.
      "},{"location":"SERVER_UPDATE/#restoring-the-database","title":"Restoring the Database","text":"

      If anything goes wrong during the automatic database update process when deploying the server, you can use the following command to restore the data:

      pg_restore --clean -U postgres -Fc -d sormas_db sormas_db_....dump

      "},{"location":"SERVER_UPDATE/#default-logins","title":"Default Logins","text":"

      These are the default users for most user roles, intended to be used on development or demo systems. In all cases except the admin user, the username and password are identical. Make sure to deactivate them or change the passwords on productive systems.

      "},{"location":"SERVER_UPDATE/#admin","title":"Admin","text":"

      Username: admin Password: sadmin

      "},{"location":"SERVER_UPDATE/#web-users","title":"Web users","text":"

      Surveillance Supervisor: SurvSup Case Supervisor: CaseSup Contact Supervisor: ContSup Point of Entry Supervisor: PoeSup Laboratory Officer: LabOff Event Officer: EveOff National User: NatUser National Clinician: NatClin

      "},{"location":"SERVER_UPDATE/#mobile-app-users","title":"Mobile app users","text":"

      Surveillance Officer: SurvOff Hospital Informant: HospInf Point of Entry Informant: PoeInf

      "},{"location":"SERVER_UPDATE/#updating-keycloak","title":"Updating Keycloak","text":""},{"location":"SERVER_UPDATE/#standalone-installation","title":"Standalone installation","text":"

      Follow the official Keycloak upgrade guide.

      To update follow this steps:

      1. Prerequisites
      2. Backup the DB
      3. Backup the current Keycloak configuration
      4. Download the 18.0.1 zip from https://www.keycloak.org/downloads
      5. Extract everything from the archive somewhere on your disk (will call this KEYCLOAK_HOME_NEW)

      6. From you current installation (will call this KEYCLOAK_HOME_OLD) directory copy the following into the new installation

      7. Copy directory KEYCLOAK_HOME_OLD/themes/sormas over to KEYCLOAK_HOME_NEW/themes
      8. Copy KEYCLOAK_HOME_OLD/providers/sormas-keycloak-service-provider-*.jar over to KEYCLOAK_HOME_16/providers

      9. Setup Keycloak to use the Database

      10. Start Keycloak
      11. Database will be migrated automatically
      "},{"location":"SERVER_UPDATE/#docker-installation","title":"Docker installation","text":"

      The docker installation is automatically upgraded to the latest version specified in the Dockerfile.

      Prerequisites: Make sure the DB is backed up, because once the upgrade is done the new DB won't be usable with the old version of Keycloak.

      For more info see the Keycloak Docker Documentation.

      "},{"location":"SERVER_UPDATE/#how-to-migrate-to-new-payara-server","title":"How to migrate to new Payara Server","text":""},{"location":"SERVER_UPDATE/#step-1-shutdown-and-backup-existing-domain","title":"Step 1: Shutdown and backup existing domain","text":"
      # Stop domain\nservice payara-sormas stop\n\n# Move (backup) existing domain\nDOMAIN_PATH=/opt/domains\nDOMAIN_NAME=\"sormas\"\nDOMAIN_BACKUP_NAME=\"sormas_backup\"\nmv $DOMAIN_PATH/$DOMAIN_NAME $DOMAIN_PATH/$DOMAIN_BACKUP_NAME\n
      "},{"location":"SERVER_UPDATE/#step-2-setup-payara-domain","title":"Step 2: Setup Payara domain","text":"

      Please follow the server setup: Create the payara domain under the same path as before, use the same directory paths and the same database settings.

      "},{"location":"SERVER_UPDATE/#step-3-apply-your-config-file-changes","title":"Step 3: Apply your config file changes","text":"

      Transfer your settings from sormas.properties, logback.xml or changes in the domain setup. Use the new provided files and copy your changes in, don't reuse old files!

      "},{"location":"SERVER_UPDATE/#step-4-install-new-sormas-version","title":"Step 4: Install new SORMAS version","text":"

      To install the new SORMAS version in the Payara domain, proceed with the automatic update or for developers: Deploy SORMAS via the IDE as usual.

      "},{"location":"SERVER_UPDATE/#alternative-for-development-systems","title":"Alternative for development systems","text":"

      For minor updates of the payara version, you will most often be able to keep the existing domain and only replace the payara server.

      1. Download the needed version of Payara server.
      2. Undeploy all sormas modules from the payara domain and stop the domain.
      3. Replace your payara server in /opt/payara5 (default path) with the downloaded one. Remove the default domain in opt/payara5/glassfish/domains as it is not needed.
      4. Replace the application server in your IDE with the new server. See IDE setup guide
      5. If you are facing any problems, restart your IDE and clean all generated files from the sormas domain.
      "},{"location":"SOP_DISEASES/","title":"SOP for Adding New Diseases to SORMAS","text":"

      This file defines the SOP (Standard Operating Procedure) that should be followed when requesting new diseases to be added to the system by the core development team. Answering all the questions asked in this guide will make sure that we will be able to integrate new diseases into SORMAS as quickly as possible.

      "},{"location":"SOP_DISEASES/#content","title":"Content","text":"
      • SOP for Adding New Diseases to SORMAS
      • Content
      • Guide
        • Step 1: Download the Data Dictionary
        • Step 2: Define Basic Disease Details
        • Step 3: Define Existing Case Fields
        • Step 4: Define Existing Person Fields
        • Step 5: Define the Relevant Symptoms
        • Step 6: Define the Relevant Epidemiological Data
        • Step 7: Define Health Conditions
        • Step 8: Define New Fields in Other Areas
        • Step 9: Provide Case Classification Criteria
        • Step 10: Provide Additional Information
        • Step 11: Send Your Disease Definition to the SORMAS Team
      "},{"location":"SOP_DISEASES/#guide","title":"Guide","text":""},{"location":"SOP_DISEASES/#step-1-download-the-data-dictionary","title":"Step 1: Download the Data Dictionary","text":"

      Download the latest Data Dictionary from this repository and open it. Please never use a version of the Data Dictionary that you downloaded earlier as it is very likely that its contents have changed in the meantime.

      You will use the Data Dictionary to define all the details of the new disease. Please make sure to mark every addition or change (e.g. by colorizing the text or background of the row in a subtle red) so we don't miss any of the information you have provided.

      "},{"location":"SOP_DISEASES/#step-2-define-basic-disease-details","title":"Step 2: Define Basic Disease Details","text":"

      Open the Case tab of the Data Dictionary and scroll down to the tables that have a blue background. These tables define enumerations, which are basically data types with fixed values. Examples include the different case classifications, the gender of a person or the diseases that are used in SORMAS. Find the Disease enumeration table (refer to the Type column) and add a new row to it. Enter the following details:

      • The name of the disease in the Caption column
      • Optionally, if the disease has a long name, a short name or abbreviation in the Short column

      Use the Description column to answer the following question(s):

      • Does the disease have contact follow-up?
      • If yes, for how many days should contact follow-up be done?
      "},{"location":"SOP_DISEASES/#step-3-define-existing-case-fields","title":"Step 3: Define Existing Case Fields","text":"

      Look through the rows in the first table of the Case tab (which has a grey background). This table defines all the fields that are displayed in the Case Information tab in the SORMAS application. The Caption column defines the name of the field as it is displayed in the user interface, while the Diseases column specifies which diseases use this field. Please add the name (or, if available, short name) of your new disease to the \"New disease\" column of every row that represents a field that is relevant for it and colorize it.

      "},{"location":"SOP_DISEASES/#step-4-define-existing-person-fields","title":"Step 4: Define Existing Person Fields","text":"

      Open the Person tab and repeat step 3 for the first table containing the fields that define the details of a person in SORMAS.

      "},{"location":"SOP_DISEASES/#step-5-define-the-relevant-symptoms","title":"Step 5: Define the Relevant Symptoms","text":"

      Open the Symptoms tab which lists all the symptoms that are currently used in SORMAS. This is a very long list and you will have to go through every single row and define whether this symptom should be tracked for your new disease or not.

      It's possible that your new disease uses one or more symptoms that are currently not part of SORMAS. In that case, you need to add a new row for each of these symptoms to the bottom of the table and provide the name of the symptom in the Caption column.

      Most symptoms in SORMAS are simple Yes/No/Unknown fields where Yes means that the symptom is present, No that the symptom is not present and Unknown that there is no information about whether the symptom is present or not. If your symptom can simply be defined by this pattern, you don't have to specify anything else. However, if your symptom is more complex (e.g. there are a number of predefined values that the user should choose from), please provide all the necessary details about how the symptom should be specified by users in the Description column.

      "},{"location":"SOP_DISEASES/#step-6-define-the-relevant-epidemiological-data","title":"Step 6: Define the Relevant Epidemiological Data","text":"

      Open the Epidemiological data tab which lists all fields that are used to collect information about the epidemiological background of the case, e.g. whether they visited burials, had contact with a confirmed case or animals. Repeat step 3 for all rows in the first table, and add new rows if your new disease requires information that is not currently collected within SORMAS. As new fields in this tab are likely to be more complex than basic symptoms, make sure to define as much information about how they should function in the Description column.

      "},{"location":"SOP_DISEASES/#step-7-define-health-conditions","title":"Step 7: Define Health Conditions","text":"

      Open the Health conditions tab which contains a list of pre-existing conditions that are not symptoms of the disease, but are still relevant especially for case management purposes in a hospital. Repeat step 3 for all rows in the first table, and add new rows if there are health conditions relevant for your new disease that are not part of SORMAS yet. As always with new fields, make sure to provide all relevant details in the Description column.

      "},{"location":"SOP_DISEASES/#step-8-define-new-fields-in-other-areas","title":"Step 8: Define New Fields in Other Areas","text":"

      It is possible that your disease requires further information to be collected that is not supported by SORMAS yet, e.g. new details about the person, specific information about its hospitalization, or even very important fields that should directly go into the case information. You can use the same process you used to define new symptoms, health conditions or epidemiological data fields by opening the tab in question and adding new rows to the topmost table.

      At this point, you have finished all the necessary definitions in the Data Dictionary. Save your work and prepare an email with the Data Dictionary file attached to it. Don't send this email before working through the remaining steps though, as there are still a few details that are needed in order to finish the specification of your new disease.

      "},{"location":"SOP_DISEASES/#step-9-provide-case-classification-criteria","title":"Step 9: Provide Case Classification Criteria","text":"

      Optimally, when defining a new disease, you should also specify the criteria SORMAS should use to automatically classify the case as suspect, probable or confirmed. In order to do this in a way that is compatible with the system we use, you will need access to a running SORMAS system (e.g. the play server you can find at https://sormas.org). Log in as any user (e.g. the default user on the play server), open the About section from the main menu, and open the Case Classification Rules (HTML) document. Please define the classification criteria in a way that is similar to the system used in this document. If available, you can also send us an official document by WHO or your national CDC that specifies the classification criteria.

      "},{"location":"SOP_DISEASES/#step-10-provide-additional-information","title":"Step 10: Provide Additional Information","text":"

      If there are still things that are necessary in order to properly implement the new disease in SORMAS (you might require us to create a whole new area for cases or there might be very complex mechanics that need a lot more specification), please give us as many details about them as possible. Just put all this information into your email.

      "},{"location":"SOP_DISEASES/#step-11-send-your-disease-definition-to-the-sormas-team","title":"Step 11: Send Your Disease Definition to the SORMAS Team","text":"

      Send your email containing the updated Data Dictionary file, the case classification criteria and your additional notes to sormas@helmholtz-hzi.de. Congratulations, your work is done! We should now have all the information we need in order to integrate your disease into SORMAS. If there is anything that is unclear or if we need additional details, we will get in touch with you as soon as possible. Thank you so much for contributing to SORMAS and helping us to fight the spread of as many diseases as possible!

      "},{"location":"SORMAS2SORMAS/","title":"Sormas2Sormas Setup","text":""},{"location":"SORMAS2SORMAS/#introduction","title":"Introduction","text":"

      Sormas2Sormas (or S2S for short) is a feature that allows sharing of entities (e.g., cases, contacts, events, etc.) between two or more SORMAS instances.

      "},{"location":"SORMAS2SORMAS/#components","title":"Components","text":""},{"location":"SORMAS2SORMAS/#sormas-instance","title":"SORMAS Instance","text":"

      A SORMAS instance is a running instance of the SORMAS application. A SORMAS instance which is configured to participate in S2S is obviously the main component of the Sormas2Sormas feature. It is the one that initiates the sharing process and the one that receives the shared entities. Each instance has a unique S2S identifier which is an arbitrary string. We do not enforce a certain scheme, but it is strongly encouraged to use a meaningful and consistent identifiers (e.g., 2.sormas.id.sormas_a in Germany which is the dedicated SurvNet ID for the public health office).

      "},{"location":"SORMAS2SORMAS/#server-descriptors","title":"Server Descriptors","text":"

      A server descriptor is a struct which contains the information needed to connect to a SORMAS instance (see the SormasServerDescriptor class). It consists of the S2S ID, a human-readable name used in the UI, and the hostname. Descriptors centrally distributed via a dedicated etcd keyspace (e.g., /s2s/).

      {\n  \"id\":\"2.sormas.id.sormas_a\",\n  \"name\":\"sormas_a_org_name\",\n  \"hostName\":\"sormas_a:6080\"\n}\n
      "},{"location":"SORMAS2SORMAS/#keystore","title":"Keystore","text":"

      Each instance receives a dedicated x509 certificate and private key pair which is used to encrypt all S2S communication. The certificate is signed by a common S2S CA. The certificate and private key are stored in a pkcs12 keystore under the S2S ID of the instance. See here for an example of how to generate a keystore.

      "},{"location":"SORMAS2SORMAS/#truststore","title":"Truststore","text":"

      Each instance receives a truststore which contains the S2S CA certificate under the sormas2sormas.rootCaAlias. See here for an example of how to generate a truststore.

      "},{"location":"SORMAS2SORMAS/#properties","title":"Properties","text":"

      The following properties are required to be set in the sormas.properties file:

      Property Description central.oidc.url URL of the OIDC server authenticating instances and authorizing S2S requests. central.etcd.host= The hostname of the etcd instance providing data like server descriptors. central.etcd.clientName The client name of the instance used in authentication towards etcd. central.etcd.clientPassword The client password of the instance used in authentication towards etcd. central.etcd.caPath The full filesystem path to the CA trusted by etcd clients when creating a TLS connection to the etcd server. central.location.sync If set to true, all infrastructure data from the central server will be synchronized into the local SORMAS database at (re)start and on a daily basis. sormas2sormas.path Filesystem path to a directory where certificates and files related to SORMAS2SORMAS are stored. sormas2sormas.id= The S2S ID of this instance. The identifier can be arbitrary, but should be meaningful. sormas2sormas.keystoreName Name of the key store file relative to sormas2sormas.path. sormas2sormas.keystorePass Password of the key store file. sormas2sormas.rootCaAlias The alias of the trusted root CA which secures the S2S communication. This is NOT used for TLS. sormas2sormas.truststoreName Name of the truststore file relative to sormas2sormas.path. sormas2sormas.truststorePass Password of the truststore. sormas2sormas.oidc.realm Name of our S2S authorization realm provided by the OIDC server. sormas2sormas.oidc.clientId The client ID used in authentication of the instance towards the OIDC server. sormas2sormas.oidc.clientSecret The client secret used in authorization of the instance towards the OIDC server. sormas2sormas.etcd.keyPrefix The etcd key space prefix which is used to store s2s related information. sormas2sormas.ignoreProperty.additionalDetails Controls whether the value in additionalDetails field should be sent or not to the other instances. Possible values: true/false(default). sormas2sormas.ignoreProperty.externalId Controls whether the value in externalId field should be sent or not to the other instances. Possible values: true/false(default). sormas2sormas.ignoreProperty.externalToken Controls whether the value in externalToken field should be sent or not to the other instances. Possible values: true/false(default). sormas2sormas.ignoreProperty.internalToken Controls whether the value in internalToken field should be sent or not to the other instances. Possible values: true/false(default). #sormas2sormas.districtExternalId External ID of the district to which the cases/contacts to be assigned when accepting a share request"},{"location":"SORMAS2SORMAS/#keycloak","title":"Keycloak","text":"

      Keycloak is an open source identity and access management solution. It is used to manage the users and their permissions. In our case, we use the OIDC client credentials flow to authenticate the S2S enabled instances and to authorize the S2S share operations. It consists of a dedicated S2S realm which contains all the necessary configurations, namely dedicated clients and client scopes. Connections to Keycloak must be protected by TLS.

      "},{"location":"SORMAS2SORMAS/#clients","title":"Clients","text":"

      Each S2S instance receives a dedicated client in the S2S realm. An S2S instance is acting as client and is configured to use the OIDC client credentials.

      "},{"location":"SORMAS2SORMAS/#client-scopes","title":"Client Scopes","text":"

      For each client, a similar client scope is created. Each S2S instance A which should be able to send data to another instance B must be assigned the client scope of instance B. This way, instance A is allowed to send data to instance B. The client scope must be prefixed with s2s- and must contain the S2S ID of the instance it is assigned to (e.g., s2s-2.sormas.id.sormas_b).

      "},{"location":"SORMAS2SORMAS/#etcd","title":"etcd","text":""},{"location":"SORMAS2SORMAS/#server-descriptors-stored-in-etcd","title":"Server Descriptors stored in etcd","text":"

      etcd is a distributed key-value store. It is used to store the server descriptors of all S2S enabled instances. They are all stored under a dedicated S2S key space prefix (e.g., /s2s/) at a path like /s2s/2.sormas.id.sormas_b. The etcd server is expected to be secured by TLS with a trusted CA announced to instances via central.etcd.caPath. The etcd client name and password are used to authenticate the S2S instances towards the etcd server.

      "},{"location":"SORMAS2SORMAS/#infrastructure-data","title":"Infrastructure Data","text":"

      etcd is also used to store the infrastructure data of the central server. This data is used to synchronize the local DB to have a consistent view of the infrastructure data. The infrastructure data is stored under a dedicated key space of the form /central/location/${entity} where entity is one of continent, subcontinent, country, region, district, and community. A complete key could look like /central/location/continent/3fab2b69-31cd-5765-91e3-d68826a6dd48 where the uuid is the uuid assigned to the continent in the central server. The values are expected to mirror the JSON values of the DTOs (e.g., ContinentDto, etc.).

      "},{"location":"SORMAS2SORMAS/#secure-communication","title":"Secure Communication","text":"

      The S2S communication is expected to be secured by TLS. For additional security, the S2S communication is also encrypted. Each instance receives a dedicated keystore and truststore. The keystore contains the instance's private and public key. The truststore contains a root CA certificate (i.e., sormas2sormas.rootCaAlias) which is used to sign all instance certificates. The connection to Keycloak must be secured by TLS. The connection to etcd must be secured by TLS. The CA trusted by etcd clients when creating a TLS connection to the server is set via central.etcd.caPath.

      "},{"location":"SORMAS2SORMAS/#deactivation","title":"Deactivation","text":"
      1. Stop the instance and create backup
      2. The instance on which S2S will be deactivated has to revoke all outgoing pending requests AND has to accept or reject all incoming pending request. Please note that as of now, users won't be able to see the whole sharebox or share directory. Therefore, they cannot track if the case/contact was shared with/by another instance or not.
      3. Deactivate the S2S feature by commenting all relevant properties in sormas.properties:
      4. sormas2sormas.*
      5. central.oidc.url
      6. Optional if GA wants to disable central infra sync: central.*. Strongly discouraged.
      7. Remove service descriptor of the deactivated instance from central etcd to prevent further discovery.
      8. Reset feature configuration table.
      9. Remove client and client scope of the instance from central Keycloak.
      10. shred the instance's key material.
      11. Restart the instance.
      "},{"location":"TROUBLESHOOTING/","title":"Troubleshooting","text":"

      Please consult this collection of solutions to common problems if you have any issues before issuing a support request or asking developers for help. Also note that this resource has only been added recently and will be extended in the future. If you have encountered (and fixed) any issue that you think would be worth adding to this list, please don't hesitate to let us know!

      • Android Application FAQ
      • Identify Performance Problems
      • Log Slow Database Queries
      • Create Analysis of Slow Database Query (SORMAS-Docker)
      • Log Slow Java Code
      • Analyze Performance Problems
      • Analyze a Slow Query
      • Analyze Java Code Performance Logs
      • IDE Troubleshooting: Android Studio
      • IDE Troubleshooting: eclipse
      • Deployment Problems
      • News Feeds Polling
      • Redeployment problems
      • Malware detection triggers
      "},{"location":"TROUBLESHOOTING/#android-application-faq","title":"Android Application FAQ","text":"

      Q: I don't see a logout option anywhere in the mobile app. How can I change my user? A: The logout option is hidden by default because users in the field often don't know their own passwords, but their devices are instead set up by a supervisor. If you want to change your user, go to the Settings screen and tap the version number five times to bring up additional options, including the logout option.

      Q: The app crashes. How can I get a log file? A: If you are using a release version of the app and need to get error logs, you can do the following:

      1. Enable developer options in the Android device's settings
      2. Use the \"Take Bug Report\" option. The full report is not needed.
      3. The zip file that is created will have a dumpstate-.txt file that contains the log and some more information
      4. Open it and search for de.symeda.sormas to identify the process ID. E.g. de.symeda.sormas.app/de.symeda.sormas.app.login.LoginActivity$_11109#0 -> 11109 is the ID
      5. Search for all occurences of the process ID to filter the file down to lines that contain the actual log of sormas
      6. "},{"location":"TROUBLESHOOTING/#identify-performance-problems","title":"Identify Performance Problems","text":"

        There are two main sources of bad performance in the application:

        1. Slow database queries
        2. Slow Java code
        "},{"location":"TROUBLESHOOTING/#log-slow-database-queries","title":"Log Slow Database Queries","text":"

        Possibly the most generally useful log setting for troubleshooting performance, especially on a production server.

        SORMAS-Docker already logs slow SQL queries by default. You can view the log output on its host VM with docker logs sormas-docker_postgres_1.

        You can enable the logging of slow SQL queries in your PostgreSQL server in postgresql.conf:

        1. Change the value of log_min_duration_statement to a value that fits your need (e.g. 10000).
        2. Restart the PostgreSQL service or reload the config.
        3. Monitor the log file.
        "},{"location":"TROUBLESHOOTING/#create-analysis-of-slow-database-query-sormas-docker","title":"Create Analysis of Slow Database Query (SORMAS-Docker)","text":"

        You can provide an analysis of a slow running query to help the developers to see where the query is getting slow and how to fix it.

        1. Extract the slow SQL statement from slow query log.

        2. Copy the SQL statement, replace all parameters ($x) with the values (see the following log statement) and place the SQL query on the system (outside Docker container on host):

        sudo bash\ncd /var/lib/docker/psqldata\nvi explain.sql\n\n# hit i (INSERT)\n# Paste this into the file:  EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON) \n# Paste the complete SQL statement\n# Hit ESC and :wq to save the file\n
        1. Execute the SQL (inside Docker container):
        sudo bash\ndocker exec -ti sormas-docker_postgres_1 bash\ncd /var/lib/postgresql/data\nsu postgres\npsql -XqAt -d sormas -f explain.sql > analyze.json\n
        1. Copy the output to your home dir on the VM (not inside the Docker container) to be able to copy it from the VM to your local system: mv analyze.json /home/user.name/

        2. Create a visual report at https://explain.dalibo.com/ in order to share the analysis. Make sure the raw query and timings are included.

        How to analyze this is explained below.

        "},{"location":"TROUBLESHOOTING/#log-slow-java-code","title":"Log Slow Java Code","text":"

        If a specific view or action is facing bad performance it may be helpful to log the execution time of the responsible Java code.

        1. Open the logback file located in your domain (default path: /opt/domains/sormas/config/logback.xml) and change the log level of PerformanceLoggingInterceptor to DEBUG or TRACE. The config change will be recognized during runtime within 30s. After that you will see detailed log entries in the SORMAS log.
        2. Reproduce the scenario you want to investigate on the server instance.
        3. Set the log level back to its default once the logging has been done since it can reduce the overall performance of SORMAS.

        Caution: Do not expose any private data! Whenever you debug problems on an instance with productive data, please make sure that the logged information does not contain any personal data like real person names, birth dates, etc. to the public. Never provide such data anywhere on GitHub or any other online tool!

        How to analyze the log is explained below.

        "},{"location":"TROUBLESHOOTING/#analyze-performance-problems","title":"Analyze Performance Problems","text":""},{"location":"TROUBLESHOOTING/#analyze-a-slow-query","title":"Analyze a Slow Query","text":"

        To get your hands on a specific query executed by a view or action, you can do the following:

        1. Get an analysis from a production system
        2. Create your own:
        3. Identify or create a unit test that calls the related backend method
        4. Set the 'hibernate.show_sql' config in the persistence.xml of the test resources to true
        5. Run the unit test and extract the query from the log
        6. Replace any parameters in the query
        7. Execute the query on your local database or a test system pre-faced by 'EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON)'
        8. Create a visual report at https://explain.dalibo.com/ in order to share the analysis. Make sure the raw query and timings are included.

        Important points when interpreting the visual report:

        • Look for the red highlights in the report, especially for nodes that have a long runtime
        • Look for nodes that are processing a lot of rows (100+ millions). This is often the case for joins
        • When you are dealing with a lof of data, most often you only need to output a subset

        How to improve the query is most often a process of trial and error. Here are some things to consider:

        • Adding indices so postgres does not have to go through all data. You can use a btree index to include multiple columns and sorting
        • Getting rid of unnecessary joins. Example: Joining a region of a case to compare it to the region of the user, instead of directly doing this on the region_id field of the case
        • Using limit to reduce the output (e.g. the first 100). Make sure there is no sorting done close to the end of the query graph. This is often the case when 'DISTINCT' is used. See Example
        • Splitting the query into separate queries when 'DISTINCT' has to be used / using distinct on a sub-query
        • Using sub-queries to influence the query planner. See Example

        How to save time when optimizing the query:

        • Make sure you have easy access to a database that allows you to reproduce the bad performance of the query and to manipulate the query and re-run it directly on the database.
        • Use the explain feature of pgAdmin to quickly output the query graph for debugging purposes
        • Make sure you have a unit test (see above) that allows you to create the SQL query from a criteria query without having to re-deploy your server
        "},{"location":"TROUBLESHOOTING/#analyze-java-code-performance-logs","title":"Analyze Java Code Performance Logs","text":"

        After Logging Slow Java Code the debug log file (default path: /opt/domains/sormas/logs/application.debug) can be analyzed in detail using the PerformanceLogAnalysisGenerator.

        The log file's path is specified as the program argument when calling PerformanceLogAnalysisGenerator's main method. Processing the log file will generate three files (<logfileName>.csv, <logfileName>.txt, <logfileName>.html) to further investigate method runtimes.

        <logfileName>.html provides a navigable overview of methods along with runtime statistics (total, min, max and average time) and calls to sub methods.

        Sometimes it is convenient to analyze a number of different scenarios in a row. To do so, produce snippets of the application.debug log using tail for each of the scenarios to be investigated:

        1. start tail -f <logfileName> > <snippetDirectory>/<snippet.debug>
        2. replay the steps to be analyzed
        3. stop tail -f

        The PerformanceLogAnalysisGenerator can now batch process all of the snippets by pointing to the directory instead of a log file. Calling PerformanceLogAnalysisGenerator.main with argument <snippetDirectory> generates the analysis files (.csv, .txt, .html) for each file *.debug in this directory. The generated files will be placed in <snippetDirectory>, too.

        "},{"location":"TROUBLESHOOTING/#ide-troubleshooting-android-studio","title":"IDE Troubleshooting: Android Studio","text":"

        If for some reason the Android App is not building correctly (for example due to unexpected ClassNotFoundExceptions), here is what you should try: - Clean the Project (Build -> Clean Project) - Invalidate Caches (File -> Invalidate Caches / Restart...) - Wipe your Android VM (AVD Manager -> Wipe Data)

        If you get this exception: Unable to load class 'javax.xml.bind.JAXBException', the reason is most likely a faulty JDK version. For the androidapp, you need Java JDK 8. To change the JDK, go to File -> Project Structure -> JDK Location and select a valid JDK (on Linux, check the folder /usr/lib/jvm and/or install if necessary: sudo apt install openjdk-8-jdk)

        "},{"location":"TROUBLESHOOTING/#ide-troubleshooting-eclipse","title":"IDE Troubleshooting: eclipse","text":""},{"location":"TROUBLESHOOTING/#deployment-problems","title":"Deployment Problems","text":"

        Unfortunately, when using eclipse together with the Payara Tools, there are a number of deployment problems that you might run into. Examples of these include:

        • ClassDefNotFoundExceptions after deploying the artifacts and logging in to the web app
        • Error messages in eclipse telling you that the deployment failed

        There are a couple of things you can do to fix these problems:

        • Do a Maven update for all projects
        • Stop and restart the server
        • Re-deploy the server artifacts

        If the problem occurred right after you've pulled new code from GitHub, your safest bet is probably to start with the Maven update. For most other problems, a simple re-deployment or, if necessary, server restart should suffice.

        "},{"location":"TROUBLESHOOTING/#news-feeds-polling","title":"News Feeds Polling","text":"

        When running eclipse with JDK 11, you might encounter the following error message: An internal error occurred during: \"Polling news feeds\". javax/xml/bind/JAXBContext. To fix it, disable Window --> Preferences --> General --> News --> \"Enable automatic news polling\".

        "},{"location":"TROUBLESHOOTING/#redeployment-problems","title":"Redeployment problems","text":"

        If you face problems that sormas-ui or sormas-rest cannot call the backend anymore after redeploying, please follow this instruction.

        "},{"location":"TROUBLESHOOTING/#malware-detection-triggers","title":"Malware detection triggers","text":"

        It might happen that a defensive program on your system falsely recognizes files needed to run SORMAS as vulnerability.

        Please ignore the following known findings (no quarantine, no deletion): * File: payara-5.2021.10.zip, Recognized: Trojan:Script/Oneeva.A!ml (found by Windows Defender). Has rarely happened when running server-setup.sh which downloads that file. The script subsequently fails because zip file cannot be extracted. * File: glassfish/modules/war-util.jar, Recognized: Exploit:Java/CVE-2012-0507.D!ldr (found by Windows Defender in payara-5.2021.10). The deployed OSGi bundle might also be recognized, for example under this path: osgi-cache/felix/bundle365/version0.0/bundle.jar . If the file is quarantined, the paraya domain fails to start, without any exception in the log.

        "},{"location":"sormas-base/doc/keycloak/","title":"Keycloak","text":"

        Open Source Identity and Access Management. In SORMAS Keycloak is available as an alternative authentication provider to the default authentication method.

        Current version is: Keycloak 16.1.0

        "},{"location":"sormas-base/doc/keycloak/#setup","title":"Setup","text":"

        To set up Keycloak check the guide here Keycloak Setup

        "},{"location":"sormas-base/doc/keycloak/#sormas-realm","title":"SORMAS Realm","text":"

        The SORMAS Realm in Keycloak contains all the configuration which are specific to the SORMAS Project. All the configuration is part of the setup/keycloak/SORMAS.json file.

        "},{"location":"sormas-base/doc/keycloak/#configuration-summary","title":"Configuration summary","text":""},{"location":"sormas-base/doc/keycloak/#login-authentication","title":"Login & Authentication","text":"
        • Duplicate emails are allowed in order to support the same requirement as SORMAS which in some installations require admin support for some users, in which case the admin will use her own email address
        • No login with emails due to the previous point
        • Password Policy comes predefined since version 1.54 of the SORMAS-Project with the following default settings
        • Length of minimum 12 characters
        • At least 1 upper case letter
        • At least 1 lower case letter
        • At least 1 digit
        • At least 1 special character
        • OTP is supported by default through the Google Authenticator or Free OTP by has to be activated from the Keycloak Admin console
        • Forgot Password is enabled by default
        • sormas-sha256 is an encryption algorithm which comes packaged with Keycloak to support transition of existing environments towards the Keycloak Authentication Provider
        "},{"location":"sormas-base/doc/keycloak/#clients","title":"Clients","text":"

        The SORMAS Realm relies on the following clients:

        • sormas-ui - handles access to the SORMAS wen UI
        • sormas-app - handles access to the SORMAS Android App
        • sormas-rest - handles access to the SORMAS API
        • sormas-backend - handles SORMAS server requests
        • sormas-stats - handles access to the external SORMAS-Stats App
        "},{"location":"sormas-base/doc/keycloak/#how-to-debug","title":"How to debug","text":"

        How to obtain an access token to impersonate a certain client:

        curl --location --request POST 'http://localhost:8081/keycloak/realms/SORMAS/protocol/openid-connect/token' \n--header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'client_id=sormas-stats' \n--data-urlencode 'client_secret=changeit' --data-urlencode 'grant_type=password' -d \"username=admin\" \n-d \"password=1234abdcefHAH\\!asd\"\n{\"access_token\":\"SOME_TOKEN\",...}\n

        Use this token to access a certain endpoint:

        curl -X POST http://localhost:8081/keycloak/realms/SORMAS/protocol/openid-connect/userinfo \n-H 'Authorization: Bearer SOME_TOKEN'\n{\"sub\":\"399cac62-1b05-45aa-b02c-7ea0a240f144\",\"resource_access\":{\"sormas-stats\":{\"roles\":[\"sormas-stats-access\"]},\n\"account\":{\"roles\":[\"manage-account\",\"manage-account-links\",\"view-profile\"]}},\"email_verified\":false,\"name\":\"ad min\",\n\"preferred_username\":\"admin\",\"given_name\":\"ad\",\"family_name\":\"min\"}\n
        "},{"location":"sormas-base/doc/keycloak/#sormas-backend-client-in-keycloak","title":"sormas-backend client in Keycloak","text":"

        This client is used to allow the SORMAS backend to access the Keycloak server. It is configured as a confidential client, which means that it has a secret that is used to authenticate the client to the OIDC server. This is based on the client credentials grant type, which is configured via the associated Service Account. Currently, the following roles are assigned to the service account:

        {\n  \"clientRoles\": {\n    \"realm-management\": [\n      \"manage-realm\",\n      \"manage-users\",\n      \"manage-clients\"\n    ]\n  }\n}\n
        "},{"location":"sormas-base/doc/keycloak/#sormas-stats-client-in-keycloak","title":"sormas-stats client in Keycloak","text":"

        This client defines a sormas-stats-access client role. Once assigned to a user, it can access sormas-stats. The client scope client roles mapper adds the roles to the userinfo endpoint, which is queried by the Apache2 OIDC module to determine the user's roles.

        "},{"location":"sormas-base/doc/keycloak/#roles","title":"Roles","text":"

        The role management is handled solely by SORMAS starting with 1.70.

        "},{"location":"sormas-base/doc/keycloak/#email","title":"Email","text":"

        Email configurations are optional and are not part of the default configuration.

        In case the system relies on users activating their own accounts it's required to configure these settings.

        "},{"location":"sormas-base/doc/keycloak/#audit-logging","title":"Audit Logging","text":"

        Audit logging of all login activity can be done by setting org.keycloak.events to DEBUG. To enable this, please copy the files from sormas-base/setup/keycloak/audit-logging/ to /opt/jboss/startup-scripts/ inside your Keycloak server and restart the server.

        "},{"location":"sormas-base/doc/keycloak/#custom-configuration","title":"Custom Configuration","text":"

        The configuration provided by default is the minimum required configuration for Keycloak to work together with SORMAS.

        The Keycloak configuration can be adjusted by any user with admin rights, however beware that any change to the default configuration might render the system unusable.

        The following configurations are most likely to be environment specific:

        • Email Settings
        • make sure to set an email for the admin user, so the Test connection feature works
        • Password Policies
        • The Password Blacklist policy can only be configured with access to the host machine
        • OTP Policies
        • Can be activated by default for all user by marking Basic Auth Password+OTP as required in the Authentication>Flows section, then mark it as default in the Authentication>Required section
        "},{"location":"sormas-cargoserver/","title":"SORMAS development server setup using maven-cargo","text":"

        This module installs a local Payara server, deploys the previously built SORMAS artifacts and starts the server.

        The server installation is located in the project build directory (target/cargo), as well as the SORMAS server directories (target/sormasfolders).

        "},{"location":"sormas-cargoserver/#prerequisites","title":"Prerequisites","text":"

        This setup requires a working Java, maven, and docker(-compose) environment.

        "},{"location":"sormas-cargoserver/#configuration","title":"Configuration","text":"

        The configuration of the docker setup and the payara domain setup are defined by the generated file .env. It configures port and server of the sormas-postgres docker container or another postgres database, as well as other ports defined in the domain.xml.

        Properties used in the payara deployment are configured in the generated file target/sormas.properties.

        Both .env and sormas.properties can be customized by adding a file custom.env respectively custom.properties in the project base directory, where additional properties for the respective purpose are defined. Already defined properties are overwritten. Both custom.env and custom.properties are excluded in .gitignore, so local customizations are protected from accidental commits.

        To run the cargo server against an existing database, configure

        SORMAS_POSTGRES_SERVER=<database-server>\nSORMAS_POSTGRES_PORT=<database-port>\n

        in file custom.env and skip the docker-compose step in the server setup (see file custom.env.example).

        To add properties to the generated sormas.properties, configure e.g.

        custombranding=true\ncustombranding.name=<name>\ncustombranding.logo.path=<logopath>\n

        in file custom.properties (see file custom.properties.example).

        After adjusting the configurations, (re)run mvn install and (re)start the server.

        "},{"location":"sormas-cargoserver/#build-the-project-simple","title":"Build the project (simple)","text":"

        The most convenient way to build and deploy the SORMAS artifacts to cargo is to use build_deploy.sh.

        "},{"location":"sormas-cargoserver/#build-the-project-details","title":"Build the project (details)","text":"

        Build all SORMAS artifacts:

        cd sormas-base && mvn install\n
        "},{"location":"sormas-cargoserver/#start-sormas-postgresql-docker-container","title":"Start SORMAS-PostgreSQL docker container","text":"
        cd sormas-cargoserver && docker-compose up -d\n
        "},{"location":"sormas-cargoserver/#start-local-sormas-server","title":"Start local SORMAS server","text":"
        cd sormas-cargoserver && mvn cargo:run\n
        "},{"location":"sormas-cargoserver/#visit","title":"Visit","text":"

        Once the deployment is completed, you can navigate to http://localhost:6080/sormas-ui and login as admin with password sadmin.

        "},{"location":"sormas-cargoserver/#stop-local-sormas-server","title":"Stop local SORMAS server","text":"
        cd sormas-cargoserver && mvn cargo:stop\n
        "},{"location":"sormas-cargoserver/#stop-db-sormas-postgresql-docker-container","title":"Stop DB SORMAS-PostgreSQL docker container","text":"
        cd sormas-cargoserver && docker-compose down\n
        "},{"location":"sormas-cargoserver/#remove-docker-volume-if-intended","title":"Remove docker volume (if intended)","text":"

        The SORMAS-PostgreSQL docker container uses a named docker volume:

        $ docker volume ls\nDRIVER              VOLUME NAME\nlocal               sormas-cargoserver_psqldata_cargoserver\n

        To remove this docker volume:

        sudo docker volume rm sormas-cargoserver_psqldata_cargoserver\n
        "},{"location":"sormas-keycloak-service-provider/","title":"Custom SORMAS Keycloak Service Provider","text":"

        This is a keycloak service provider that implements SORMAS legacy password hashing mechanism for two use cases:

        1. Migration of existing user when moving from the basic authentication mechanism to keycloak
        2. Creation of users without email in SORMAS. In this case the password will be shown to the admin once.

        More on Authentication & Authorization.

        "},{"location":"sormas-keycloak-service-provider/#sormas-password-hash-provider","title":"SORMAS Password Hash Provider","text":"

        This Service Provider is implementing in Keycloak a Hash Mechanism for passwords similar to the one used in SORMAS to allow migrating of credentials for already existing users.

        Since SORMAS and Keycloak are using different hashing techniques, the SormasPasswordHashProvider replicates the SORMAS technique by importing the sormas-api dependency where the technique is defined.

        "},{"location":"sormas-keycloak-service-provider/#sormas-hashing-technique","title":"SORMAS Hashing Technique","text":"

        For more info about the SORMAS hashing technique see sormas-api/src/main/java/de/symeda/sormas/api/utils/PasswordHelper.java

        In Keycloak this algorithm will be identifiable by the ID sormas-sha256.

        "},{"location":"sormas-keycloak-service-provider/#keycloak-hashing-technique","title":"Keycloak Hashing Technique","text":"

        Keycloak supports a more configurable approach to password policy which can be customized for each system. See Password Policies.

        "},{"location":"sormas-keycloak-service-provider/#sormas-user-password-sync","title":"SORMAS User Password Sync","text":"

        There are only 2 ways of synchronizing the user's password from SORMAS into Keycloak: * whenever a user is created for the first time in Keycloak - being triggered from SORMAS * whenever a user's password is updated in SORMAS, and the user doesn't have an email address setup

        For any of the events about the user's credentials in Keycloak are overwritten by those from SORMAS, and the hashing algorithm will be changed to sormas-sha256.

        However once a user chooses to change their password in Keycloak (trough the Forgot Password mechanism or by the admin), their credentials will be updated using the default or configured Password Policies from Keycloak.

        "},{"location":"sormas-keycloak-service-provider/#deployment-of-the-spi","title":"Deployment of the SPI","text":"

        To deploy the Custom SPI, make sure to build this project and then follow the steps described in Register an SPI Using the Keycloak Deployer

        "},{"location":"sormas-rest/","title":"REST interface for SORMAS","text":"

        This is a one-stop-shop for all systems that need access to the SORMAS data:

        • Synchronization of data with the SORMAS Android app
        • Data access for the SORMAS Angular web app
        • Exchanging data with other SORMAS instances
        • External services like symptom diaries or citizen applications
        • Synchronization of data with other surveillance or to data analysis systems
        "},{"location":"sormas-rest/#authentication","title":"Authentication","text":"

        Access to the API is by default restricted by HTTP Basic authentication. Using OIDC/OAUTH2/Bearer authentication is also possible depending on how keycloak is set up. See Authentication & Authorization.

        For basic auth use the username and password as credentials for your HTTP requests. The user needs to have a user role having the SORMAS_REST user right.

        "},{"location":"sormas-rest/#api-documentation","title":"API Documentation","text":"

        The SORMAS REST API is documented automatically. The OpenAPI specification files are generated during the build process and can be found at ${Project Root}/sormas-rest/target/swagger.{json,yaml}.

        You can render the OpenAPI specification with tools like editor.swagger.io. This allows you to inspect endpoints and example payloads, generate a matching API client for many languages, and to easily interact with the API of a live instance.

        "},{"location":"sormas-rest/#openapi-swagger","title":"OpenAPI / Swagger","text":"

        The OpenAPI files are generated with the swagger-maven-plugin and the Swagger Annotation Framework[1].

        If you are only interested in the OpenAPI specification files, you may either download a recent SORMAS release where the files reside in the openapi directory, take a look at the files in sormas-rest, or execute the following command inside the sormas-base module's directory to build them for yourself:

        # Requires Maven to be installed!\nmvn package --projects ../sormas-rest --also-make -Dmaven.test.skip=true\n

        The specification files are created at the path specified above.

        [1] Swagger Annotations Guide on GitHub: https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations

        "},{"location":"sormas-rest/#external-visit-api","title":"External Visit API","text":"

        The purpose of this API is to enable communication between SORMAS and other symptom journals. Only users with the role REST_EXTERNAL_VISITS_USER are authorized to use the endpoints. Authentication is done using basic auth, with the user and password. For technical details please contact the dev team on GitHub Discussions.

        "},{"location":"sormas-rest/#workflow-description","title":"Workflow Description","text":""},{"location":"sormas-rest/#about-external-follow-up","title":"About external follow up","text":"

        Follow up in SORMAS is done via visits. Visits hold information about symptoms at a specific time point. Visits can be created for cases and contacts, either via the SORMAS-UI or the external visits journal API.

        "},{"location":"sormas-rest/#about-persons-cases-and-contacts","title":"About persons, cases and contacts","text":"

        This is a very basic concept that one needs to understand when working with visits. In SORMAS, a person entity represents a physically existing person. Cases and contacts represent epidemiological occurrences. When a person was in contact with an infectious environment, the person has a contact in SORMAS. When the person was in contact with such environments twice, it gets two contacts. When a person falls ill, it gets a case with the according disease. This means: In SORMAS, each contact and case relates to exactly one person. One person can have several contacts and cases, though. Follow up is done for a contact (or a case, which can be enabled in the SORMAS feature configuration). Contacts (or cases) initiate follow up. A person, though, either shows symptoms or not. It can not show symptoms for just one contact and not for the other. Thus, visits are related to all active contacts (and cases) of a person. Also, the communication with external symptom journals is PERSON BASED. Only the person uuid is used, visits are uploaded to each active case and contact of a person.

        "},{"location":"sormas-rest/#person-status-variables","title":"Person status variables","text":"

        It is important to understand the meaning of two variables: the follow-up status and the symptom journal status.

        The follow-up status describes the follow-up for a contact or a case. Possible values are defined in the FollowUpStatus enum

        Follow up can be done with, or without an external journal, the follow-up status makes no distinction there. Because the follow-up status is contact and case specific,but the communication with external journals is person based, SORMAS determines the most important follow-up status of all contacts and cases related to the person in question when communicating with external journals. Whenever there is follow-up ongoing for any of the persons contacts (and cases if the case follow-up feature is enabled in SORMAS), SORMAS will state the FollowUpStatus.FOLLOW_UP for that person towards external journals.

        The SymptomJournalStatus describes the state of a person related to external symptom journals. It is not contact or case specific.

        "},{"location":"sormas-rest/#configuration-in-sormas","title":"Configuration in SORMAS","text":"

        In the domain folder, there is a sormas.properties. it holds the following values relevant for an external journal:

        • interface.patientdiary.authurl: used to fetch an authentication token (see 1. below).

        • interface.patientdiary.frontendAuthurl: URL used to retrieve tokens for frontend requests. If not specified, no tokens will be fetched for such.

        • interface.patientdiary.tokenLifetime: Lifetime of tokens fetched via the authurl or the frontendAuthurl. To be specified in seconds. Can be set to 0 for no token caching. Defaults to 21600 (6 hrs.).

        • interface.patientdiary.probandsurl: used to register new persons in the external journal (see 2. below).

        • interface.patientdiary.url: used to open a person in the external journal (see 6. below).

        • interface.patientdiary.email: used to authenticate at the external journal (see 1. below).

        • interface.patientdiary.password: used to authenticate at the external journal.

        • interface.patientdiary.defaultuser.username: This user will be created in SORMAS and can be used by the external journal to authenticate.

        • interface.patientdiary.defaultuser.password: The above user's password.

        • interface.patientdiary.acceptPhoneContact: used to configure whether the phone number is considered relevant for registering a person in the external journal. It affects the validation of persons in SORMAS (see 2. below). Defaults to true

        "},{"location":"sormas-rest/#workflows","title":"Workflows","text":""},{"location":"sormas-rest/#sormas-fetching-an-authentication-token-from-the-external-journal","title":"SORMAS fetching an authentication token from the external journal","text":"

        POST to the interface.patientdiary.authurl.

        Request body: ```json lines { \"email\" : [patientdiary.email], \"password\" : [patientdiary.password] }

        where `[patientdiary.email]` is replaced with `interface.patientdiary.email` and `[patientdiary.password]` with\n`interface.patientdiary.password specified` in the `sormas.properties`.\n\nExpected response body:\n```json lines\n{\n    \"success\" : true,\n    \"userId\" : [some-user-id],\n    \"token\" : [token]\n}\n

        The token returned will be used to authenticate in other requests. Its lifetime can be configured via the interface.patientdiary.tokenLifetime property.

        One special scenario is fetching a token for frontend calls (see 6.): When the interface.patientdiary.frontendAuthurl is configured, it is used instead of the interface.patientdiary.authurl here. If it is not configured, no token will be used.

        "},{"location":"sormas-rest/#registration-of-a-new-person-in-the-external-journal","title":"Registration of a new person in the external journal","text":"

        This process involves several steps that are triggered via the REGISTER button a privileged user can see in the top right corner when having opened a case or a contact.

        To be able to see this button, the user must have at least one of the following user roles: national user, contact supervisor, contact officer, community officer, surveillance officer, surveillance supervisor, or admin supervisor.

        First comes a SORMAS-internal validation of contact details. The person to be registered needs to have at least an email address (or a phone number if that is accepted for registration, see interface.patientdiary.acceptPhoneContact) to pass this validation.Also, when there are several email addresses or phone numbers, one of them has to be marked primary contact detail, so that it is clear which contact detail shall be used.

        Then comes an external validation of the contact details. For this, SORMAS fetches an authentication token as in 1. Then it sends a GET request to the following URL for each contact detail to be used in the external journal:

        GET [interface.patientdiary.probandsurl]/probands?q=[URL-encoded-query-parameter-and-value], with a header like x-access-token: [token].

        The [URL-encoded-query-parameter-and-value] consists of a parameter-value-pair. The parameter is either Email or Mobile phone. The value holds the contact detail to be validated.

        An unencoded example for this is \"Email\" = \"example@example.de\", the URL-encoded version is %22Email%22%20%3D%20%22example%40example.de%22.

        [token] is replaced with the token fetched for authorization.

        The CURL equivalent for an exemplary call is \\ curl --request GET 'https://probands-URL.com//probands?q=%22Email%22%20%3D%20%22example%40example.de%22' --header 'x-access-token: my-access-token'.

        Expected result is a PatientDiaryQueryResponse which information about any person already registered in the external journal and using the same contact detail.

        It needs to be structured as follows: ```json lines PatientDiaryQueryResponse { total: integer, count: integer, results: List }

        \n- `total` should state how many persons are registered in the external journal (this information is currently never used in SORMAS).\n\n- `count` should state how many registered persons using the same contact detail were found.\n\n- `results` need to contain a PatientDiaryPersonData for each match:\n\n```json lines\nPatientDiaryPersonData {\n    _id: string,\n    idatId: PatientDiaryIdatId\n}\n
        • _id should be a unique identifier for the person this data is about (this information is currently never used in SORMAS)

        The PatientDiaryIdatId needs to be structured as follows:

        ```json lines PatientDiaryIdatId{ idat: PatientDiaryPersonDto }

        \nThe `PatientDiaryPersonDto` holds the actual person data:\n\n```json lines\nPatientDiaryPersonDto{\n    personUUID: string,\n    firstName: string,\n    lastName: string,\n    gender: string,\n    birthday: string,\n    contactInformation: PatientDiaryContactInformation,\n    endDate: string\n}\n\n
        • personUUID should be the UUID of the person in SORMAS. This UUID is used to sync with external journals (this information is currently never used in SORMAS).
        • firstName and lastName need to hold the first and last name of the person.
        • gender should hold the persons gender (this information is currently never used in SORMAS).
        • birthday should hold the person's birthday (this information is currently never used in SORMAS).
        • contactInformation should hold the contact information of that person, which should for logical reasons always contain (at least) the contact detail provided by SORMAS in the query.
        • endDate should hold the date after which follow up is supposed to be stopped by the external journal.

        ```json lines PatientDiaryIdatId { email: string, phone: PatientDiaryPhone }

        \n- `email` should hold the email address for the person\n- `phone` should hold the phone number of that person:\n\n```json lines\nPatientDiaryPhone {\n    number: string,\n    internationalNumber: string,\n    nationalNumber: string,\n    countryCode: string,\n    dialCode: string,\n}\n

        To put this all together, here is an example PatientDiaryQueryResponse with one person using the same contact detail:

        {\n    \"total\" : 100,\n    \"count\" : 1,\n    \"results\" : [{\n        \"_id\" : \"60586691d4c30700119515c8\",\n        \"idatId\" : {\n            \"idat\" : {\n            \"contactInformation\" : {\n                \"phone\" : null,\n                \"email\" : \"example@example.de\"\n            },\n            \"personUUID\" : \"RMTEF2-UZXCXE-7YBJK6-KUMSSEME\",\n              \"firstName\" : \"Maria\",\n              \"lastName\" : \"Muster\",\n              \"gender\" : \"female\",\n              \"birthday\" : null\n            }\n        }\n    }]\n}\n

        SORMAS allows to continue with the registration of a new person only when the person has a unique first name, so when all persons of the response have a different one (or if the response does not contain any matches, which needs to show in PatientDiaryQueryResponse.count == 0). This validation is necessary to avoid confusion of person related data in some external journals.

        When there are no validation errors in the process described above, SORMAS fetches an authentication token as described in 1. and then uses it to request the external journal to register the person:

        POST [interface.patientdiary.probandsurl]/external-data/[personUUID].

        The [personUUID] is replaced with the UUID of the person, which is later used to sync data between the external journal and SORMAS.

        Expected response body: ```json lines { \"success\" : [boolean], \"message\" : [messageString] }

        `[boolean]` is expected to be true in case of successful registration. SORMAS then sets the symptom journal status to\n`REGISTERED` and displays the message to the user.\n\nWhen `[boolean]` is false, the message is shown as an error to the user.\n\nTo fetch data relevant for the registration, the external journal can use the `/visits-external/person/{personUuid}`\nAPI endpoint described below.\n\n##### Synchronization of person data changed in SORMAS\n\nIt may happen that person data (like a contact detail) gets changed after a person is registered in an external journal.\nSORMAS notifies external journals about such a change with first fetching an authentication token as descriced in 1.,\nand the using this token for this request:\n\n`PUT [interface.patientdiary.probandsurl]/external-data/[personUUID]`\n\nThe external journal is expected to refetch the person data via the `/visits-external/person/{personUuid}` API endpoint\ndescribed below and save the changes.\n\nAfter re-fetching the person data, the symptom journal does its own internal validation and responds to SORMAS with the\nsynchronization result, containing eventual validation errors.\n\nThe expected response body:\n```json lines\n{\n    \"success\" : [boolean],\n    \"message\" : [messageString],\n    \"errors\" : [{\n        \"errorKey\": [errorString]\n      }]\n}\n

        If the changes were done manually by a user from the person edit form, the synchronization result is shown to the user in a popup window, so that the user can fix eventual errors and resynchronize the person data.

        "},{"location":"sormas-rest/#upload-of-symptom-journal-data-to-sormas","title":"Upload of symptom journal data to SORMAS","text":"

        For this, the /visits-external API endpoint has to be used. This is described below.

        "},{"location":"sormas-rest/#upload-of-a-symptom-journal-status-to-sormas","title":"Upload of a symptom journal status to SORMAS","text":"

        For this, the /visits-external/person/{personUuid}/status API endpoint is to be used. This is described below.

        "},{"location":"sormas-rest/#opening-a-person-in-the-external-journal-from-within-sormas","title":"Opening a person in the external journal from within SORMAS","text":"

        Once the symptom journal status of a person is set to REGISTERED or ACCEPTED, the external journal button in the SORMAS-UI changes. It does not provide a registration anymore, but the options to open the person in the external journal and to cancel external follow up. This button can be found when having opened a contact (or a case if the case follow-up feature is enabled in SORMAS) in the top right corner. If the user chooses to open the person in the external journal, SORMAS opens a new browser tab with the following URL:

        [interface.patientdiary.url]/data?q=[personUuid]&queryKey=sicFieldIdentifier

        SORMAS expects the external journal to present a view of the person there.

        If the interface.patientdiary.frontendAuthurl is configured, SORMAS fetches an authentication token as described in 1, and appends it to the URL:

        [interface.patientdiary.url]/data?q=[personUuid]&queryKey=sicFieldIdentifier&token=[token]

        "},{"location":"sormas-rest/#deletion-of-a-person-from-an-external-journal","title":"Deletion of a person from an external journal","text":"

        As described above, the journal button can offer the option to cancel external follow up. If a user choses this option, SORMAS fetches an authentication token as described in 1., and uses it to request:

        DELETE [interface.patientdiary.probandsurl]/external-data/[personUUID]

        Expected response body: json lines { \"success\" : [boolean], \"message\" : [messageString] }

        [boolean] is expected to be true in case of successful deletion. SORMAS then sets the symptom journal status to DELETED and displays the message to the user.

        When [boolean] is false, the message is shown as an error to the user.

        Please note that this does not affect any follow-up status. Cancelling follow up of a contact or case is independent of cancelling external follow up of a person.

        "}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 000000000000..28594deff3f6 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,108 @@ + + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + + None + 2023-10-05 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..99934bbc0adcaa1fb1a4630217b4694fe574a2c2 GIT binary patch literal 212 zcmV;_04x6=iwFoL+a6^C|8r?{Wo=<_E_iKh0PWUG4uUWc2H-uXAl*X?#zajiJD0BX z00b&DDJ^!Y=Lrn{oZZ58EEiTqbPN#1B zP=W7lBXXASl8hy6A4b+}Ggfz%Y31F?hiEFLKb&roMWjCxal{cv9C5@EM;!70!sjD= OKgAciQgYs|3IG5pIbC=F literal 0 HcmV?d00001 diff --git a/sormas-base/doc/keycloak/index.html b/sormas-base/doc/keycloak/index.html new file mode 100644 index 000000000000..8bd68221bbd8 --- /dev/null +++ b/sormas-base/doc/keycloak/index.html @@ -0,0 +1,912 @@ + + + + + + + + + + + + + + + + + + + + + + Keycloak - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +
        + + + + + + +
        + + +
        + +
        + + + + + + +
        +
        + + + +
        +
        +
        + + + + +
        +
        +
        + + + +
        +
        +
        + + + +
        +
        +
        + + + +
        +
        + + + + + + + + +

        Keycloak

        +

        Open Source Identity and Access Management. +In SORMAS Keycloak is available as an alternative authentication provider to the default authentication method.

        +

        Current version is: Keycloak 16.1.0

        +

        Setup

        +

        To set up Keycloak check the guide here Keycloak Setup

        +

        SORMAS Realm

        +

        The SORMAS Realm in Keycloak contains all the configuration which are specific to the SORMAS Project. +All the configuration is part of the setup/keycloak/SORMAS.json file.

        +

        Configuration summary

        +

        Login & Authentication

        +
          +
        • Duplicate emails are allowed in order to support the same requirement as SORMAS which in some installations require +admin support for some users, in which case the admin will use her own email address
        • +
        • No login with emails due to the previous point
        • +
        • Password Policy comes predefined since version 1.54 of the SORMAS-Project with the following default settings
        • +
        • Length of minimum 12 characters
        • +
        • At least 1 upper case letter
        • +
        • At least 1 lower case letter
        • +
        • At least 1 digit
        • +
        • At least 1 special character
        • +
        • OTP is supported by default through the Google Authenticator or Free OTP by has to be activated from the + Keycloak Admin console
        • +
        • Forgot Password is enabled by default
        • +
        • sormas-sha256 is an encryption algorithm which comes packaged with Keycloak to support transition of existing + environments towards the Keycloak Authentication Provider
        • +
        +

        Clients

        +

        The SORMAS Realm relies on the following clients:

        +
          +
        • sormas-ui - handles access to the SORMAS wen UI
        • +
        • sormas-app - handles access to the SORMAS Android App
        • +
        • sormas-rest - handles access to the SORMAS API
        • +
        • sormas-backend - handles SORMAS server requests
        • +
        • sormas-stats - handles access to the external SORMAS-Stats App
        • +
        +
        How to debug
        +

        How to obtain an access token to impersonate a certain client:

        +
        curl --location --request POST 'http://localhost:8081/keycloak/realms/SORMAS/protocol/openid-connect/token' 
        +--header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'client_id=sormas-stats' 
        +--data-urlencode 'client_secret=changeit' --data-urlencode 'grant_type=password' -d "username=admin" 
        +-d "password=1234abdcefHAH\!asd"
        +{"access_token":"SOME_TOKEN",...}
        +
        +

        Use this token to access a certain endpoint:

        +
        curl -X POST http://localhost:8081/keycloak/realms/SORMAS/protocol/openid-connect/userinfo 
        +-H 'Authorization: Bearer SOME_TOKEN'
        +{"sub":"399cac62-1b05-45aa-b02c-7ea0a240f144","resource_access":{"sormas-stats":{"roles":["sormas-stats-access"]},
        +"account":{"roles":["manage-account","manage-account-links","view-profile"]}},"email_verified":false,"name":"ad min",
        +"preferred_username":"admin","given_name":"ad","family_name":"min"}
        +
        +
        sormas-backend client in Keycloak
        +

        This client is used to allow the SORMAS backend to access the Keycloak server. +It is configured as a confidential client, which means that it has a secret that is used to authenticate the client +to the OIDC server. This is based on the client credentials grant type, which is configured via the associated +Service Account. Currently, the following roles are assigned to the service account:

        +
        {
        +  "clientRoles": {
        +    "realm-management": [
        +      "manage-realm",
        +      "manage-users",
        +      "manage-clients"
        +    ]
        +  }
        +}
        +
        +
        sormas-stats client in Keycloak
        +

        This client defines a sormas-stats-access client role. Once assigned to a user, it can access sormas-stats. +The client scope client roles mapper adds the roles to the userinfo endpoint, which is queried by the Apache2 +OIDC module to determine the user's roles.

        +

        Roles

        +

        The role management is handled solely by SORMAS starting with 1.70.

        +

        Email

        +

        Email configurations are optional and are not part of the default configuration.

        +

        In case the system relies on users activating their own accounts it's required to configure these settings.

        +

        Audit Logging

        +

        Audit logging of all login activity can be done by setting org.keycloak.events to DEBUG. To enable this, please copy +the files from sormas-base/setup/keycloak/audit-logging/ to /opt/jboss/startup-scripts/ inside your Keycloak server +and restart the server.

        +

        Custom Configuration

        +

        The configuration provided by default is the minimum required configuration for Keycloak to work together with SORMAS.

        +

        The Keycloak configuration can be adjusted by any user with admin rights, however beware that any change to the default +configuration might render the system unusable.

        +

        The following configurations are most likely to be environment specific:

        +
          +
        • Email Settings
        • +
        • make sure to set an email for the admin user, so the Test connection feature works
        • +
        • Password Policies
        • +
        • The Password Blacklist policy can only be configured with access to the host machine
        • +
        • OTP Policies
        • +
        • Can be activated by default for all user by marking Basic Auth Password+OTP as required in the + Authentication>Flows section, then mark it as default in the Authentication>Required section
        • +
        + + + + + + +
        +
        + + +
        + +
        + + + +
        +
        +
        +
        + + + + + + + + + \ No newline at end of file diff --git a/sormas-cargoserver/index.html b/sormas-cargoserver/index.html new file mode 100644 index 000000000000..a4ea640a31c9 --- /dev/null +++ b/sormas-cargoserver/index.html @@ -0,0 +1,824 @@ + + + + + + + + + + + + + + + + + + + + + + Cargo Server - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +
        + + + + + + +
        + + +
        + +
        + + + + + + +
        +
        + + + +
        +
        +
        + + + + +
        +
        +
        + + + + + + + +
        +
        + + + + + + + + +

        SORMAS development server setup using maven-cargo

        +

        This module installs a local Payara server, deploys the previously built SORMAS artifacts and starts the server.

        +

        The server installation is located in the project build directory (target/cargo), as well as the SORMAS server +directories (target/sormasfolders).

        +

        Prerequisites

        +

        This setup requires a working Java, maven, and docker(-compose) environment.

        +

        Configuration

        +

        The configuration of the docker setup and the payara domain setup are defined by the generated file .env. It +configures port and server of the sormas-postgres docker container or another postgres database, as well as other +ports defined in the domain.xml.

        +

        Properties used in the payara deployment are configured in the generated file target/sormas.properties.

        +

        Both .env and sormas.properties can be customized by adding a file custom.env respectively custom.properties +in the project base directory, where additional properties for the respective purpose are defined. Already defined +properties are overwritten. Both custom.env and custom.properties are excluded in .gitignore, so local +customizations are protected from accidental commits.

        +

        To run the cargo server against an existing database, configure

        +
        SORMAS_POSTGRES_SERVER=<database-server>
        +SORMAS_POSTGRES_PORT=<database-port>
        +
        +

        in file custom.env and skip the docker-compose step in the server setup (see file custom.env.example).

        +

        To add properties to the generated sormas.properties, configure e.g.

        +
        custombranding=true
        +custombranding.name=<name>
        +custombranding.logo.path=<logopath>
        +
        +

        in file custom.properties (see file custom.properties.example).

        +

        After adjusting the configurations, (re)run mvn install and (re)start the server.

        +

        Build the project (simple)

        +

        The most convenient way to build and deploy the SORMAS artifacts to cargo is to use build_deploy.sh.

        +

        Build the project (details)

        +

        Build all SORMAS artifacts:

        +
        cd sormas-base && mvn install
        +
        +

        Start SORMAS-PostgreSQL docker container

        +
        cd sormas-cargoserver && docker-compose up -d
        +
        +

        Start local SORMAS server

        +
        cd sormas-cargoserver && mvn cargo:run
        +
        +

        Visit

        +

        Once the deployment is completed, you can navigate to http://localhost:6080/sormas-ui and login as admin with +password sadmin.

        +

        Stop local SORMAS server

        +
        cd sormas-cargoserver && mvn cargo:stop
        +
        +

        Stop DB SORMAS-PostgreSQL docker container

        +
        cd sormas-cargoserver && docker-compose down
        +
        +

        Remove docker volume (if intended)

        +

        The SORMAS-PostgreSQL docker container uses a named docker volume:

        +
        $ docker volume ls
        +DRIVER              VOLUME NAME
        +local               sormas-cargoserver_psqldata_cargoserver
        +
        +

        To remove this docker volume:

        +
        sudo docker volume rm sormas-cargoserver_psqldata_cargoserver
        +
        + + + + + + +
        +
        + + +
        + +
        + + + +
        +
        +
        +
        + + + + + + + + + \ No newline at end of file diff --git a/sormas-keycloak-service-provider/index.html b/sormas-keycloak-service-provider/index.html new file mode 100644 index 000000000000..3e1bacd7ddd0 --- /dev/null +++ b/sormas-keycloak-service-provider/index.html @@ -0,0 +1,726 @@ + + + + + + + + + + + + + + + + + + + + + + Keycloak Service Provider - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +
        + + + + + + +
        + + +
        + +
        + + + + + + +
        +
        + + + +
        +
        +
        + + + + +
        +
        +
        + + + +
        +
        +
        + + + +
        +
        +
        + + + +
        +
        + + + + + + + + +

        Custom SORMAS Keycloak Service Provider

        +

        This is a keycloak service provider that implements SORMAS legacy password hashing mechanism for two use cases:

        +
          +
        1. Migration of existing user when moving from the basic authentication mechanism to keycloak
        2. +
        3. Creation of users without email in SORMAS. In this case the password will be shown to the admin once.
        4. +
        +

        More on Authentication & Authorization.

        +

        SORMAS Password Hash Provider

        +

        This Service Provider is implementing in Keycloak a Hash Mechanism for passwords similar +to the one used in SORMAS to allow migrating of credentials for already existing users.

        +

        Since SORMAS and Keycloak are using different hashing techniques, the SormasPasswordHashProvider replicates the +SORMAS technique by importing the sormas-api dependency where the technique is defined.

        +

        SORMAS Hashing Technique

        +

        For more info about the SORMAS hashing technique see sormas-api/src/main/java/de/symeda/sormas/api/utils/PasswordHelper.java

        +

        In Keycloak this algorithm will be identifiable by the ID sormas-sha256.

        +

        Keycloak Hashing Technique

        +

        Keycloak supports a more configurable approach to password policy which can be customized for each system. +See Password Policies.

        +

        SORMAS User Password Sync

        +

        There are only 2 ways of synchronizing the user's password from SORMAS into Keycloak: +* whenever a user is created for the first time in Keycloak - being triggered from SORMAS +* whenever a user's password is updated in SORMAS, and the user doesn't have an email address setup

        +

        For any of the events about the user's credentials in Keycloak are overwritten by those from SORMAS, and the hashing +algorithm will be changed to sormas-sha256.

        +

        However once a user chooses to change their password in Keycloak (trough the Forgot Password mechanism or by the admin), +their credentials will be updated using the default or configured Password Policies from Keycloak.

        +

        Deployment of the SPI

        +

        To deploy the Custom SPI, make sure to build this project and then follow the steps described in +Register an SPI Using the Keycloak Deployer

        + + + + + + +
        +
        + + +
        + +
        + + + +
        +
        +
        +
        + + + + + + + + + \ No newline at end of file diff --git a/sormas-rest/index.html b/sormas-rest/index.html new file mode 100644 index 000000000000..f2b5dcf88d5c --- /dev/null +++ b/sormas-rest/index.html @@ -0,0 +1,1194 @@ + + + + + + + + + + + + + + + + + + + + + + REST - SORMAS Project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +
        + + + + + + +
        + + +
        + +
        + + + + + + +
        +
        + + + +
        +
        +
        + + + + +
        +
        +
        + + + + + + + +
        +
        + + + + + + + + +

        REST interface for SORMAS

        +

        This is a one-stop-shop for all systems that need access to the SORMAS data:

        +
          +
        • Synchronization of data with the SORMAS Android app
        • +
        • Data access for the SORMAS Angular web app
        • +
        • Exchanging data with other SORMAS instances
        • +
        • External services like symptom diaries or citizen applications
        • +
        • Synchronization of data with other surveillance or to data analysis systems
        • +
        +

        Authentication

        +

        Access to the API is by default restricted by HTTP Basic authentication. Using OIDC/OAUTH2/Bearer authentication is +also possible depending on how keycloak is set up. +See Authentication & Authorization.

        +

        For basic auth use the username and password as credentials for your HTTP requests. +The user needs to have a user role having the SORMAS_REST user right.

        +

        API Documentation

        +

        The SORMAS REST API is documented automatically. The OpenAPI specification files are generated during the build process +and can be found at ${Project Root}/sormas-rest/target/swagger.{json,yaml}.

        +

        You can render the OpenAPI specification with tools like +editor.swagger.io. +This allows you to inspect endpoints and example payloads, generate a matching API client for many languages, and to easily interact with the API of a live instance.

        +

        OpenAPI / Swagger

        +

        The OpenAPI files are generated with the swagger-maven-plugin +and the Swagger Annotation Framework[1].

        +

        If you are only interested in the OpenAPI specification files, you may either download a recent SORMAS +release where the files reside in the openapi +directory, take a look at the files in sormas-rest, or execute the following command inside the sormas-base module's +directory to build them for yourself:

        +
        # Requires Maven to be installed!
        +mvn package --projects ../sormas-rest --also-make -Dmaven.test.skip=true
        +
        +

        The specification files are created at the path specified above.

        +
        +

        [1] Swagger Annotations Guide on GitHub: +https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations

        +

        External Visit API

        +

        The purpose of this API is to enable communication between SORMAS and other symptom journals. +Only users with the role REST_EXTERNAL_VISITS_USER are authorized to use the endpoints. Authentication is done using +basic auth, with the user and password. For technical details please contact the dev team on +GitHub Discussions.

        +

        Workflow Description

        +

        About external follow up

        +

        Follow up in SORMAS is done via visits. Visits hold information about symptoms at a specific time point. +Visits can be created for cases and contacts, either via the SORMAS-UI or the external visits journal API.

        +

        About persons, cases and contacts

        +

        This is a very basic concept that one needs to understand when working with visits. In SORMAS, a person entity +represents a physically existing person. Cases and contacts represent epidemiological occurrences. When a person was +in contact with an infectious environment, the person has a contact in SORMAS. +When the person was in contact with such environments twice, it gets two contacts. +When a person falls ill, it gets a case with the according disease. This means: In SORMAS, each contact and case relates +to exactly one person. One person can have several contacts and cases, though. Follow up is done for a contact +(or a case, which can be enabled in the SORMAS feature configuration). Contacts (or cases) initiate follow up. A person, +though, either shows symptoms or not. It can not show symptoms for just one contact and not for the other. Thus, visits +are related to all active contacts (and cases) of a person. Also, the communication with external symptom journals is +PERSON BASED. Only the person uuid is used, visits are uploaded to each active case and contact of a person.

        +

        Person status variables

        +

        It is important to understand the meaning of two variables: the follow-up status and the symptom journal status.

        +

        The follow-up status describes the follow-up for a contact or a case. Possible values are defined in +the FollowUpStatus enum

        +

        Follow up can be done with, or without an external journal, the follow-up status makes no distinction there. Because the +follow-up status is contact and case specific,but the communication with external journals is person based, SORMAS +determines the most important follow-up status of all contacts and cases related to the person in question when +communicating with external journals. Whenever there is follow-up ongoing for any of the persons contacts (and cases if +the case follow-up feature is enabled in SORMAS), SORMAS will state the FollowUpStatus.FOLLOW_UP for that person +towards external journals.

        +

        The SymptomJournalStatus +describes the state of a person related to external symptom journals. It is not contact or case specific.

        +

        Configuration in SORMAS

        +

        In the domain folder, there is a sormas.properties. it holds the following values relevant for an external journal:

        +
          +
        • +

          interface.patientdiary.authurl: used to fetch an authentication token (see 1. below).

          +
        • +
        • +

          interface.patientdiary.frontendAuthurl: URL used to retrieve tokens for frontend requests. If not specified, no tokens will be fetched for such.

          +
        • +
        • +

          interface.patientdiary.tokenLifetime: Lifetime of tokens fetched via the authurl or the frontendAuthurl. To be specified in seconds. Can be set to 0 for no token caching. Defaults to 21600 (6 hrs.).

          +
        • +
        • +

          interface.patientdiary.probandsurl: used to register new persons in the external journal (see 2. below).

          +
        • +
        • +

          interface.patientdiary.url: used to open a person in the external journal (see 6. below).

          +
        • +
        • +

          interface.patientdiary.email: used to authenticate at the external journal (see 1. below).

          +
        • +
        • +

          interface.patientdiary.password: used to authenticate at the external journal.

          +
        • +
        • +

          interface.patientdiary.defaultuser.username: This user will be created in SORMAS and can be used by the external journal to authenticate.

          +
        • +
        • +

          interface.patientdiary.defaultuser.password: The above user's password.

          +
        • +
        • +

          interface.patientdiary.acceptPhoneContact: used to configure whether the phone number is considered relevant for registering a person in the external journal. It affects the validation of persons in SORMAS (see 2. below). Defaults to true

          +
        • +
        +

        Workflows

        +
        SORMAS fetching an authentication token from the external journal
        +

        POST to the interface.patientdiary.authurl.

        +

        Request body: +```json lines +{ + "email" : [patientdiary.email], + "password" : [patientdiary.password] +}

        +
        where `[patientdiary.email]` is replaced with `interface.patientdiary.email` and `[patientdiary.password]` with
        +`interface.patientdiary.password specified` in the `sormas.properties`.
        +
        +Expected response body:
        +```json lines
        +{
        +    "success" : true,
        +    "userId" : [some-user-id],
        +    "token" : [token]
        +}
        +
        +

        The token returned will be used to authenticate in other requests. Its lifetime can be configured via the +interface.patientdiary.tokenLifetime property.

        +

        One special scenario is fetching a token for frontend calls (see 6.): When the interface.patientdiary.frontendAuthurl +is configured, it is used instead of the interface.patientdiary.authurl here. If it is not configured, no token will +be used.

        +
        Registration of a new person in the external journal
        +

        This process involves several steps that are triggered via the REGISTER button a privileged user can see in the top +right corner when having opened a case or a contact.

        +

        To be able to see this button, the user must have at least one of the following user roles: national user, contact +supervisor, contact officer, community officer, surveillance officer, surveillance supervisor, or admin supervisor.

        +

        First comes a SORMAS-internal validation of contact details. +The person to be registered needs to have at least an email address (or a phone number if that is accepted for +registration, see interface.patientdiary.acceptPhoneContact) to pass this validation.Also, when there are several +email addresses or phone numbers, one of them has to be marked primary contact detail, so that it is clear which contact +detail shall be used.

        +

        Then comes an external validation of the contact details. For this, SORMAS fetches an authentication token as in 1. +Then it sends a GET request to the following URL for each contact detail to be used in the external journal:

        +

        GET [interface.patientdiary.probandsurl]/probands?q=[URL-encoded-query-parameter-and-value], with a header like +x-access-token: [token].

        +

        The [URL-encoded-query-parameter-and-value] consists of a parameter-value-pair. The parameter is either Email or +Mobile phone. The value holds the contact detail to be validated.

        +

        An unencoded example for this is "Email" = "example@example.de", the URL-encoded version is +%22Email%22%20%3D%20%22example%40example.de%22.

        +

        [token] is replaced with the token fetched for authorization.

        +

        The CURL equivalent for an exemplary call is \ +curl --request GET 'https://probands-URL.com//probands?q=%22Email%22%20%3D%20%22example%40example.de%22' --header 'x-access-token: my-access-token'.

        +

        Expected result is a PatientDiaryQueryResponse which information about any person already registered in the external +journal and using the same contact detail.

        +

        It needs to be structured as follows: +```json lines +PatientDiaryQueryResponse { + total: integer, + count: integer, + results: List +}

        +
        
        +- `total` should state how many persons are registered in the external journal (this information is currently never used in SORMAS).
        +
        +- `count` should state how many registered persons using the same contact detail were found.
        +
        +- `results` need to contain a PatientDiaryPersonData for each match:
        +
        +```json lines
        +PatientDiaryPersonData {
        +    _id: string,
        +    idatId: PatientDiaryIdatId
        +}
        +
        +
          +
        • _id should be a unique identifier for the person this data is about (this information is currently never used in SORMAS)
        • +
        +

        The PatientDiaryIdatId needs to be structured as follows:

        +

        ```json lines +PatientDiaryIdatId{ + idat: PatientDiaryPersonDto +}

        +
        
        +The `PatientDiaryPersonDto` holds the actual person data:
        +
        +```json lines
        +PatientDiaryPersonDto{
        +    personUUID: string,
        +    firstName: string,
        +    lastName: string,
        +    gender: string,
        +    birthday: string,
        +    contactInformation: PatientDiaryContactInformation,
        +    endDate: string
        +}
        +
        +
        +
          +
        • personUUID should be the UUID of the person in SORMAS. This UUID is used to sync with external journals (this information is currently never used in SORMAS).
        • +
        • firstName and lastName need to hold the first and last name of the person.
        • +
        • gender should hold the persons gender (this information is currently never used in SORMAS).
        • +
        • birthday should hold the person's birthday (this information is currently never used in SORMAS).
        • +
        • contactInformation should hold the contact information of that person, which should for logical reasons always contain (at least) the contact detail provided by SORMAS in the query.
        • +
        • endDate should hold the date after which follow up is supposed to be stopped by the external journal.
        • +
        +

        ```json lines +PatientDiaryIdatId { + email: string, + phone: PatientDiaryPhone +}

        +
        
        +- `email` should hold the email address for the person
        +- `phone` should hold the phone number of that person:
        +
        +```json lines
        +PatientDiaryPhone {
        +    number: string,
        +    internationalNumber: string,
        +    nationalNumber: string,
        +    countryCode: string,
        +    dialCode: string,
        +}
        +
        +

        To put this all together, here is an example PatientDiaryQueryResponse with one person using the same contact detail:

        +
        {
        +    "total" : 100,
        +    "count" : 1,
        +    "results" : [{
        +        "_id" : "60586691d4c30700119515c8",
        +        "idatId" : {
        +            "idat" : {
        +            "contactInformation" : {
        +                "phone" : null,
        +                "email" : "example@example.de"
        +            },
        +            "personUUID" : "RMTEF2-UZXCXE-7YBJK6-KUMSSEME",
        +              "firstName" : "Maria",
        +              "lastName" : "Muster",
        +              "gender" : "female",
        +              "birthday" : null
        +            }
        +        }
        +    }]
        +}
        +
        +

        SORMAS allows to continue with the registration of a new person only when the person has a unique first name, so when +all persons of the response have a different one (or if the response does not contain any matches, which needs to show +in PatientDiaryQueryResponse.count == 0). This validation is necessary to avoid confusion of person related data in +some external journals.

        +

        When there are no validation errors in the process described above, SORMAS fetches an authentication token as described +in 1. and then uses it to request the external journal to register the person:

        +

        POST [interface.patientdiary.probandsurl]/external-data/[personUUID].

        +

        The [personUUID] is replaced with the UUID of the person, which is later used to sync data between the external +journal and SORMAS.

        +

        Expected response body: +```json lines +{ + "success" : [boolean], + "message" : [messageString] +}

        +
        `[boolean]` is expected to be true in case of successful registration. SORMAS then sets the symptom journal status to
        +`REGISTERED` and displays the message to the user.
        +
        +When `[boolean]` is false, the message is shown as an error to the user.
        +
        +To fetch data relevant for the registration, the external journal can use the `/visits-external/person/{personUuid}`
        +API endpoint described below.
        +
        +##### Synchronization of person data changed in SORMAS
        +
        +It may happen that person data (like a contact detail) gets changed after a person is registered in an external journal.
        +SORMAS notifies external journals about such a change with first fetching an authentication token as descriced in 1.,
        +and the using this token for this request:
        +
        +`PUT [interface.patientdiary.probandsurl]/external-data/[personUUID]`
        +
        +The external journal is expected to refetch the person data via the `/visits-external/person/{personUuid}` API endpoint
        +described below and save the changes.
        +
        +After re-fetching the person data, the symptom journal does its own internal validation and responds to SORMAS with the
        +synchronization result, containing eventual validation errors.
        +
        +The expected response body:
        +```json lines
        +{
        +    "success" : [boolean],
        +    "message" : [messageString],
        +    "errors" : [{
        +        "errorKey": [errorString]
        +      }]
        +}
        +
        +

        If the changes were done manually by a user from the person edit form, the synchronization result is shown to the user +in a popup window, so that the user can fix eventual errors and resynchronize the person data.

        +
        Upload of symptom journal data to SORMAS
        +

        For this, the /visits-external API endpoint has to be used. This is described below.

        +
        Upload of a symptom journal status to SORMAS
        +

        For this, the /visits-external/person/{personUuid}/status API endpoint is to be used. This is described below.

        +
        Opening a person in the external journal from within SORMAS
        +

        Once the symptom journal status of a person is set to REGISTERED or ACCEPTED, the external journal button in the +SORMAS-UI changes. It does not provide a registration anymore, but the options to open the person in the external +journal and to cancel external follow up. This button can be found when having opened a contact (or a case if the case +follow-up feature is enabled in SORMAS) in the top right corner. If the user chooses to open the person in the external +journal, SORMAS opens a new browser tab with the following URL:

        +

        [interface.patientdiary.url]/data?q=[personUuid]&queryKey=sicFieldIdentifier

        +

        SORMAS expects the external journal to present a view of the person there.

        +

        If the interface.patientdiary.frontendAuthurl is configured, SORMAS fetches an authentication token as described in 1, +and appends it to the URL:

        +

        [interface.patientdiary.url]/data?q=[personUuid]&queryKey=sicFieldIdentifier&token=[token]

        +
        Deletion of a person from an external journal
        +

        As described above, the journal button can offer the option to cancel external follow up. If a user choses this option, +SORMAS fetches an authentication token as described in 1., and uses it to request:

        +

        DELETE [interface.patientdiary.probandsurl]/external-data/[personUUID]

        +

        Expected response body: +json lines +{ + "success" : [boolean], + "message" : [messageString] +}

        +

        [boolean] is expected to be true in case of successful deletion. SORMAS then sets the symptom journal status to +DELETED and displays the message to the user.

        +

        When [boolean] is false, the message is shown as an error to the user.

        +

        Please note that this does not affect any follow-up status. Cancelling follow up of a contact or case is independent +of cancelling external follow up of a person.

        + + + + + + +
        +
        + + +
        + +
        + + + +
        +
        +
        +
        + + + + + + + + + \ No newline at end of file