Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Commit 70fa407

Browse files
authored
Merge branch 'development' into feat-3977_keycloak_password_policy (SORMAS-Foundation#3988)
* SORMAS-Foundation#3977: Add default password policy and documentation about the Keycloak configuration * SORMAS-Foundation#3977: Fixed typo * SORMAS-Foundation#3977: Added missing import
1 parent d6a098e commit 70fa407

4 files changed

Lines changed: 91 additions & 10 deletions

File tree

SERVER_SETUP.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ In case Keycloak is set up alongside an already running instance of SORMAS, thes
140140
2. Login to SORMAS and trigger the **Sync Users** button from the **Users** page
141141
3. This will sync users to Keycloak keeping their original password - see [SORMAS Keycloak Service Provider](sormas-keycloak-service-provider/README.md) for more information about this
142142

143+
### Keycloak configuration
144+
145+
More about the default configuration and how to customize can be found here [Keycloak](sormas-base/doc/keycloak.md)
146+
143147
## Web Server Setup
144148

145149
### Apache Web Server

sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonService.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package de.symeda.sormas.backend.person;
1919

2020
import static de.symeda.sormas.backend.ExtendedPostgreSQL94Dialect.SIMILARITY_OPERATOR;
21+
import static de.symeda.sormas.backend.common.CriteriaBuilderHelper.and;
2122

2223
import java.sql.Timestamp;
2324
import java.util.Collections;
@@ -311,24 +312,24 @@ public List<PersonNameDto> getMatchingNameDtos(PersonSimilarityCriteria criteria
311312
Predicate activeCasesFilter = caseService.createActiveCasesFilter(cb, personCaseJoin);
312313
Predicate caseUserFilter = caseService.createUserFilter(cb, personQuery, personCaseJoin);
313314
Predicate personCasePredicate =
314-
CriteriaBuilderHelper.and(cb, personCaseJoin.get(Case.ID).isNotNull(), activeCasesFilter, caseUserFilter);
315+
and(cb, personCaseJoin.get(Case.ID).isNotNull(), activeCasesFilter, caseUserFilter);
315316

316317
// Persons of active contacts
317318
Predicate activeContactsFilter = contactService.createActiveContactsFilter(cb, personContactJoin);
318319
Predicate contactUserFilter = contactService.createUserFilter(cb, personQuery, personContactJoin);
319320
Predicate personContactPredicate =
320-
CriteriaBuilderHelper.and(cb, personContactJoin.get(Contact.ID).isNotNull(), contactUserFilter, activeContactsFilter);
321+
and(cb, personContactJoin.get(Contact.ID).isNotNull(), contactUserFilter, activeContactsFilter);
321322

322323
// Persons of event participants in active events
323324
Predicate activeEventParticipantsFilter = eventParticipantService.createActiveEventParticipantsFilter(cb, personEventParticipantJoin);
324325
Predicate eventParticipantUserFilter = eventParticipantService.createUserFilter(cb, personQuery, personEventParticipantJoin);
325326
Predicate personEventParticipantPredicate =
326-
CriteriaBuilderHelper.and(cb, personEventParticipantJoin.get(EventParticipant.ID).isNotNull(), activeEventParticipantsFilter, eventParticipantUserFilter);
327+
and(cb, personEventParticipantJoin.get(EventParticipant.ID).isNotNull(), activeEventParticipantsFilter, eventParticipantUserFilter);
327328

328329
caseContactEventParticipantLinkPredicate =
329330
CriteriaBuilderHelper.or(cb, personCasePredicate, personContactPredicate, personEventParticipantPredicate);
330331

331-
personQuery.where(CriteriaBuilderHelper.and(cb, personSimilarityFilter, caseContactEventParticipantLinkPredicate));
332+
personQuery.where(and(cb, personSimilarityFilter, caseContactEventParticipantLinkPredicate));
332333
personQuery.distinct(true);
333334

334335
TypedQuery<PersonNameDto> query = em.createQuery(personQuery);
@@ -410,34 +411,34 @@ public Predicate buildSimilarityCriteriaFilter(PersonSimilarityCriteria criteria
410411

411412
String name = criteria.getFirstName() + " " + criteria.getLastName();
412413

413-
filter = CriteriaBuilderHelper.and(cb, filter, cb.isTrue(cb.function(SIMILARITY_OPERATOR, boolean.class, nameExpr, cb.literal(name))));
414+
filter = and(cb, filter, cb.isTrue(cb.function(SIMILARITY_OPERATOR, boolean.class, nameExpr, cb.literal(name))));
414415
}
415416

416417
if (criteria.getSex() != null) {
417-
filter = CriteriaBuilderHelper.and(cb, filter, cb.or(cb.isNull(personFrom.get(Person.SEX)), cb.equal(personFrom.get(Person.SEX), criteria.getSex())));
418+
filter = and(cb, filter, cb.or(cb.isNull(personFrom.get(Person.SEX)), cb.equal(personFrom.get(Person.SEX), criteria.getSex())));
418419
}
419420
if (criteria.getBirthdateYYYY() != null) {
420-
filter = CriteriaBuilderHelper.and(
421+
filter = and(
421422
cb,
422423
filter,
423424
cb.or(
424425
cb.isNull(personFrom.get(Person.BIRTHDATE_YYYY)),
425426
cb.equal(personFrom.get(Person.BIRTHDATE_YYYY), criteria.getBirthdateYYYY())));
426427
}
427428
if (criteria.getBirthdateMM() != null) {
428-
filter = CriteriaBuilderHelper.and(
429+
filter = and(
429430
cb,
430431
filter,
431432
cb.or(cb.isNull(personFrom.get(Person.BIRTHDATE_MM)), cb.equal(personFrom.get(Person.BIRTHDATE_MM), criteria.getBirthdateMM())));
432433
}
433434
if (criteria.getBirthdateDD() != null) {
434-
filter = CriteriaBuilderHelper.and(
435+
filter = and(
435436
cb,
436437
filter,
437438
cb.or(cb.isNull(personFrom.get(Person.BIRTHDATE_DD)), cb.equal(personFrom.get(Person.BIRTHDATE_DD), criteria.getBirthdateDD())));
438439
}
439440
if (!StringUtils.isBlank(criteria.getNationalHealthId())) {
440-
filter = CriteriaBuilderHelper.and(
441+
filter = and(
441442
cb,
442443
filter,
443444
cb.or(

sormas-base/doc/keycloak.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Keycloak
2+
3+
Open Source Identity and Access Management.
4+
In SORMAS Keycloak is available as an alternative authentication provider to the default authentication method.
5+
6+
Current version is: Keycloak 12
7+
8+
## Setup
9+
10+
To set up Keycloak check the guide here [Keycloak Setup](../../SERVER_SETUP.md#keycloak-server)
11+
12+
## SORMAS Realm
13+
14+
The SORMAS Realm in Keycloak contains all the configuration which are specific to the SORMAS Project.
15+
All the configuration is part of the [SORMAS.json](../setup/keycloak/SORMAS.json) file.
16+
17+
### Configuration summary
18+
19+
#### Login & Authentication
20+
21+
* **Duplicate emails** are allowed in order to support the same requirement as SORMAS which in some installations require
22+
admin support for some users, in which case the admin will use her own email address
23+
* **No login with emails** due to the previous point
24+
* **Password Policy** comes predefined since version 1.54 of the SORMAS-Project with the following default settings
25+
* Length of minimum 12 characters
26+
* At least 1 upper case letter
27+
* At least 1 lower case letter
28+
* At least 1 digit
29+
* At least 1 special character
30+
* **OTP** is supported by default trough the *Google Authenticator* or *Free OTP* by has to be activated from the
31+
Keycloak Admin console
32+
* **Forgot Password** is enabled by default
33+
* **sormas-sha256** is an encryption algorithm which comes packaged with Keycloak to support transition of existing
34+
environments towards the Keycloak Authentication Provider
35+
36+
#### Clients
37+
38+
The SORMAS Realm relies on 4 clients:
39+
40+
* **sormas-ui** - handles access to the SORMAS wen UI
41+
* **sormas-app** - handles access to the SORMAS Android App
42+
* **sormas-rest** - handles access to the SORMAS API
43+
* **sormas-backend** - handles SORMAS server requests
44+
45+
#### Roles
46+
47+
The role management is handled by SORMAS however as a pre-validation Keycloak is also configured with a few roles which
48+
are required for certain API access:
49+
* **USER** - required by default for any API access
50+
* **REST_USER** - required for most API endpoints (main purpose is for the SurvNet converter)
51+
* **REST_EXTERNAL_VISITS_USER** - required by Symptom Journals which are connected to SORMAS
52+
* **SORMAS_TO_SORMAS_CLIENT** - required by other SORMAS instance to access the current SORMAS instance
53+
54+
#### Email
55+
56+
Email configurations are optional and are not part of the default configuration.
57+
58+
In case the system relies on users activating their own accounts it's required to configure these settings.
59+
60+
#### Custom Configuration
61+
62+
The configuration provided by default is the minimum required configuration for Keycloak to work together with SORMAS.
63+
64+
The Keycloak configuration can be adjusted by any user with admin rights, however **beware** that any change to the default
65+
configuration might render the system unusable.
66+
67+
The following configurations are most likely to be environment specific:
68+
69+
* **[Email Settings](https://www.keycloak.org/docs/12.0/server_admin/#_email)**
70+
* make sure to set an email for the admin user, so the **Test connection** feature works
71+
* **[Password Policies](https://www.keycloak.org/docs/latest/server_admin/#_password-policies)**
72+
* The **Password Blacklist** policy can only be configured with access to the host machine
73+
* **[OTP Policies](https://www.keycloak.org/docs/latest/server_admin/#otp-policies)**
74+
* Can be activated by default for all user by marking **Basic Auth Password+OTP** as required in the
75+
**Authentication>Flows** section, then mark it as default in the **Authentication>Required** section

sormas-base/setup/keycloak/SORMAS.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@
410410
"requiredCredentials": [
411411
"password"
412412
],
413+
"passwordPolicy": "length(12) and upperCase(1) and lowerCase(1) and digits(1) and specialChars(1) and notUsername(undefined) and notEmail(undefined)",
413414
"otpPolicyType": "totp",
414415
"otpPolicyAlgorithm": "HmacSHA1",
415416
"otpPolicyInitialCounter": 0,

0 commit comments

Comments
 (0)