├── dpppt-config-backend ├── .gitignore ├── src │ ├── main │ │ ├── resources │ │ │ ├── application-local.properties │ │ │ ├── static │ │ │ │ └── ios_agency_image │ │ │ │ │ ├── swiss.png │ │ │ │ │ ├── swiss2x.png │ │ │ │ │ └── swiss3x.png │ │ │ ├── application-mock-forceupdate.properties │ │ │ ├── application-mock-infobox.properties │ │ │ ├── i18n │ │ │ │ ├── messages_v1_legacy_ti.properties │ │ │ │ ├── messages_v1_legacy_en.properties │ │ │ │ ├── messages_v1_legacy_hr.properties │ │ │ │ ├── messages_v1_legacy_bs.properties │ │ │ │ ├── messages_v1_legacy_sr.properties │ │ │ │ ├── messages_v1_legacy_tr.properties │ │ │ │ ├── messages_v1_legacy_de.properties │ │ │ │ ├── messages_v1_legacy_pt.properties │ │ │ │ ├── messages_v1_legacy_it.properties │ │ │ │ ├── messages_v1_legacy_sq.properties │ │ │ │ ├── messages_v1_legacy_rm.properties │ │ │ │ ├── messages_v1_legacy_fr.properties │ │ │ │ └── messages_v1_legacy_es.properties │ │ │ ├── application.properties │ │ │ ├── local-logback.xml │ │ │ └── banner.txt │ │ └── java │ │ │ └── org │ │ │ └── dpppt │ │ │ └── switzerland │ │ │ └── backend │ │ │ └── sdk │ │ │ └── config │ │ │ └── ws │ │ │ ├── security │ │ │ └── signature │ │ │ │ ├── checksig.sh │ │ │ │ └── SignatureResponseWrapper.java │ │ │ ├── config │ │ │ ├── configbeans │ │ │ │ └── ActuatorSecurityConfig.java │ │ │ ├── BaseSecurity.java │ │ │ ├── WSAbnConfig.java │ │ │ ├── WSDevConfig.java │ │ │ ├── WSProdConfig.java │ │ │ ├── WSTestConfig.java │ │ │ ├── ActuatorSecurity.java │ │ │ ├── mock │ │ │ │ ├── MockForceUpdateConfig.java │ │ │ │ └── MockInfoBoxConfig.java │ │ │ └── WSBaseConfig.java │ │ │ ├── interceptor │ │ │ └── HeaderInjector.java │ │ │ ├── model │ │ │ ├── Language.java │ │ │ ├── TestLocation.java │ │ │ ├── VaccinationBookingCanton.java │ │ │ ├── FaqEntry.java │ │ │ ├── SDKConfig.java │ │ │ ├── VaccinationBookingInfo.java │ │ │ ├── Canton.java │ │ │ ├── WhatToDoPositiveTestTexts.java │ │ │ ├── InfoBox.java │ │ │ ├── GAENSDKConfig.java │ │ │ ├── TestLocationCollection.java │ │ │ ├── InfoBoxCollection.java │ │ │ ├── WhatToDoPositiveTestTextsCollection.java │ │ │ └── ConfigResponse.java │ │ │ ├── Application.java │ │ │ ├── poeditor │ │ │ └── Messages.java │ │ │ ├── filter │ │ │ └── ResponseWrapperFilter.java │ │ │ ├── helper │ │ │ ├── VaccinationInfoHelper.java │ │ │ ├── TestLocationHelper.java │ │ │ ├── MockHelper.java │ │ │ └── IOS136InfoBoxHelper.java │ │ │ └── semver │ │ │ └── Version.java │ └── test │ │ ├── resources │ │ └── http │ │ │ ├── http-client.private.env.json │ │ │ ├── dp3t-config-ws.http │ │ │ └── http-client.env.json │ │ └── java │ │ └── org │ │ └── dpppt │ │ └── switzerland │ │ └── backend │ │ └── sdk │ │ └── config │ │ └── ws │ │ ├── GaenConfigControllerWithoutActuatorSecurityTest.java │ │ ├── TestApplication.java │ │ ├── GaenConfigControllerTest.java │ │ └── VersionTest.java └── getTranslations.py ├── renovate.json ├── .gitignore ├── Makefile ├── .github └── workflows │ ├── tagged_release.yaml │ └── maven.yml ├── README.md ├── documentation └── yaml │ └── sdk.yaml └── LICENSE /dpppt-config-backend/.gitignore: -------------------------------------------------------------------------------- 1 | generated 2 | target 3 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/application-local.properties: -------------------------------------------------------------------------------- 1 | logging.config=classpath:local-logback.xml -------------------------------------------------------------------------------- /dpppt-config-backend/src/test/resources/http/http-client.private.env.json: -------------------------------------------------------------------------------- 1 | { 2 | "local": { 3 | }, 4 | "dev": { 5 | }, 6 | "prod": { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/test/resources/http/dp3t-config-ws.http: -------------------------------------------------------------------------------- 1 | ### config request 2 | 3 | GET {{baseUrl}}/v1/config?appversion={{appversion}}&osversion={{osversion}}&buildnr={{buildnr}} 4 | 5 | ### 6 | 7 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/static/ios_agency_image/swiss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwissCovid/swisscovid-config-backend/HEAD/dpppt-config-backend/src/main/resources/static/ios_agency_image/swiss.png -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/static/ios_agency_image/swiss2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwissCovid/swisscovid-config-backend/HEAD/dpppt-config-backend/src/main/resources/static/ios_agency_image/swiss2x.png -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/static/ios_agency_image/swiss3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwissCovid/swisscovid-config-backend/HEAD/dpppt-config-backend/src/main/resources/static/ios_agency_image/swiss3x.png -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "reviewers": [ 6 | "gstoehld" 7 | ], 8 | "packageRules": [ 9 | { 10 | "matchPackagePatterns": ["*"], 11 | "groupName": "dependencies" 12 | } 13 | ], 14 | "schedule": [ 15 | "before 3am on the first day of the month" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/security/signature/checksig.sh: -------------------------------------------------------------------------------- 1 | curl --silent localhost:8080/v1/testinfobox/config\?appversion=1.0\&osversion=1.0 2>/dev/null | shasum -a 256 - && curl -v --silent localhost:8080/v1/testinfobox/config\?appversion=1.0\&osversion=1.0 2>&1 | awk '/.*Digest:/ { gsub("sha-256=", "", $3); print $3}' -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/application-mock-forceupdate.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020 Ubique Innovation AG 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | # 8 | # SPDX-License-Identifier: MPL-2.0 9 | # 10 | spring.main.allow-bean-definition-overriding=true -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/application-mock-infobox.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020 Ubique Innovation AG 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | # 8 | # SPDX-License-Identifier: MPL-2.0 9 | # 10 | spring.main.allow-bean-definition-overriding=true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | .DS_Store 26 | **/.project 27 | **/org.eclipse* 28 | **/.classpath 29 | dpppt-config-backend/.sts4-cache/classpath-data.json 30 | 31 | dpppt-config-backend/dp3t-config-ws.log.1 32 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/configbeans/ActuatorSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.config.configbeans; 2 | 3 | public class ActuatorSecurityConfig { 4 | private final String username; 5 | private final String password; 6 | 7 | public String getUsername() { 8 | return username; 9 | } 10 | 11 | public String getPassword() { 12 | return password; 13 | } 14 | 15 | public ActuatorSecurityConfig(String username, String password) { 16 | this.username = username; 17 | this.password = password; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/BaseSecurity.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 6 | 7 | @Configuration 8 | public class BaseSecurity extends WebSecurityConfigurerAdapter { 9 | @Override 10 | protected void configure(HttpSecurity http) throws Exception { 11 | http.authorizeRequests().antMatchers("/v1/**").permitAll(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/test/resources/http/http-client.env.json: -------------------------------------------------------------------------------- 1 | { 2 | "local": { 3 | "baseUrl": "localhost:8080", 4 | "appversion": "ios-1.1.0", 5 | "osversion": "ios14.1", 6 | "buildnr": "ios-210119.1234.123" 7 | }, 8 | "dev": { 9 | "baseUrl": "https://www.pt-d.bfs.admin.ch", 10 | "appversion": "ios-1.1.0", 11 | "osversion": "ios14.1", 12 | "buildnr": "ios-210119.1234.123" 13 | }, 14 | "test": { 15 | "baseUrl": "https://www.pt-t.bfs.admin.ch", 16 | "appversion": "ios-1.1.0", 17 | "osversion": "ios14.1", 18 | "buildnr": "ios-210119.1234.123" 19 | }, 20 | "prod": { 21 | "baseUrl": "https://www.pt.bfs.admin.ch", 22 | "appversion": "ios-1.1.0", 23 | "osversion": "ios14.1", 24 | "buildnr": "ios-210119.1234.123" 25 | } 26 | } -------------------------------------------------------------------------------- /dpppt-config-backend/src/test/java/org/dpppt/switzerland/backend/sdk/config/ws/GaenConfigControllerWithoutActuatorSecurityTest.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws; 2 | 3 | import org.junit.Before; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 6 | 7 | @SpringBootTest( 8 | properties = { 9 | "management.endpoints.enabled-by-default=true", 10 | "management.endpoints.web.exposure.include=*" 11 | }) 12 | public class GaenConfigControllerWithoutActuatorSecurityTest extends BaseControllerTest { 13 | 14 | @Before 15 | public void setup() throws Exception { 16 | super.setup(); 17 | this.mockMvc = 18 | MockMvcBuilders.webAppContextSetup(webApplicationContext) 19 | .addFilter(filter, "/*") 20 | .build(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/test/java/org/dpppt/switzerland/backend/sdk/config/ws/TestApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws; 12 | 13 | import org.springframework.boot.autoconfigure.SpringBootApplication; 14 | import org.springframework.context.annotation.ComponentScan; 15 | 16 | @ComponentScan( 17 | basePackages = { 18 | "org.dpppt.switzerland.backend.sdk.config.ws.config", 19 | "ch.admin.bag.covidcertificate.log", 20 | "ch.admin.bag.covidcertificate.rest" 21 | }) 22 | @SpringBootApplication 23 | public class TestApplication {} 24 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/interceptor/HeaderInjector.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.interceptor; 2 | 3 | import java.util.Map; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | import org.springframework.web.servlet.HandlerInterceptor; 7 | 8 | public class HeaderInjector implements HandlerInterceptor { 9 | private final Map headers; 10 | 11 | public HeaderInjector(Map headers) { 12 | this.headers = headers; 13 | } 14 | 15 | @Override 16 | public boolean preHandle( 17 | HttpServletRequest request, HttpServletResponse response, Object handler) 18 | throws Exception { 19 | if (headers != null) { 20 | for (var header : headers.keySet()) { 21 | response.setHeader(header, headers.get(header)); 22 | } 23 | } 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/Language.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonValue; 4 | import java.util.List; 5 | import java.util.Locale; 6 | 7 | public enum Language { 8 | BS("bs"), 9 | DE("de"), 10 | EN("en"), 11 | ES("es"), 12 | FR("fr"), 13 | HR("hr"), 14 | IT("it"), 15 | PT("pt"), 16 | RM("rm"), 17 | SQ("sq"), 18 | SR("sr"), 19 | TI("ti"), 20 | TR("tr"); 21 | 22 | private String key; 23 | 24 | Language(String key) { 25 | this.key = key; 26 | } 27 | 28 | @JsonValue 29 | public String getKey() { 30 | return key; 31 | } 32 | 33 | public Locale toLocale() { 34 | return Locale.forLanguageTag(this.getKey()); 35 | } 36 | 37 | /** 38 | * for use during development when missing translations 39 | * 40 | * @return 41 | */ 42 | public static List deOnly() { 43 | return List.of(Language.DE); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/TestLocation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | public class TestLocation { 14 | 15 | private String region; 16 | private String url; 17 | 18 | public TestLocation() {} 19 | 20 | public TestLocation(String region, String url) { 21 | super(); 22 | this.region = region; 23 | this.url = url; 24 | } 25 | 26 | public String getRegion() { 27 | return region; 28 | } 29 | 30 | public void setRegion(String region) { 31 | this.region = region; 32 | } 33 | 34 | public String getUrl() { 35 | return url; 36 | } 37 | 38 | public void setUrl(String url) { 39 | this.url = url; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/VaccinationBookingCanton.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 2 | 3 | import javax.validation.constraints.NotNull; 4 | 5 | public class VaccinationBookingCanton { 6 | @NotNull private String name; 7 | @NotNull private String iconAndroid; 8 | @NotNull private String iconIos; 9 | @NotNull private String linkUrl; 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | public void setName(String name) { 16 | this.name = name; 17 | } 18 | 19 | public String getIconAndroid() { 20 | return iconAndroid; 21 | } 22 | 23 | public void setIconAndroid(String iconAndroid) { 24 | this.iconAndroid = iconAndroid; 25 | } 26 | 27 | public String getIconIos() { 28 | return iconIos; 29 | } 30 | 31 | public void setIconIos(String iconIos) { 32 | this.iconIos = iconIos; 33 | } 34 | 35 | public String getLinkUrl() { 36 | return linkUrl; 37 | } 38 | 39 | public void setLinkUrl(String linkUrl) { 40 | this.linkUrl = linkUrl; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws; 12 | 13 | import org.springframework.boot.SpringApplication; 14 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 15 | import org.springframework.context.annotation.ComponentScan; 16 | import org.springframework.context.annotation.Configuration; 17 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 18 | 19 | @Configuration 20 | @ComponentScan( 21 | basePackages = { 22 | "org.dpppt.switzerland.backend.sdk.config.ws.config", 23 | "ch.admin.bag.covidcertificate.log", 24 | "ch.admin.bag.covidcertificate.rest" 25 | }) 26 | @EnableAutoConfiguration 27 | @EnableWebMvc 28 | public class Application { 29 | 30 | public static void main(String[] args) { 31 | SpringApplication.run(Application.class, args); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_ti.properties: -------------------------------------------------------------------------------- 1 | ti: 2 | v1_legacy_inform_detail_box_subtitle: ብኮቪድኮድ 3 | v1_legacy_inform_detail_box_title: ሰንሰለት ለበዳ ሕማም ምቁራጽ 4 | v1_legacy_inform_detail_box_button: ኮቪድኮድ ኣእትው 5 | v1_legacy_inform_detail_box_text: ኮቪድኮድ ብምእታውኩም ነቲ ኣፕ ውጽኢት መርመራኹም ፖዚቲቭ ንሓድሽ ኮሮናቫይረስ ከምዝኾነ ትሕብሩ። 6 | v1_legacy_inform_detail_faq1_title: ኮቪድኮድ እንታይ እዩ፧ 7 | v1_legacy_inform_detail_faq1_text: ሰባት ኮሮናቫይረስ ከምዘለዎም ምስዝረጋግጽ / ፖዚቲቭ ምስኮኑ (ናይ PCR መርመራ ወይ ቅልጡፍ መርመራ ጸረ-ፈርዒ) ሓደ ኮቪድ-ኮድ ክወሃቡ እዮም።\n\nሽዑ ጥራይ ዝተረጋገጹ ጉዳያት በቲ ኤፕ ክሕበሩ ከምዝኾኑ ተረጋጊጹ። 8 | v1_legacy_inform_detail_faq2_title: እንታይ እዩ ዝስደድ፧ 9 | v1_legacy_inform_detail_faq2_text: ውልቃዊ ሓበሬታ ዘይኮነ፣ ጥራይ እቶም ግላውያን መፋትሕ እዮም ዝስደዱ።\n \nብኸምዚ መንገዲ ካልኦት ተጠቀምቲ ስዊስኮቪድ ተኽእሎ መልከፍቲ ሕማም ከምዘሎ ይፈልጡ። 10 | v1_legacy_inform_detail_faq3_text: ዋላኳ ዝኾነ ሓበሬታ ብዛዕባኹም እንተዘይተሰደደ ዝኾነ ሰብ በቲ ዕለት ጌሩ ምርኻብኩም ክዝክሮ ይኽእል እዩ። 11 | v1_legacy_inform_detail_faq3_title: ስቱር ድዩ፧ 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: ክትሰምዑ ኣይትኽእሉን ወይ ናይ ምስማዕ ስንክልና ስለዘለና ነቲ ሓበሬታ ስልኪ ኮሮናቫይረስ ክትድውሉ ኣይትኽእሉን፧\n\nሽዑ ኢመይል ናብ covid-support@medgate.ch ስደዱ ኢኹም 14 | v1_legacy_inform_detail_infobox1_text: ፖዚቲቭ ተመርሚርኩም (ናይ PCR መርመራ ወይ ቅልጡፍ መርመራ ጸረ-ፈርዒ) ከምኡ'ውን ድሕሪ 4 ሰዓት ገና ናይ ኮቪድ ኮድ ኣይተወሃብኩምን፧ ሽዑ እቲ ኢንፎላይን ኮሮናቫይረስ ርኸቡ ኢኹም፥ 15 | v1_legacy_inform_detail_infobox1_title: ናይ ኮቪድ ኮድ ገና የብልኩምን፧ 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ###################### 2 | # Makefile # 3 | ###################### 4 | 5 | FILE_NAME = documentation.tex 6 | 7 | LATEX = xelatex 8 | BIBER = biber 9 | RUSTY_SWAGGER = rusty-swagger 10 | 11 | all: clean all1 12 | all1: clean updateproject updatedoc swagger la la2 la3 13 | no: clean updateproject updatedoc swagger la la2 14 | docker-build: updateproject docker 15 | doc: updatedoc swagger la la2 la3 16 | 17 | updateproject: 18 | mvn -f dpppt-config-backend/pom.xml install 19 | 20 | updatedoc: 21 | mvn -f dpppt-config-backend/pom.xml install -Dmaven.test.skip=true 22 | mvn springboot-swagger-3:springboot-swagger-3 -f dpppt-config-backend/pom.xml 23 | mkdir -p documentation/yaml 24 | cp dpppt-config-backend/generated/swagger/swagger.yaml documentation/yaml/sdk.yaml 25 | 26 | swagger: 27 | cd documentation; $(RUSTY_SWAGGER) --file ../dpppt-config-backend/dpppt-config-backend/generated/swagger/swagger.yaml 28 | 29 | la: 30 | cd documentation;$(LATEX) $(FILE_NAME) 31 | bib: 32 | cd documentation;$(BIBER) $(FILE_NAME) 33 | la2: 34 | cd documentation;$(LATEX) $(FILE_NAME) 35 | la3: 36 | cd documentation;$(LATEX) $(FILE_NAME) 37 | show: 38 | cd documentation; open $(FILE_NAME).pdf & 39 | 40 | clean: 41 | @rm -f documentation/*.log documentation/*.aux documentation/*.dvi documentation/*.ps documentation/*.blg documentation/*.bbl documentation/*.out documentation/*.bcf documentation/*.run.xml documentation/*.fdb_latexmk documentation/*.fls documentation/*.toc 42 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/WSAbnConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.config; 12 | 13 | import java.util.Base64; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.context.annotation.Profile; 17 | import org.springframework.scheduling.config.ScheduledTaskRegistrar; 18 | 19 | @Configuration 20 | @Profile("cloud-abn") 21 | public class WSAbnConfig extends WSBaseConfig { 22 | @Value("${vcap.services.ecdsa_cs_abn.credentials.privateKey}") 23 | private String privateKey; 24 | 25 | @Value("${vcap.services.ecdsa_cs_abn.credentials.publicKey}") 26 | public String publicKey; 27 | 28 | @Override 29 | public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {} 30 | 31 | @Override 32 | String getPrivateKey() { 33 | return new String(Base64.getDecoder().decode(privateKey)); 34 | } 35 | 36 | @Override 37 | String getPublicKey() { 38 | return new String(Base64.getDecoder().decode(publicKey)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/WSDevConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.config; 12 | 13 | import java.util.Base64; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.context.annotation.Profile; 17 | import org.springframework.scheduling.config.ScheduledTaskRegistrar; 18 | 19 | @Configuration 20 | @Profile("cloud-dev") 21 | public class WSDevConfig extends WSBaseConfig { 22 | 23 | @Value("${vcap.services.ecdsa_cs_dev.credentials.privateKey}") 24 | private String privateKey; 25 | 26 | @Value("${vcap.services.ecdsa_cs_dev.credentials.publicKey}") 27 | public String publicKey; 28 | 29 | @Override 30 | public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {} 31 | 32 | @Override 33 | String getPrivateKey() { 34 | return new String(Base64.getDecoder().decode(privateKey)); 35 | } 36 | 37 | @Override 38 | String getPublicKey() { 39 | return new String(Base64.getDecoder().decode(publicKey)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/WSProdConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.config; 12 | 13 | import java.util.Base64; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.context.annotation.Profile; 17 | import org.springframework.scheduling.config.ScheduledTaskRegistrar; 18 | 19 | @Configuration 20 | @Profile("cloud-prod") 21 | public class WSProdConfig extends WSBaseConfig { 22 | @Value("${vcap.services.ecdsa_cs_prod.credentials.privateKey}") 23 | private String privateKey; 24 | 25 | @Value("${vcap.services.ecdsa_cs_prod.credentials.publicKey}") 26 | public String publicKey; 27 | 28 | @Override 29 | public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {} 30 | 31 | @Override 32 | String getPrivateKey() { 33 | return new String(Base64.getDecoder().decode(privateKey)); 34 | } 35 | 36 | @Override 37 | String getPublicKey() { 38 | return new String(Base64.getDecoder().decode(publicKey)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/WSTestConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.config; 12 | 13 | import java.util.Base64; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.context.annotation.Profile; 17 | import org.springframework.scheduling.config.ScheduledTaskRegistrar; 18 | 19 | @Configuration 20 | @Profile("cloud-test") 21 | public class WSTestConfig extends WSBaseConfig { 22 | @Value("${vcap.services.ecdsa_cs_test.credentials.privateKey}") 23 | private String privateKey; 24 | 25 | @Value("${vcap.services.ecdsa_cs_test.credentials.publicKey}") 26 | public String publicKey; 27 | 28 | @Override 29 | public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {} 30 | 31 | @Override 32 | String getPrivateKey() { 33 | return new String(Base64.getDecoder().decode(privateKey)); 34 | } 35 | 36 | @Override 37 | String getPublicKey() { 38 | return new String(Base64.getDecoder().decode(publicKey)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_en.properties: -------------------------------------------------------------------------------- 1 | en: 2 | v1_legacy_inform_detail_box_subtitle: With the Covidcode 3 | v1_legacy_inform_detail_box_title: Break the chains of infection 4 | v1_legacy_inform_detail_box_button: Enter the Covidcode 5 | v1_legacy_inform_detail_box_text: By entering the Covidcode, you tell the app that you have tested positive for the coronavirus. 6 | v1_legacy_inform_detail_faq1_title: What is a Covidcode? 7 | v1_legacy_inform_detail_faq1_text: People who test positive for coronavirus (with a PCR test or rapid antigen test) will receive a Covidcode.\n\nThis guarantees that only confirmed cases are reported via the app. 8 | v1_legacy_inform_detail_faq2_title: What information gets sent? 9 | v1_legacy_inform_detail_faq2_text: Only the private keys from your app are sent, no personal data. \n\nOther SwissCovid apps can then check if it is possible that they have been infected. 10 | v1_legacy_inform_detail_faq3_text: Although no personal data relating to you is sent out, it may well be that someone remembers their encounter with you from the date. 11 | v1_legacy_inform_detail_faq3_title: Does anyone find out who I am? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: Are you deaf or hard of hearing and unable to call the coronavirus infoline on the phone?\n\nSend an email to covid-support@medgate.ch. 14 | v1_legacy_inform_detail_infobox1_text: Have you tested positive (PCR test or rapid antigen test) and did not receive a Covidcode after 4 hours?\nIf so, contact the Coronavirus Infoline: 15 | v1_legacy_inform_detail_infobox1_title: Don’t have a Covidcode yet? 16 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_hr.properties: -------------------------------------------------------------------------------- 1 | hr: 2 | v1_legacy_inform_detail_box_subtitle: Sa Covid šifrom... 3 | v1_legacy_inform_detail_box_title: Zaustavljanje lanca širenja infekcije 4 | v1_legacy_inform_detail_box_button: Unos Covid šifre 5 | v1_legacy_inform_detail_box_text: Unosom Covid šifre obaveštavate aplikaciju da ste testirani pozitivno na virus korona. 6 | v1_legacy_inform_detail_faq1_title: Šta je Covid šifra? 7 | v1_legacy_inform_detail_faq1_text: Osobe pozitivno testirane na korona virus (PCR test ili antigen (brzi test) dobijaju kovid šifru.\n\nTime se obezbeđuje da samo potvrđeni slučajevi budu prijavljeni preko aplikacije. 8 | v1_legacy_inform_detail_faq2_title: Šta se šalje? 9 | v1_legacy_inform_detail_faq2_text: Šalju se samo privatni identifikacioni ključevi iz vaše aplikacije, a ne šalju se lični podaci.\n\nTime drugi korisnici aplikacije SwissCovid mogu da saznaju da postoji mogućnost infekcije. 10 | v1_legacy_inform_detail_faq3_text: Iako se lični podaci koji se odnose na vas ne šalju, možda se neko seća svog kontakta sa vama na osnovu datuma. 11 | v1_legacy_inform_detail_faq3_title: Da li ostajem anoniman? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: Da li ste bez sluha ili imate smetnju sluha i ne možete nazvati Infoline Coronavirus?\n\nOnda pošaljite e-mail na covid-support@medgate.ch 14 | v1_legacy_inform_detail_infobox1_text: Pozitivno ste testirani (PCR test ili antigen brzi test) i ni posle 4 sata niste dobili kovid šifru?\nU tom slučaju kontaktirajte Infoline Coronavirus: 15 | v1_legacy_inform_detail_infobox1_title: Još niste dobili Covid šifru? -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_bs.properties: -------------------------------------------------------------------------------- 1 | bs: 2 | v1_legacy_inform_detail_box_subtitle: Sa Covid šifrom... 3 | v1_legacy_inform_detail_box_title: Zaustavljanje lanca širenja infekcije 4 | v1_legacy_inform_detail_box_button: Unos Covid šifre 5 | v1_legacy_inform_detail_box_text: Unosom Covid šifre obaveštavate aplikaciju da ste testirani pozitivno na virus korona. 6 | v1_legacy_inform_detail_faq1_title: Šta je Covid šifra? 7 | v1_legacy_inform_detail_faq1_text: Osobe pozitivno testirane na korona virus (PCR test ili antigen (brzi test) dobijaju kovid šifru.\n\nTime se obezbeđuje da samo potvrđeni slučajevi budu prijavljeni preko aplikacije. 8 | v1_legacy_inform_detail_faq2_title: Šta se šalje? 9 | v1_legacy_inform_detail_faq2_text: Šalju se samo privatni identifikacioni ključevi iz vaše aplikacije, a ne šalju se lični podaci.\n\nTime drugi korisnici aplikacije SwissCovid mogu da saznaju da postoji mogućnost infekcije. 10 | v1_legacy_inform_detail_faq3_text: Iako se lični podaci koji se odnose na vas ne šalju, možda se neko seća svog kontakta sa vama na osnovu datuma. 11 | v1_legacy_inform_detail_faq3_title: Da li ostajem anoniman? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: Da li ste bez sluha ili imate smetnju sluha i ne možete nazvati Infoline Coronavirus?\n\nOnda pošaljite e-mail na covid-support@medgate.ch 14 | v1_legacy_inform_detail_infobox1_text: Pozitivno ste testirani (PCR test ili antigen brzi test) i ni posle 4 sata niste dobili kovid šifru?\nU tom slučaju kontaktirajte Infoline Coronavirus: 15 | v1_legacy_inform_detail_infobox1_title: Još niste dobili Covid šifru? 16 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_sr.properties: -------------------------------------------------------------------------------- 1 | sr: 2 | v1_legacy_inform_detail_box_subtitle: Sa Covid šifrom... 3 | v1_legacy_inform_detail_box_title: Zaustavljanje lanca širenja infekcije 4 | v1_legacy_inform_detail_box_button: Unos Covid šifre 5 | v1_legacy_inform_detail_box_text: Unosom Covid šifre obaveštavate aplikaciju da ste testirani pozitivno na virus korona. 6 | v1_legacy_inform_detail_faq1_title: Šta je Covid šifra? 7 | v1_legacy_inform_detail_faq1_text: Osobe pozitivno testirane na korona virus (PCR test ili antigen (brzi test) dobijaju kovid šifru.\n\nTime se obezbeđuje da samo potvrđeni slučajevi budu prijavljeni preko aplikacije. 8 | v1_legacy_inform_detail_faq2_title: Šta se šalje? 9 | v1_legacy_inform_detail_faq2_text: Šalju se samo privatni identifikacioni ključevi iz vaše aplikacije, a ne šalju se lični podaci.\n\nTime drugi korisnici aplikacije SwissCovid mogu da saznaju da postoji mogućnost infekcije. 10 | v1_legacy_inform_detail_faq3_text: Iako se lični podaci koji se odnose na vas ne šalju, možda se neko seća svog kontakta sa vama na osnovu datuma. 11 | v1_legacy_inform_detail_faq3_title: Da li ostajem anoniman? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: Da li ste bez sluha ili imate smetnju sluha i ne možete nazvati Infoline Coronavirus?\n\nOnda pošaljite e-mail na covid-support@medgate.ch 14 | v1_legacy_inform_detail_infobox1_text: Pozitivno ste testirani (PCR test ili antigen brzi test) i ni posle 4 sata niste dobili kovid šifru?\nU tom slučaju kontaktirajte Infoline Coronavirus: 15 | v1_legacy_inform_detail_infobox1_title: Još niste dobili Covid šifru? 16 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_tr.properties: -------------------------------------------------------------------------------- 1 | tr: 2 | v1_legacy_inform_detail_box_subtitle: Kovid kodu ile... 3 | v1_legacy_inform_detail_box_title: Enfeksiyon zincirini kır 4 | v1_legacy_inform_detail_box_button: Kovid kodu gir 5 | v1_legacy_inform_detail_box_text: Kovid kodunu girerek, uygulamaya Korona virüsüne ilişkin pozitif test edildiğinizi bildirirsiniz. 6 | v1_legacy_inform_detail_faq1_title: Bir Kovid kodu nedir? 7 | v1_legacy_inform_detail_faq1_text: Koronavirüs için testi pozitif çıkan kişiler (PCR testi veya hızlı antijen testi) bir Covid kodu alır.\n\nBöylece uygulama üzerinden yalnızca onaylı vakaların bildirilmesi sağlanır. 8 | v1_legacy_inform_detail_faq2_title: Neler iletilir? 9 | v1_legacy_inform_detail_faq2_text: Şahsi bilgileriniz değil, uygulamanızın yalnızca özel kodları iletilir. \nBu şekilde diğer SwissCovid uygulaması kullanıcıları yalnızca bir bulaşma riski olabileceğini öğenir. 10 | v1_legacy_inform_detail_faq3_text: Kişisel bilgileriniz iletilmese dahi, bazı kişiler tarihler temelinde sizinle olan teması hatırlayabilirler. 11 | v1_legacy_inform_detail_faq3_title: Bilgilerim anonim mi? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: İşitmiyor veya işitme özürlü müsünüz ve Koronavirüs bilgi hattını arayamıyor musunuz?\n\nO halde covid-support@medgate.ch adresine bir e-posta gönderiniz 14 | v1_legacy_inform_detail_infobox1_text: Testiniz pozitif çıktı (PCR testi veya hızlı antijen testi) ve 4 saat sonra halen bir Covid kodu almadınız mı?\nO halde koronavirüz danışma hattıyla iletişim kurun: 15 | v1_legacy_inform_detail_infobox1_title: Covid kodunu henüz almadınız mı? 16 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_de.properties: -------------------------------------------------------------------------------- 1 | de: 2 | v1_legacy_inform_detail_box_subtitle: Mit dem Covidcode... 3 | v1_legacy_inform_detail_box_title: Infektionsketten stoppen 4 | v1_legacy_inform_detail_box_button: Covidcode eingeben 5 | v1_legacy_inform_detail_box_text: Mit der Eingabe des Covidcodes teilen Sie der App mit, dass Sie positiv auf das Coronavirus getestet wurden. 6 | v1_legacy_inform_detail_faq1_title: Was ist ein Covidcode? 7 | v1_legacy_inform_detail_faq1_text: Positiv auf das Coronavirus getestete Personen (PCR-Test oder Antigen-Schnelltest) erhalten einen Covidcode.\n\nDamit wird sichergestellt, dass nur bestätigte Fälle über die App gemeldet werden. 8 | v1_legacy_inform_detail_faq2_title: Was wird gesendet? 9 | v1_legacy_inform_detail_faq2_text: Es werden nur die privaten Schlüssel Ihrer App gesendet, keine persönlichen Daten.\n\nDamit erfahren andere SwissCovid App Nutzer, dass die Möglichkeit einer Ansteckung besteht. 10 | v1_legacy_inform_detail_faq3_text: Obwohl keine Daten zu Ihrer Person gesendet werden, kann es sein, dass sich jemand anhand des Datums an die Begegnung erinnern kann. 11 | v1_legacy_inform_detail_faq3_title: Bleibe ich anonym? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: Sind Sie gehörlos oder hörbehindert und können die Infoline Coronavirus nicht anrufen?\n\nDann senden Sie eine E-Mail an covid-support@medgate.ch 14 | v1_legacy_inform_detail_infobox1_text: Sie wurden positiv getestet (PCR-Test oder Antigen-Schnelltest) und haben nach 4h noch keinen Covidcode erhalten?\nDann kontaktieren Sie die Infoline Coronavirus: 15 | v1_legacy_inform_detail_infobox1_title: Noch keinen Covidcode? 16 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_pt.properties: -------------------------------------------------------------------------------- 1 | pt: 2 | v1_legacy_inform_detail_box_subtitle: Com o código COVID... 3 | v1_legacy_inform_detail_box_title: Travar as cadeias de transmissão 4 | v1_legacy_inform_detail_box_button: Inserir código COVID 5 | v1_legacy_inform_detail_box_text: Ao introduzir o código COVID, notifica a app de que testou positivo para o novo coronavírus. 6 | v1_legacy_inform_detail_faq1_title: O que é um código COVID? 7 | v1_legacy_inform_detail_faq1_text: Pessoas com teste de coronavírus positivo (teste PCR ou teste rápido de antigénio) recebem um código Covid.\n\nIsso assegura que só casos confirmados sejam comunicados através do aplicativo. 8 | v1_legacy_inform_detail_faq2_title: Que dados são enviados? 9 | v1_legacy_inform_detail_faq2_text: Só são enviadas as chaves privadas do seu app e nunca dados pessoais.\n\nDesta forma, outros utilizadores do app SwissCovid ficam a saber que existe a possibilidade de um contágio. 10 | v1_legacy_inform_detail_faq3_text: Embora não sejam enviados dados pessoais seus, é possível que alguém se recorde de um contacto consigo pela data. 11 | v1_legacy_inform_detail_faq3_title: Permaneço anónimo? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: Sofre de surdez ou de insuficiência auditiva e não pode ligar para a linha informativa do coronavírus?\n\nNesse caso, envie um e-mail para covid-support@medgate.ch 14 | v1_legacy_inform_detail_infobox1_text: Foi testado positivo (teste PCR ou teste rápido de antigénio) e ainda não recebeu um código Covid depois de 4 hs?\nContacte a linha de informação coronavírus: 15 | v1_legacy_inform_detail_infobox1_title: Ainda não recebeu um código COVID? -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_it.properties: -------------------------------------------------------------------------------- 1 | it: 2 | v1_legacy_inform_detail_box_subtitle: Con il codice Covid... 3 | v1_legacy_inform_detail_box_title: Fermare le catene di infezione 4 | v1_legacy_inform_detail_box_button: Immetti il codice Covid 5 | v1_legacy_inform_detail_box_text: Immettendo il codice Covid, comunichi all'app che sei risultato positivo al test del nuovo coronavirus. 6 | v1_legacy_inform_detail_faq1_title: Cos'è un codice Covid? 7 | v1_legacy_inform_detail_faq1_text: Le persone che sono risultate positive al test del coronavirus (PCR o antigenico rapido) ricevono un codice Covid.\n\nIn questo modo si garantisce che l'app segnali soltanto i casi confermati. 8 | v1_legacy_inform_detail_faq2_title: Che cosa viene inviato? 9 | v1_legacy_inform_detail_faq2_text: Vengono inviate soltanto le chiavi private della tua app, non i tuoi dati personali.\n\nIn questo modo gli altri utenti dell'app SwissCovid vengono a sapere che vi è la possibilità di un contagio. 10 | v1_legacy_inform_detail_faq3_text: Anche se non vengono inviati dati personali, è possibile che qualcuno possa ricordarsi dell'incontro sulla base della data in cui è avvenuto. 11 | v1_legacy_inform_detail_faq3_title: Resto anonimo? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: Siete sordi o audiolesi e non potete chiamare l'Infoline Coronavirus?\n\nScrivete un'e-mail all'indirizzo: covid-support@medgate.ch 14 | v1_legacy_inform_detail_infobox1_text: Sei risultato positivo al test (PCR o antigenico rapido) e dopo quattro ore non hai ancora ricevuto un codice Covid?\nContatta la Infoline coronavirus: 15 | v1_legacy_inform_detail_infobox1_title: Non hai ancora un codice Covid? -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_sq.properties: -------------------------------------------------------------------------------- 1 | sq: 2 | v1_legacy_inform_detail_box_subtitle: Me kodin Covid... 3 | v1_legacy_inform_detail_box_title: Ndalo zinxhirin e infeksionit 4 | v1_legacy_inform_detail_box_button: Fut kodin e Covid 5 | v1_legacy_inform_detail_box_text: Me futjen e kodit të Covid ju njoftoni aplikacionin që jeni testuar pozitivë me koronavirusin e ri. 6 | v1_legacy_inform_detail_faq1_title: Çfarë është një kod Covid? 7 | v1_legacy_inform_detail_faq1_text: Njerëzit që kanë rezultuar pozitivë për koronavirusin (test PCR ose test i shpejtë i antigjenit) marrin një kod Covid.\n\nKjo siguron se vetëm rastet e konfirmuara raportohen përmes aplikacionit. 8 | v1_legacy_inform_detail_faq2_title: Çfarë dërgohet? 9 | v1_legacy_inform_detail_faq2_text: Dërgohen vetëm kodet e identifikimit të aplikacionit tuaj, asnjë e dhënë personale.\n\nNë këtë mënyrë mësoni përdoruesit e tjerë të alikacionit SwissCovid, të cilët mundësisht janë infektuar. 10 | v1_legacy_inform_detail_faq3_text: Edhe pse nuk dërgohet asnjë e dhënë personale, mund të ndodhë që dikush mund të kujtohet për kontaktin me anë të datës. 11 | v1_legacy_inform_detail_faq3_title: A mbetem unë anonim? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: A jeni i shurdhër ose keni probleme dëgjimi dhe nuk mund të telefononi linjën telefonike për koronavirusin Infoline Coronavirus?\n\nAtëherë na dërgoni një email te covid-support@medgate.ch 14 | v1_legacy_inform_detail_infobox1_text: A keni testuar pozitiv (nga test PCR ose test i shpejtë antigjeni) dhe nuk keni marrë një kod Covid pas 4 orësh? \nAtëherë kontaktoni qendrën telefonike të koronavirusit: 15 | v1_legacy_inform_detail_infobox1_title: Nuk keni marrë ende asnjë kod Covid? -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_rm.properties: -------------------------------------------------------------------------------- 1 | rm: 2 | v1_legacy_inform_detail_box_subtitle: Cun il code covid... 3 | v1_legacy_inform_detail_box_title: Franar chadainas d'infecziun 4 | v1_legacy_inform_detail_box_button: Endatar il code covid 5 | v1_legacy_inform_detail_box_text: Cun endatar il code covid communitgais Vus a l'app che Vus essas testada/testà en moda positiva sin il nov coronavirus 6 | v1_legacy_inform_detail_faq1_title: Tge è in code covid? 7 | v1_legacy_inform_detail_faq1_text: Persunas cun in test positiv sin il coronavirus (test da PCR u test svelt d'antigens) retschaivan in code covid.\n\nUschia vegn garantì ch'i vegnian annunziads sur l'app mo ils cas confermads. 8 | v1_legacy_inform_detail_faq2_title: Tge vegn tramess? 9 | v1_legacy_inform_detail_faq2_text: I vegnan tramessas mo las clavs privatas da Vossa app, naginas datas persunalas.\n\nUschia vegnan autras utilisadras ed auters utilisaders da l'app SwissCovid a savair ch'ina infecziun è pussaivla. 10 | v1_legacy_inform_detail_faq3_text: Cumbain ch'i na vegnan tramessas naginas datas davart Vossa persuna, èsi pussaivel ch'insatgi sa regorda da l'inscunter a maun da la data. 11 | v1_legacy_inform_detail_faq3_title: Rest jau anonima/anonim? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: Essas Vus senza udida u avais Vus in impediment d'udida e na pudais betg telefonar a la Infoline coronavirus?\n\nAlura tramettai in e-mail a covid-support@medgate.ch 14 | v1_legacy_inform_detail_infobox1_text: Vus avais in test positiv (test da PCR u test svelt d'antigens) e n'avais suenter 4 uras anc betg retschavì in code covid?\nAlura contactai la Infoline coronavirus: 15 | v1_legacy_inform_detail_infobox1_title: Anc nagin code covid? 16 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_fr.properties: -------------------------------------------------------------------------------- 1 | fr: 2 | v1_legacy_inform_detail_box_subtitle: Avec le code COVID… 3 | v1_legacy_inform_detail_box_title: Interrompre les chaînes d'infection 4 | v1_legacy_inform_detail_box_button: Entrer le code COVID 5 | v1_legacy_inform_detail_box_text: En saisissant le code COVID, vous indiquez à l'application que vous avez été testé positif au nouveau coronavirus. 6 | v1_legacy_inform_detail_faq1_title: Qu'est-ce qu'un code COVID ? 7 | v1_legacy_inform_detail_faq1_text: Les personnes testées positives au coronavirus (test PCR ou test rapide antigénique) reçoivent un code COVID.\n\nCeci permet de garantir que seuls les cas confirmés sont déclarés dans l'application. 8 | v1_legacy_inform_detail_faq2_title: Qu'est-ce qui est envoyé ? 9 | v1_legacy_inform_detail_faq2_text: Seules les clés privées de votre application sont envoyées, aucune donnée personnelle.\n\nAinsi, d'autres utilisateurs de l'application SwissCovid apprennent qu'ils ont peut-être été infectés. 10 | v1_legacy_inform_detail_faq3_text: Même si aucune donnée personnelle n'est envoyée sur vous, il est possible que quelqu'un se souvienne du contact sur la base de la date. 11 | v1_legacy_inform_detail_faq3_title: Est-ce que je reste anonyme ? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: Vous êtes sourd ou malentendant et vous ne pouvez pas appeler l'infoline dédiée au coronavirus ? \n\nAlors envoyez un message à covid-support@medgate.ch. 14 | v1_legacy_inform_detail_infobox1_text: Vous avez été testé positif (test PCR ou test rapide antigénique) mais vous n'avez pas encore reçu de code COVID après 4 heures ?\nContactez l'infoline coronavirus : 15 | v1_legacy_inform_detail_infobox1_title: Pas encore de code COVID ? 16 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/i18n/messages_v1_legacy_es.properties: -------------------------------------------------------------------------------- 1 | es: 2 | v1_legacy_inform_detail_box_subtitle: Con el código Covid… 3 | v1_legacy_inform_detail_box_title: Parar las cadenas de contagio 4 | v1_legacy_inform_detail_box_button: Introducir el código Covid 5 | v1_legacy_inform_detail_box_text: Al introducir el código Covid, está usted informando a la aplicación de que ha dado positivo en el test del coronavirus. 6 | v1_legacy_inform_detail_faq1_title: ¿Qué es el código Covid? 7 | v1_legacy_inform_detail_faq1_text: Las personas que han dado positivo al coronavirus (test PCR o test rápido de antígenos) reciben un código Covid.\n\nAsí queda garantizado que solo se notifiquen casos confirmados a través de la aplicación. 8 | v1_legacy_inform_detail_faq2_title: ¿Qué es lo que se envía? 9 | v1_legacy_inform_detail_faq2_text: Solamente se envían las claves privadas de la aplicación y no los datos personales. \n\nDe esta forma, se informa a otros usuarios de la aplicación SwissCovid de que existe la posibilidad de contagio. 10 | v1_legacy_inform_detail_faq3_text: A pesar de que no se envían datos sobre su persona, es posible que alguien recuerde por la fecha haber tenido contacto con usted. 11 | v1_legacy_inform_detail_faq3_title: ¿Permanezco en el anonimato? 12 | v1_legacy_infoline_coronavirus_number: +41 58 387 77 80 13 | v1_legacy_hearing_impaired_info: ¿Es usted sordo o tiene una discapacidad auditiva y no puede llamar a la Infoline del coronavirus?\n\nEnvíe un correo electrónico a covid-support@medgate.ch 14 | v1_legacy_inform_detail_infobox1_text: ¿Ha dado usted positivo (test PCR o test rápido de antígenos) y no ha recibido el código Covid después de 4 horas?\nContacte con la Infoline del coronavirus: 15 | v1_legacy_inform_detail_infobox1_title: ¿Aún no ha recibido el código Covid? 16 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/FaqEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | public class FaqEntry { 14 | 15 | private String title; 16 | private String text; 17 | private String iconAndroid; 18 | private String iconIos; 19 | 20 | /* optional */ 21 | private String linkTitle; 22 | private String linkUrl; 23 | 24 | public String getTitle() { 25 | return title; 26 | } 27 | 28 | public void setTitle(String title) { 29 | this.title = title; 30 | } 31 | 32 | public String getText() { 33 | return text; 34 | } 35 | 36 | public void setText(String text) { 37 | this.text = text; 38 | } 39 | 40 | public String getIconAndroid() { 41 | return iconAndroid; 42 | } 43 | 44 | public void setIconAndroid(String iconAndroid) { 45 | this.iconAndroid = iconAndroid; 46 | } 47 | 48 | public String getIconIos() { 49 | return iconIos; 50 | } 51 | 52 | public void setIconIos(String iconIos) { 53 | this.iconIos = iconIos; 54 | } 55 | 56 | public String getLinkTitle() { 57 | return linkTitle; 58 | } 59 | 60 | public void setLinkTitle(String linkTitle) { 61 | this.linkTitle = linkTitle; 62 | } 63 | 64 | public String getLinkUrl() { 65 | return linkUrl; 66 | } 67 | 68 | public void setLinkUrl(String linkUrl) { 69 | this.linkUrl = linkUrl; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/SDKConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | public class SDKConfig { 14 | private Integer numberOfWindowsForExposure = 3; 15 | 16 | @Deprecated private Float eventThreshold = 0.8f; 17 | 18 | @Deprecated private Float badAttenuationThreshold = 73.0f; 19 | 20 | private Float contactAttenuationThreshold = 73.0f; 21 | 22 | public Integer getNumberOfWindowsForExposure() { 23 | return numberOfWindowsForExposure; 24 | } 25 | 26 | public Float getContactAttenuationThreshold() { 27 | return contactAttenuationThreshold; 28 | } 29 | 30 | public void setContactAttenuationThreshold(Float contactAttenuationThreshold) { 31 | this.contactAttenuationThreshold = contactAttenuationThreshold; 32 | } 33 | 34 | public Float getBadAttenuationThreshold() { 35 | return badAttenuationThreshold; 36 | } 37 | 38 | public void setBadAttenuationThreshold(Float badAttenuationThreshold) { 39 | this.badAttenuationThreshold = badAttenuationThreshold; 40 | } 41 | 42 | public Float getEventThreshold() { 43 | return eventThreshold; 44 | } 45 | 46 | public void setEventThreshold(Float eventThreshold) { 47 | this.eventThreshold = eventThreshold; 48 | } 49 | 50 | public void setNumberOfWindowsForExposure(Integer numberOfWindowsForExposure) { 51 | this.numberOfWindowsForExposure = numberOfWindowsForExposure; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/VaccinationBookingInfo.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 2 | 3 | import javax.validation.constraints.NotNull; 4 | 5 | public class VaccinationBookingInfo { 6 | 7 | @NotNull 8 | private String title; 9 | @NotNull 10 | private String text; 11 | @NotNull 12 | private String info; 13 | @NotNull 14 | private String impfcheckTitle; 15 | @NotNull 16 | private String impfcheckText; 17 | @NotNull 18 | private String impfcheckButton; 19 | @NotNull 20 | private String impfcheckUrl; 21 | 22 | public String getTitle() { 23 | return title; 24 | } 25 | 26 | public void setTitle(String title) { 27 | this.title = title; 28 | } 29 | 30 | public String getText() { 31 | return text; 32 | } 33 | 34 | public void setText(String text) { 35 | this.text = text; 36 | } 37 | 38 | public String getInfo() { 39 | return info; 40 | } 41 | 42 | public void setInfo(String info) { 43 | this.info = info; 44 | } 45 | 46 | public String getImpfcheckTitle() { 47 | return impfcheckTitle; 48 | } 49 | 50 | public void setImpfcheckTitle(String impfcheckTitle) { 51 | this.impfcheckTitle = impfcheckTitle; 52 | } 53 | 54 | public String getImpfcheckText() { 55 | return impfcheckText; 56 | } 57 | 58 | public void setImpfcheckText(String impfcheckText) { 59 | this.impfcheckText = impfcheckText; 60 | } 61 | 62 | public String getImpfcheckButton() { 63 | return impfcheckButton; 64 | } 65 | 66 | public void setImpfcheckButton(String impfcheckButton) { 67 | this.impfcheckButton = impfcheckButton; 68 | } 69 | 70 | public String getImpfcheckUrl() { 71 | return impfcheckUrl; 72 | } 73 | 74 | public void setImpfcheckUrl(String impfcheckUrl) { 75 | this.impfcheckUrl = impfcheckUrl; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/Canton.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | public enum Canton { 8 | AG("ag", "canton_aargau"), 9 | AR("ar", "canton_appenzell_ausserrhoden"), 10 | AI("ai", "canton_appenzell_innerrhoden"), 11 | BL("bl", "canton_basel_country"), 12 | BS("bs", "canton_basel_city"), 13 | BE("be", "canton_berne"), 14 | FR("fr", "canton_fribourg"), 15 | GE("ge", "canton_geneva"), 16 | GL("gl", "canton_glarus"), 17 | GR("gr", "canton_graubuenden"), 18 | JU("ju", "canton_jura"), 19 | LU("lu", "canton_lucerne"), 20 | NE("ne", "canton_neuchatel"), 21 | NW("nw", "canton_nidwalden"), 22 | OW("ow", "canton_obwalden"), 23 | SG("sg", "canton_st_gallen"), 24 | SH("sh", "canton_schaffhausen"), 25 | SZ("sz", "canton_schwyz"), 26 | SO("so", "canton_solothurn"), 27 | TG("tg", "canton_thurgovia"), 28 | TI("ti", "canton_ticino"), 29 | UR("ur", "canton_uri"), 30 | VS("vs", "canton_valais"), 31 | VD("vd", "canton_vaud"), 32 | ZG("zg", "canton_zug"), 33 | ZH("zh", "canton_zurich"); 34 | 35 | private String id; 36 | private String namePoeditorKey; 37 | 38 | Canton(String id, String namePoeditorKey) { 39 | this.id = id; 40 | this.namePoeditorKey = namePoeditorKey; 41 | } 42 | 43 | public String getId() { 44 | return id; 45 | } 46 | 47 | public String getNamePoeditorKey() { 48 | return namePoeditorKey; 49 | } 50 | 51 | public String getAndroidIconAssetName() { 52 | return "icon_" + id; 53 | } 54 | 55 | public String getIosIconAssetName() { 56 | return "icon-" + id; 57 | } 58 | 59 | public static List namePoeditorKeys() { 60 | return Arrays.stream(Canton.values()) 61 | .map(Canton::getNamePoeditorKey) 62 | .collect(Collectors.toList()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020 Ubique Innovation AG 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | # 8 | # SPDX-License-Identifier: MPL-2.0 9 | # 10 | 11 | spring.profiles.active=cloud-dev,local 12 | spring.application.name=dp3t-config-service 13 | server.port=8080 14 | management.endpoints.enabled-by-default=false 15 | vcap.services.ecdsa_cs_dev.credentials.privateKey=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0VBZ0VBTUJBR0J5cUdTTTQ5QWdFR0JTdUJCQUFLQkcwd2F3SUJBUVFnMkRsai9lNW5rRlBtTk1MVjd1NjQKenFuOHdSeVgrUTgyc045RDRSWXlvNjJoUkFOQ0FBVGFzRmgwR0tlazVZNEp1d2dpNUg4QWsvcWZqa0pDd3o3UApYb0lVZWJnaXN5N1RWUUxySW1CUFU3aWcwMzdrMGRvVXhrK1hkQktYNDN2L3JkR1lVS2YxCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K 16 | vcap.services.ecdsa_cs_dev.credentials.publicKey=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNTRENDQWU2Z0F3SUJBZ0lVS3pEQlJIZlZVN2djRGZxUTBGUDFLTkJULzVJd0NnWUlLb1pJemowRUF3SXcKZXpFTE1Ba0dBMVVFQmhNQ1EwZ3hEVEFMQmdOVkJBZ01CRUpsY200eERUQUxCZ05WQkFjTUJFSmxjbTR4RERBSwpCZ05WQkFvTUEwSkpWREVNTUFvR0ExVUVDd3dEUlZkS01RMHdDd1lEVlFRRERBUlVaWE4wTVNNd0lRWUpLb1pJCmh2Y05BUWtCRmhSemRYQndiM0owUUdKcGRDNWhaRzFwYmk1amFEQWVGdzB5TURBME1qZ3hNakUwTlRGYUZ3MHkKTVRBME1qZ3hNakUwTlRGYU1Ic3hDekFKQmdOVkJBWVRBa05JTVEwd0N3WURWUVFJREFSQ1pYSnVNUTB3Q3dZRApWUVFIREFSQ1pYSnVNUXd3Q2dZRFZRUUtEQU5DU1ZReEREQUtCZ05WQkFzTUEwVlhTakVOTUFzR0ExVUVBd3dFClZHVnpkREVqTUNFR0NTcUdTSWIzRFFFSkFSWVVjM1Z3Y0c5eWRFQmlhWFF1WVdSdGFXNHVZMmd3VmpBUUJnY3EKaGtqT1BRSUJCZ1VyZ1FRQUNnTkNBQVRhc0ZoMEdLZWs1WTRKdXdnaTVIOEFrL3FmamtKQ3d6N1BYb0lVZWJnaQpzeTdUVlFMckltQlBVN2lnMDM3azBkb1V4aytYZEJLWDQzdi9yZEdZVUtmMW8xTXdVVEFkQmdOVkhRNEVGZ1FVCnVTS2lWSUdsRnpQdDdXd3Z1VGNicDNrckQ0UXdId1lEVlIwakJCZ3dGb0FVdVNLaVZJR2xGelB0N1d3dnVUY2IKcDNrckQ0UXdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QUtCZ2dxaGtqT1BRUURBZ05JQURCRkFpQkRteEJUQ3BZawphN0hFeUFEWnN4d3p3b2h0TjBwNTd5QllMYjZzQ3B3ODhBSWhBSXpTUDdCV0tGWmNDSmI5ZmhwcjZaTXpJd0tlCkhhSWpIK2E4elV2Nk1PaW8KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= 17 | ws.deactivate-app=true -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/poeditor/Messages.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.poeditor; 2 | 3 | import java.util.Locale; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.context.MessageSource; 7 | import org.springframework.context.NoSuchMessageException; 8 | import org.springframework.context.i18n.LocaleContextHolder; 9 | 10 | public class Messages { 11 | 12 | private MessageSource messageSource; 13 | private static final Logger logger = 14 | LoggerFactory.getLogger( 15 | org.dpppt.switzerland.backend.sdk.config.ws.poeditor.Messages.class); 16 | 17 | public Messages(MessageSource messageSource) { 18 | this.messageSource = messageSource; 19 | } 20 | 21 | public String getMessage(String key) { 22 | return getMessage(key, LocaleContextHolder.getLocale()); 23 | } 24 | 25 | /** 26 | * returns message for the given message key and locale. throws a {@link NoSuchMessageException} 27 | * if no such message exists 28 | * 29 | * @param messageKey 30 | * @param locale 31 | * @return 32 | */ 33 | public String getMessage(String messageKey, Locale locale) throws NoSuchMessageException { 34 | return messageSource.getMessage(messageKey, null, locale); 35 | } 36 | 37 | /** 38 | * FOR USE DURING DEVELOPMENT WHEN NOT ALL TRANSLATIONS ARE PRESENT returns message for the 39 | * given messagekey and locale. fallback language: EN returns null if no such message exists. 40 | * 41 | * @param messageKey 42 | * @param locale 43 | * @return 44 | */ 45 | public String getNullableMessage(String messageKey, Locale locale) { 46 | try { 47 | return messageSource.getMessage(messageKey, null, locale); 48 | } catch (NoSuchMessageException e) { 49 | try { 50 | return messageSource.getMessage(messageKey, null, Locale.ENGLISH); 51 | } catch (NoSuchMessageException e2) { 52 | return null; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.github/workflows/tagged_release.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven when a tag is pushed 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | --- 4 | name: Java CI with Maven 5 | on: 6 | push: 7 | tags: 8 | - "v*" 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - run: | 15 | git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* 16 | - name: Set up JDK 11 17 | uses: actions/setup-java@v3 18 | with: 19 | java-version: 11.0.7 20 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 21 | settings-path: ${{ github.workspace }} # location for the settings.xml file: : 22 | distribution: 'zulu' 23 | - name: 'Create settings.xml' 24 | uses: whelk-io/maven-settings-xml-action@v20 25 | with: 26 | servers: '[{"id": "github", "username": "${{ github.actor }}", "password": "${{ github.token}}"}, {"id": "github-annotation", "username": "${{ github.actor }}", "password": "${{ github.token }}"}]' 27 | repositories: '[{"id" : "github", "url" : "https://maven.pkg.github.com/Ubique-OSS/springboot-swagger3"}, {"id" : "github-annotation", "url" : "https://maven.pkg.github.com/Ubique-OSS/springboot-swagger3-annotations"}]' 28 | - name: Build with Maven 29 | run: | 30 | mvn install --file dpppt-config-backend/pom.xml; 31 | env: 32 | GITHUB_TOKEN: ${{ github.token }} 33 | TESTCONTAINERS_RYUK_DISABLED: true 34 | - name: shasum 35 | run: shasum -a 256 dpppt-config-backend/target/dpppt-config-backend.jar > dpppt-config-backend/target/dpppt-config-backend.jar.sha256 36 | shell: bash 37 | - name: "Create new release" 38 | uses: "marvinpinto/action-automatic-releases@latest" 39 | with: 40 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 41 | prerelease: false 42 | files: | 43 | dpppt-config-backend/target/dpppt-config-backend.jar 44 | dpppt-config-backend/generated/swagger/swagger.yaml 45 | dpppt-config-backend/target/dpppt-config-backend.jar.sha256 46 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/filter/ResponseWrapperFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.filter; 12 | 13 | import java.io.IOException; 14 | import java.security.KeyPair; 15 | import java.security.PublicKey; 16 | import java.security.Security; 17 | import java.util.List; 18 | import javax.servlet.Filter; 19 | import javax.servlet.FilterChain; 20 | import javax.servlet.ServletException; 21 | import javax.servlet.ServletRequest; 22 | import javax.servlet.ServletResponse; 23 | import javax.servlet.http.HttpServletResponse; 24 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 25 | import org.dpppt.switzerland.backend.sdk.config.ws.security.signature.SignatureResponseWrapper; 26 | 27 | public class ResponseWrapperFilter implements Filter { 28 | 29 | private final KeyPair pair; 30 | private final int retentionDays; 31 | private final List protectedHeaders; 32 | 33 | public PublicKey getPublicKey() { 34 | return pair.getPublic(); 35 | } 36 | 37 | public ResponseWrapperFilter(KeyPair pair, int retentionDays, List protectedHeaders) { 38 | Security.addProvider(new BouncyCastleProvider()); 39 | Security.setProperty("crypto.policy", "unlimited"); 40 | this.pair = pair; 41 | this.retentionDays = retentionDays; 42 | this.protectedHeaders = protectedHeaders; 43 | } 44 | 45 | @Override 46 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 47 | throws IOException, ServletException { 48 | HttpServletResponse httpResponse = (HttpServletResponse) response; 49 | SignatureResponseWrapper wrapper = 50 | new SignatureResponseWrapper(httpResponse, pair, retentionDays, protectedHeaders); 51 | chain.doFilter(request, wrapper); 52 | wrapper.outputData(httpResponse.getOutputStream()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | name: Java CI with Maven 4 | on: 5 | push: 6 | branches: [ develop ] 7 | pull_request: 8 | branches: [ develop ] 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up JDK 11 15 | uses: actions/setup-java@v3 16 | with: 17 | java-version: 11.0.7 18 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 19 | settings-path: ${{ github.workspace }} # location for the settings.xml file: : 20 | distribution: 'zulu' 21 | - name: 'Create settings.xml' 22 | uses: whelk-io/maven-settings-xml-action@v20 23 | with: 24 | servers: '[{"id": "github", "username": "${{ github.actor }}", "password": "${{ github.token}}"}, {"id": "github-annotation", "username": "${{ github.actor }}", "password": "${{ github.token }}"}]' 25 | repositories: '[{"id" : "github", "url" : "https://maven.pkg.github.com/Ubique-OSS/springboot-swagger3"}, {"id" : "github-annotation", "url" : "https://maven.pkg.github.com/Ubique-OSS/springboot-swagger3-annotations"}]' 26 | - name: Build with Maven 27 | run: mvn clean install --file dpppt-config-backend/pom.xml 28 | env: 29 | GITHUB_TOKEN: ${{ github.token }} 30 | - name: Echo current ref 31 | run: echo "${{ github.ref }}" 32 | - name: shasum 33 | run: shasum -a 256 dpppt-config-backend/target/dpppt-config-backend.jar > dpppt-config-backend/target/dpppt-config-backend.jar.sha256 34 | shell: bash 35 | - name: Create Snapshot Release 36 | uses: ncipollo/release-action@v1 37 | if: github.ref == 'refs/heads/develop' 38 | id: create_release 39 | with: 40 | name: Snapshot Release ${{ github.ref }} 41 | tag: SNAPSHOT 42 | artifacts: "dpppt-config-backend/target/dpppt-config-backend.jar,dpppt-config-backend/target/dpppt-config-backend.jar.sha256" 43 | body: | 44 | Changes in this Release 45 | - Snapshot release 46 | token: ${{ secrets.GITHUB_TOKEN }} 47 | draft: false 48 | prerelease: false 49 | allowUpdates: true 50 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/WhatToDoPositiveTestTexts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 14 | import java.util.List; 15 | 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class WhatToDoPositiveTestTexts { 18 | 19 | private String enterCovidcodeBoxSupertitle; 20 | private String enterCovidcodeBoxTitle; 21 | private String enterCovidcodeBoxText; 22 | private String enterCovidcodeBoxButtonTitle; 23 | 24 | // dismissible will be ignored by clients 25 | private InfoBox infoBox; 26 | 27 | private List faqEntries; 28 | 29 | public String getEnterCovidcodeBoxSupertitle() { 30 | return enterCovidcodeBoxSupertitle; 31 | } 32 | 33 | public void setEnterCovidcodeBoxSupertitle(String enterCovidcodeBoxSupertitle) { 34 | this.enterCovidcodeBoxSupertitle = enterCovidcodeBoxSupertitle; 35 | } 36 | 37 | public String getEnterCovidcodeBoxTitle() { 38 | return enterCovidcodeBoxTitle; 39 | } 40 | 41 | public void setEnterCovidcodeBoxTitle(String enterCovidcodeBoxTitle) { 42 | this.enterCovidcodeBoxTitle = enterCovidcodeBoxTitle; 43 | } 44 | 45 | public String getEnterCovidcodeBoxText() { 46 | return enterCovidcodeBoxText; 47 | } 48 | 49 | public void setEnterCovidcodeBoxText(String enterCovidcodeBoxText) { 50 | this.enterCovidcodeBoxText = enterCovidcodeBoxText; 51 | } 52 | 53 | public String getEnterCovidcodeBoxButtonTitle() { 54 | return enterCovidcodeBoxButtonTitle; 55 | } 56 | 57 | public void setEnterCovidcodeBoxButtonTitle(String enterCovidcodeBoxButtonTitle) { 58 | this.enterCovidcodeBoxButtonTitle = enterCovidcodeBoxButtonTitle; 59 | } 60 | 61 | public InfoBox getInfoBox() { 62 | return infoBox; 63 | } 64 | 65 | public void setInfoBox(InfoBox infoBox) { 66 | this.infoBox = infoBox; 67 | } 68 | 69 | public List getFaqEntries() { 70 | return faqEntries; 71 | } 72 | 73 | public void setFaqEntries(List faqEntries) { 74 | this.faqEntries = faqEntries; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /dpppt-config-backend/getTranslations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import http.client, urllib.request, urllib.parse, urllib.error 4 | import subprocess 5 | import json 6 | import sys 7 | 8 | def loadLanguage( language, apiToken ): 9 | params = urllib.parse.urlencode({'api_token': apiToken, 'action': 'view_terms', 'id': '334421', 'type': 'json', 'language': language}) 10 | headers = {"Content-type": "application/x-www-form-urlencoded", 11 | "Accept": "text/plain"} 12 | conn = http.client.HTTPSConnection("poeditor.com") 13 | conn.request("POST", "/api/", params, headers) 14 | response = conn.getresponse() 15 | data = response.read() 16 | conn.close() 17 | 18 | results = json.loads(data.decode('utf-8')) # obj now contains a dict of the data 19 | 20 | if results['response']['code'] == '200': 21 | # Open file 22 | fo = open("src/main/resources/i18n/messages_"+language+".properties", "wb") 23 | 24 | lineUtf8 = (language + ": \n").encode('UTF-8') 25 | fo.write(lineUtf8) 26 | 27 | for translation in results['list']: 28 | if translation['definition']['form'] != "": 29 | val = translation['definition']['form'] 30 | val = val.replace("\n", "\\n") 31 | lineUtf8 = (" " + translation['term'] + ": " + val + "\n").encode('UTF-8') 32 | fo.write(lineUtf8) 33 | 34 | # Close opend file 35 | fo.close() 36 | else: 37 | print("poeditor API responded with error code: " + results['response']['code']) 38 | sys.exit(1) 39 | 40 | 41 | def getApiToken(): 42 | p1 = subprocess.Popen(["source ~/.zshrc; getPassword 'POEditor API Token'"], stdout=subprocess.PIPE, shell=True, executable='/bin/zsh') 43 | output, err = p1.communicate() 44 | if err: 45 | sys.stderr.write(err.decode("utf-8")) 46 | return output.decode("utf-8").splitlines()[-1].split()[0] 47 | 48 | apiToken = getApiToken() 49 | print('Using API token: ' + apiToken) 50 | 51 | loadLanguage("sq", apiToken) # albanian 52 | loadLanguage("bs", apiToken) # bosnian 53 | loadLanguage("hr", apiToken) # croatian 54 | loadLanguage("en", apiToken) # english 55 | loadLanguage("fr", apiToken) # french 56 | loadLanguage("de", apiToken) # german 57 | loadLanguage("it", apiToken) # italian 58 | loadLanguage("pt", apiToken) # portuguese 59 | loadLanguage("rm", apiToken) # romansh 60 | loadLanguage("sr", apiToken) # serbian 61 | loadLanguage("es", apiToken) # spanish 62 | loadLanguage("ti", apiToken) # tigrinya 63 | loadLanguage("tr", apiToken) # turkish 64 | 65 | print('Successfully loaded terms') 66 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/local-logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | DEBUG 29 | 30 | 31 | ${CONSOLE_LOG_PATTERN} 32 | utf8 33 | 34 | 35 | 36 | 37 | 38 | ${LOG_FILE}.log 39 | 40 | ${LOG_FILE}.%d{yyyy-MM-dd}.gz 41 | 7 42 | 43 | 44 | ${CONSOLE_LOG_PATTERN} 45 | utf8 46 | 47 | 48 | 512KB 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/InfoBox.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | import ch.ubique.openapi.docannotations.Documentation; 14 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 15 | 16 | /** @author bachmann created on 28.04.20 */ 17 | @JsonIgnoreProperties(ignoreUnknown = true) 18 | public class InfoBox { 19 | 20 | @Documentation(description = "Title of the infobox", example = "Alert") 21 | private String title; 22 | 23 | @Documentation(description = "Message shown to the user", example = "Please restart your phone") 24 | private String msg; 25 | 26 | @Documentation( 27 | description = "If given, adds a url to the message", 28 | example = "https://dp-3t.github.io/") 29 | private String url; 30 | 31 | @Documentation(description = "Title to be shown for the URL", example = "DP3T page") 32 | private String urlTitle; 33 | 34 | @Documentation(description = "If true, the user can dismiss the message", example = "true") 35 | private boolean isDismissible = false; 36 | 37 | @Documentation(description = "Text to display in hearing impaired pop up") 38 | private String hearingImpairedInfo; 39 | 40 | public String getInfoId() { 41 | return Integer.toString( 42 | ((getTitle() != null) ? getTitle().hashCode() : 0) 43 | + ((getMsg() != null) ? getMsg().hashCode() : 0) 44 | + ((getUrl() != null) ? getUrl().hashCode() : 0) 45 | + ((getUrlTitle() != null) ? getUrlTitle().hashCode() : 0) 46 | + (Boolean.hashCode(getIsDismissible()))); 47 | } 48 | 49 | public String getTitle() { 50 | return title; 51 | } 52 | 53 | public void setTitle(String title) { 54 | this.title = title; 55 | } 56 | 57 | public String getMsg() { 58 | return msg; 59 | } 60 | 61 | public void setMsg(String msg) { 62 | this.msg = msg; 63 | } 64 | 65 | public String getUrl() { 66 | return url; 67 | } 68 | 69 | public void setUrl(String url) { 70 | this.url = url; 71 | } 72 | 73 | public String getUrlTitle() { 74 | return urlTitle; 75 | } 76 | 77 | public void setUrlTitle(String urlTitle) { 78 | this.urlTitle = urlTitle; 79 | } 80 | 81 | public boolean getIsDismissible() { 82 | return isDismissible; 83 | } 84 | 85 | public void setIsDismissible(boolean isDismissible) { 86 | this.isDismissible = isDismissible; 87 | } 88 | 89 | public String getHearingImpairedInfo() { 90 | return hearingImpairedInfo; 91 | } 92 | 93 | public void setHearingImpairedInfo(String hearingImpairedInfo) { 94 | this.hearingImpairedInfo = hearingImpairedInfo; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/GAENSDKConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | import ch.ubique.openapi.docannotations.Documentation; 14 | 15 | public class GAENSDKConfig { 16 | 17 | @Documentation( 18 | description = 19 | "Lower threshold that is sent to the GAEN to count encounters with other devices. It" 20 | + " is expressed in dB attenuation from the Bluetooth chip.", 21 | example = "53") 22 | private Integer lowerThreshold = 55; 23 | 24 | @Documentation( 25 | description = 26 | "Higher threshold that is sent to the GAEN to count encounters with other devices " 27 | + ".It is expressed in dB attenuation from the Bluetooth chip.", 28 | example = "60") 29 | private Integer higherThreshold = 63; 30 | 31 | @Documentation( 32 | description = 33 | "Multiplication factor used to weigh the return value of the GAEN in " 34 | + "attenuationDuration[0]", 35 | example = "1.0d") 36 | private Double factorLow = 1.0d; 37 | 38 | @Documentation( 39 | description = 40 | "Multiplication factor used to weigh the return value of the GAEN in " 41 | + "attenuationDuration[1]", 42 | example = "0.5d") 43 | private Double factorHigh = 0.5d; 44 | 45 | @Documentation( 46 | description = 47 | "Minimum duration of exposure during one day reported by the GAEN before the user " 48 | + "is alerted of an exposure risk", 49 | example = "15") 50 | private Integer triggerThreshold = 15; 51 | 52 | public Integer getLowerThreshold() { 53 | return lowerThreshold; 54 | } 55 | 56 | public void setLowerThreshold(Integer lowerThreshold) { 57 | this.lowerThreshold = lowerThreshold; 58 | } 59 | 60 | public Integer getHigherThreshold() { 61 | return higherThreshold; 62 | } 63 | 64 | public void setHigherThreshold(Integer higherThreshold) { 65 | this.higherThreshold = higherThreshold; 66 | } 67 | 68 | public Double getFactorLow() { 69 | return factorLow; 70 | } 71 | 72 | public void setFactorLow(Double factorLow) { 73 | this.factorLow = factorLow; 74 | } 75 | 76 | public Double getFactorHigh() { 77 | return factorHigh; 78 | } 79 | 80 | public void setFactorHigh(Double factorHigh) { 81 | this.factorHigh = factorHigh; 82 | } 83 | 84 | public Integer getTriggerThreshold() { 85 | return triggerThreshold; 86 | } 87 | 88 | public void setTriggerThreshold(Integer triggerThreshold) { 89 | this.triggerThreshold = triggerThreshold; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ,itffti, 2 | ,C@@@@@@@@C, 3 | .8@@@@@@@@@@8. 4 | 1@@@@@@@@@@@@1 5 | ,8@@@@@@@@@@8, 6 | :tCG0GL1. :C@@@@@@@@C: ,tCG0GCt, 7 | .C@@@@@@@@8t .t@@@@@@t. .L@@@@@@@@@L. 8 | G@@@@@@@@@@@1 ,itC08@@@@@@@@@@8GCti, L@@@@@@@@@@@L 9 | .@@@@@@@@@@@@G .;L0@@@@@@@@@@@@@@@@@@@@@@0Li. 0@@@@@@@@@@@8 10 | f@@@@@@@@@@@@0t, :L8@@@@@@@@@@@888888@@@@@@@@@@@8L:.t8@@@@@@@@@@@1 11 | 10@@@@@@@@@@@@@CC@@@@@@@@0Cti:,......,:itC0@@@@@@@@8@@@@@@@@@@@Gi 12 | :1tf1if8@@@@@@@@@@@@0t: :t0@@@@@@@@@@@Ltfti, 13 | .iC@@@@@@@@C; ;C@@@@@@@0, 14 | t@@@@@@0; ;0@@@@@8i 15 | t@@@@@@L. :;:. .L@@@@@@1, 16 | :@@@@@@L :0@@@0i L@@@@@@@0t. 17 | G@@@@@G t@@@@@G 0@@@@@@@@0, 18 | :@@@@@@; tG0Gf, .,, ;@@@@@@@@@L 19 | 1@@@@@8. iC0@@@8C1. .8@@@@@@@@f 20 | :itt1;f@@@@@0 ,G@@@@@@@@@0: 0@@@@@@@C. 21 | i0@@@@@@@@@@@@8. C@@@@@@@@@@@G .8@@@@@G; 22 | f@@@@@@@@@@@@@@@; G@@@@@@@@@@@0 ;@@@@@@, 23 | .@@@@@@@@@@@@@@@@G :;;, :8@@@@@@@@@@; 0@@@@@0,. ,:;;:. 24 | G@@@@@@@@@@@@@@@@L ,G@@@8t .t0@@@@@0f: L@@@@@@@@0CG@@@@@8C; 25 | ,G@@@@@@@@@C@@@@@@L. i@@@@@8. ,:;:,. .L@@@@@@@@@@@@@@@@@@@@f 26 | ;fCGGGC1, 1@@@@@@0; iCGGL, ;0@@@@@@G0@@@@@@@@@@@@@@, 27 | i0@@@@@@C; ;C@@@@@@8i .,1@@@@@@@@@@@@, 28 | L@@@@@@@@0t:. .:t0@@@@@@@L, f@@@@@@@@@8i 29 | . ,f8@@@@@@@@@@@@8Cti:,......,:itC8@@@@@@@@C; :fG8@@8Gt. 30 | ;fG080@@@@@@@8L8@@@@@@@@@@@888888@@@@@@@@@@@@C, .... 31 | .C@@@@@@@@@@@8t. .;f0@@@@@@@@@@@@@@@@@@@@@@@@@@8i 32 | L@@@@@@@@@@@0, ,;t@@@@@@@@@@@@@8GCtiG@@@@@@L. 33 | 0@@@@@@@@@@@0 .G@@@@@@@@@@C,, f@@@@@@0GLt; 34 | i@@@@@@@@@@@i f@@@@@@@@@@@@, i8@@@@@@@@@0; 35 | :C8@@@@@8C: 1@@@@@@@@@@@@, 0@@@@@@@@@@@: 36 | .;1t1;. f@@@@@@@@@8; :@@@@@@@@@@@@t 37 | :fG8@80C1. .G@@@@@@@@@@@, 38 | ... .L8@@@@@@@C: 39 | ,itfff1, 40 | ___ ___ _________ ___ __ _ __ __ _ _ 41 | | \| _ \__ /_ _| / __|___ _ _ / _(_)__ _ \ \ / /__| |__ ___ ___ _ ___ _(_)__ ___ 42 | | |) | _/|_ \ | | | (__/ _ \ ' \| _| / _` | \ \/\/ / -_) '_ (_-> getVaccinationBookingCantons() { 29 | Map> result = new EnumMap<>(Language.class); 30 | for (Language language : Language.values()) { 31 | Locale l = language.toLocale(); 32 | List cantonInfos = new ArrayList<>(); 33 | for (Canton canton : Canton.values()) { 34 | VaccinationBookingCanton cantonInfo = new VaccinationBookingCanton(); 35 | cantonInfo.setName(msg.getMessage(canton.getNamePoeditorKey(), l)); 36 | cantonInfo.setIconAndroid(canton.getAndroidIconAssetName()); 37 | cantonInfo.setIconIos(canton.getIosIconAssetName()); 38 | cantonInfo.setLinkUrl( 39 | msg.getMessage(VACCINATION_BOOKING_PREFIX + canton.getId() + "_url", l)); 40 | cantonInfos.add(cantonInfo); 41 | } 42 | result.put(language, cantonInfos); 43 | } 44 | return result; 45 | } 46 | 47 | public Map getVaccinationBookingInfo() { 48 | Map result = new EnumMap<>(Language.class); 49 | for (Language language : Language.values()) { 50 | Locale l = language.toLocale(); 51 | VaccinationBookingInfo info = new VaccinationBookingInfo(); 52 | info.setTitle(msg.getMessage(VACCINATION_BOOKING_INFO_PREFIX + "title", l)); 53 | info.setText(msg.getMessage(VACCINATION_BOOKING_INFO_PREFIX + "text", l)); 54 | info.setInfo(msg.getMessage(VACCINATION_BOOKING_INFO_PREFIX + "info", l)); 55 | 56 | info.setImpfcheckTitle(msg.getMessage(VACCINATION_IMPF_CHECK_PREFIX + "title", l)); 57 | info.setImpfcheckText(msg.getMessage(VACCINATION_IMPF_CHECK_PREFIX + "info_text", l)); 58 | info.setImpfcheckButton(msg.getMessage(VACCINATION_IMPF_CHECK_PREFIX + "action", l)); 59 | info.setImpfcheckUrl(msg.getMessage(VACCINATION_IMPF_CHECK_PREFIX + "url", l)); 60 | 61 | result.put(language, info); 62 | } 63 | return result; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/ActuatorSecurity.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.config; 2 | 3 | import org.dpppt.switzerland.backend.sdk.config.ws.config.configbeans.ActuatorSecurityConfig; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.boot.actuate.health.HealthEndpoint; 6 | import org.springframework.boot.actuate.info.InfoEndpoint; 7 | import org.springframework.boot.actuate.logging.LoggersEndpoint; 8 | import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Profile; 11 | import org.springframework.core.Ordered; 12 | import org.springframework.core.annotation.Order; 13 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 14 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 15 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 16 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 17 | 18 | @Configuration 19 | @Order(Ordered.HIGHEST_PRECEDENCE + 9) 20 | @Profile(value = "actuator-security") 21 | @EnableWebSecurity 22 | public class ActuatorSecurity extends WebSecurityConfigurerAdapter { 23 | 24 | private static final String PROMETHEUS_ROLE = "PROMETHEUS"; 25 | 26 | @Value("${ws.monitor.prometheus.user}") 27 | private String user; 28 | @Value("${ws.monitor.prometheus.password}") 29 | private String password; 30 | 31 | @Override 32 | protected void configure(HttpSecurity http) throws Exception { 33 | http.requestMatcher( 34 | org.springframework.boot.actuate.autoconfigure.security.servlet 35 | .EndpointRequest.toAnyEndpoint()) 36 | .authorizeRequests() 37 | .requestMatchers( 38 | org.springframework.boot.actuate.autoconfigure.security.servlet 39 | .EndpointRequest.to(HealthEndpoint.class)) 40 | .permitAll() 41 | .requestMatchers( 42 | org.springframework.boot.actuate.autoconfigure.security.servlet 43 | .EndpointRequest.to(InfoEndpoint.class)) 44 | .permitAll() 45 | .requestMatchers( 46 | org.springframework.boot.actuate.autoconfigure.security.servlet 47 | .EndpointRequest.to(LoggersEndpoint.class)) 48 | .hasRole(PROMETHEUS_ROLE) 49 | .requestMatchers( 50 | org.springframework.boot.actuate.autoconfigure.security.servlet 51 | .EndpointRequest.to(PrometheusScrapeEndpoint.class)) 52 | .hasRole(PROMETHEUS_ROLE) 53 | .anyRequest() 54 | .denyAll() 55 | .and() 56 | .httpBasic(); 57 | 58 | http.csrf().ignoringAntMatchers("/actuator/loggers/**"); 59 | } 60 | 61 | @Override 62 | protected void configure( 63 | AuthenticationManagerBuilder auth) 64 | throws Exception { 65 | auth.inMemoryAuthentication() 66 | .withUser(user) 67 | .password(password) 68 | .roles(PROMETHEUS_ROLE); 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/test/java/org/dpppt/switzerland/backend/sdk/config/ws/GaenConfigControllerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws; 12 | 13 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 14 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 15 | 16 | import javax.servlet.Filter; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.boot.test.context.SpringBootTest; 21 | import org.springframework.test.context.ActiveProfiles; 22 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 23 | 24 | @ActiveProfiles({"actuator-security"}) 25 | @SpringBootTest( 26 | properties = { 27 | "ws.monitor.prometheus.user=prometheus", 28 | "ws.monitor.prometheus.password={noop}prometheus", 29 | "management.endpoints.enabled-by-default=true", 30 | "management.endpoints.web.exposure.include=*" 31 | }) 32 | public class GaenConfigControllerTest extends BaseControllerTest { 33 | @Autowired private Filter springSecurityFilterChain; 34 | 35 | @Before 36 | public void setup() throws Exception { 37 | super.setup(); 38 | this.mockMvc = 39 | MockMvcBuilders.webAppContextSetup(webApplicationContext) 40 | .addFilter(springSecurityFilterChain) 41 | .addFilter(filter, "/*") 42 | .build(); 43 | } 44 | 45 | @Test 46 | public void testActuatorSecurity() throws Exception { 47 | var response = 48 | mockMvc.perform(get("/actuator/health")) 49 | .andExpect(status().is2xxSuccessful()) 50 | .andReturn() 51 | .getResponse(); 52 | response = 53 | mockMvc.perform(get("/actuator/loggers")) 54 | .andExpect(status().is(401)) 55 | .andReturn() 56 | .getResponse(); 57 | response = 58 | mockMvc.perform( 59 | get("/actuator/loggers") 60 | .header( 61 | "Authorization", 62 | "Basic cHJvbWV0aGV1czpwcm9tZXRoZXVz")) 63 | .andExpect(status().isOk()) 64 | .andReturn() 65 | .getResponse(); 66 | response = 67 | mockMvc.perform(get("/actuator/prometheus")) 68 | .andExpect(status().is(401)) 69 | .andReturn() 70 | .getResponse(); 71 | response = 72 | mockMvc.perform( 73 | get("/actuator/prometheus") 74 | .header( 75 | "Authorization", 76 | "Basic cHJvbWV0aGV1czpwcm9tZXRoZXVz")) 77 | .andExpect(status().isOk()) 78 | .andReturn() 79 | .getResponse(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/mock/MockForceUpdateConfig.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.config.mock; 2 | 3 | import ch.ubique.openapi.docannotations.Documentation; 4 | import java.util.List; 5 | import org.dpppt.switzerland.backend.sdk.config.ws.controller.GaenConfigController; 6 | import org.dpppt.switzerland.backend.sdk.config.ws.helper.VaccinationInfoHelper; 7 | import org.dpppt.switzerland.backend.sdk.config.ws.model.ConfigResponse; 8 | import org.dpppt.switzerland.backend.sdk.config.ws.poeditor.Messages; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.context.annotation.Primary; 13 | import org.springframework.context.annotation.Profile; 14 | import org.springframework.http.ResponseEntity; 15 | 16 | @Configuration 17 | @Profile("mock-forceupdate") 18 | public class MockForceUpdateConfig { 19 | 20 | @Value("${ws.interops.countrycodes:}") 21 | List interOpsCountryCodes; 22 | 23 | @Bean 24 | @Primary 25 | public GaenConfigController gaenConfigController( 26 | Messages messages, 27 | VaccinationInfoHelper vaccinationInfoHelper, 28 | @Value("${ws.vaccination-info.show:false}") boolean showVaccinationInfo, 29 | @Value("$(ws.deactivate-app:false") boolean deactivate) { 30 | return new MockForceUpdateController(messages, vaccinationInfoHelper, showVaccinationInfo, deactivate); 31 | } 32 | 33 | public class MockForceUpdateController extends GaenConfigController { 34 | 35 | public MockForceUpdateController( 36 | Messages messages, 37 | VaccinationInfoHelper vaccinationInfoHelper, 38 | boolean showVaccinationInfo, 39 | boolean deactivate) { 40 | super( 41 | messages, 42 | interOpsCountryCodes, 43 | false, 44 | vaccinationInfoHelper, 45 | showVaccinationInfo, 46 | deactivate); 47 | } 48 | 49 | @Override 50 | public @Documentation( 51 | description = "Echo endpoint", 52 | responses = "200 => Hello from DP3T Config WS") String hello() { 53 | return super.hello() + " (mock-forceupdate)"; 54 | } 55 | 56 | @Override 57 | public @Documentation( 58 | description = 59 | "Read latest configuration and messages, depending on the version of the phone and the app.", 60 | responses = 61 | "200 => ConfigResponse structure with eventual notifications and epidemic parameters") 62 | ResponseEntity getConfig( 63 | @Documentation(description = "Version of the App installed", example = "ios-1.0.7") 64 | String appversion, 65 | @Documentation(description = "Version of the OS", example = "ios13.6") 66 | String osversion, 67 | @Documentation( 68 | description = "Build number of the app", 69 | example = "ios-200619.2333.175") 70 | String buildnr) { 71 | ResponseEntity response = 72 | super.getConfig(appversion, osversion, buildnr); 73 | response.getBody().setForceUpdate(true); 74 | ; 75 | return response; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/TestLocationCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | import com.fasterxml.jackson.annotation.JsonInclude; 14 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 15 | import java.util.List; 16 | 17 | @JsonInclude(value = Include.NON_NULL) 18 | public class TestLocationCollection { 19 | 20 | private List de; 21 | private List fr; 22 | private List it; 23 | private List en; 24 | private List pt; 25 | private List es; 26 | private List sq; 27 | private List bs; 28 | private List hr; 29 | private List sr; 30 | private List rm; 31 | private List tr; 32 | private List ti; 33 | 34 | public List getDe() { 35 | return de; 36 | } 37 | 38 | public void setDe(List de) { 39 | this.de = de; 40 | } 41 | 42 | public List getFr() { 43 | return fr; 44 | } 45 | 46 | public void setFr(List fr) { 47 | this.fr = fr; 48 | } 49 | 50 | public List getIt() { 51 | return it; 52 | } 53 | 54 | public void setIt(List it) { 55 | this.it = it; 56 | } 57 | 58 | public List getEn() { 59 | return en; 60 | } 61 | 62 | public void setEn(List en) { 63 | this.en = en; 64 | } 65 | 66 | public List getPt() { 67 | return pt; 68 | } 69 | 70 | public void setPt(List pt) { 71 | this.pt = pt; 72 | } 73 | 74 | public List getEs() { 75 | return es; 76 | } 77 | 78 | public void setEs(List es) { 79 | this.es = es; 80 | } 81 | 82 | public List getSq() { 83 | return sq; 84 | } 85 | 86 | public void setSq(List sq) { 87 | this.sq = sq; 88 | } 89 | 90 | public List getBs() { 91 | return bs; 92 | } 93 | 94 | public void setBs(List bs) { 95 | this.bs = bs; 96 | } 97 | 98 | public List getHr() { 99 | return hr; 100 | } 101 | 102 | public void setHr(List hr) { 103 | this.hr = hr; 104 | } 105 | 106 | public List getSr() { 107 | return sr; 108 | } 109 | 110 | public void setSr(List sr) { 111 | this.sr = sr; 112 | } 113 | 114 | public List getRm() { 115 | return rm; 116 | } 117 | 118 | public void setRm(List rm) { 119 | this.rm = rm; 120 | } 121 | 122 | public List getTr() { 123 | return tr; 124 | } 125 | 126 | public void setTr(List tr) { 127 | this.tr = tr; 128 | } 129 | 130 | public List getTi() { 131 | return ti; 132 | } 133 | 134 | public void setTi(List ti) { 135 | this.ti = ti; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/mock/MockInfoBoxConfig.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.config.mock; 2 | 3 | import ch.ubique.openapi.docannotations.Documentation; 4 | import java.util.List; 5 | import org.dpppt.switzerland.backend.sdk.config.ws.controller.GaenConfigController; 6 | import org.dpppt.switzerland.backend.sdk.config.ws.helper.MockHelper; 7 | import org.dpppt.switzerland.backend.sdk.config.ws.helper.VaccinationInfoHelper; 8 | import org.dpppt.switzerland.backend.sdk.config.ws.model.ConfigResponse; 9 | import org.dpppt.switzerland.backend.sdk.config.ws.poeditor.Messages; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.context.annotation.Primary; 14 | import org.springframework.context.annotation.Profile; 15 | import org.springframework.http.ResponseEntity; 16 | 17 | @Configuration 18 | @Profile("mock-infobox") 19 | public class MockInfoBoxConfig { 20 | 21 | @Value("${ws.interops.countrycodes:}") 22 | List interOpsCountryCodes; 23 | 24 | @Bean 25 | @Primary 26 | public GaenConfigController gaenConfigController( 27 | Messages messages, 28 | VaccinationInfoHelper vaccinationInfoHelper, 29 | @Value("${ws.vaccination-info.show:false}") boolean showVaccinationInfo, 30 | @Value("$(ws.deactivate-app:false") boolean deactivate) { 31 | return new MockInfoBoxController(messages, vaccinationInfoHelper, showVaccinationInfo, deactivate); 32 | } 33 | 34 | public class MockInfoBoxController extends GaenConfigController { 35 | 36 | public MockInfoBoxController( 37 | Messages messages, 38 | VaccinationInfoHelper vaccinationInfoHelper, 39 | boolean showVaccinationInfo, 40 | boolean deactivate) { 41 | super( 42 | messages, 43 | interOpsCountryCodes, 44 | false, 45 | vaccinationInfoHelper, 46 | showVaccinationInfo, 47 | deactivate); 48 | } 49 | 50 | @Override 51 | public @Documentation( 52 | description = "Echo endpoint", 53 | responses = "200 => Hello from DP3T Config WS") String hello() { 54 | return super.hello() + " (mock-infobox)"; 55 | } 56 | 57 | @Override 58 | public @Documentation( 59 | description = 60 | "Read latest configuration and messages, depending on the version of the phone and the app.", 61 | responses = 62 | "200 => ConfigResponse structure with eventual notifications and epidemic parameters") 63 | ResponseEntity getConfig( 64 | @Documentation(description = "Version of the App installed", example = "ios-1.0.7") 65 | String appversion, 66 | @Documentation(description = "Version of the OS", example = "ios13.6") 67 | String osversion, 68 | @Documentation( 69 | description = "Build number of the app", 70 | example = "ios-200619.2333.175") 71 | String buildnr) { 72 | ResponseEntity response = 73 | super.getConfig(appversion, osversion, buildnr); 74 | response.getBody() 75 | .setInfoBox( 76 | MockHelper.mockConfigResponseWithInfoBox(true, messages).getInfoBox()); 77 | return response; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/InfoBoxCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | import com.fasterxml.jackson.annotation.JsonInclude; 14 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 15 | 16 | @JsonInclude(value = Include.NON_NULL) 17 | public class InfoBoxCollection { 18 | 19 | private InfoBox deInfoBox; 20 | private InfoBox frInfoBox; 21 | private InfoBox itInfoBox; 22 | private InfoBox enInfoBox; 23 | private InfoBox ptInfoBox; 24 | private InfoBox esInfoBox; 25 | private InfoBox sqInfoBox; 26 | private InfoBox bsInfoBox; 27 | private InfoBox hrInfoBox; 28 | private InfoBox srInfoBox; 29 | private InfoBox rmInfoBox; 30 | private InfoBox trInfoBox; 31 | private InfoBox tiInfoBox; 32 | 33 | public InfoBox getPtInfoBox() { 34 | return this.ptInfoBox; 35 | } 36 | 37 | public void setPtInfoBox(InfoBox ptInfoBox) { 38 | this.ptInfoBox = ptInfoBox; 39 | } 40 | 41 | public InfoBox getEsInfoBox() { 42 | return this.esInfoBox; 43 | } 44 | 45 | public void setEsInfoBox(InfoBox esInfoBox) { 46 | this.esInfoBox = esInfoBox; 47 | } 48 | 49 | public InfoBox getSqInfoBox() { 50 | return this.sqInfoBox; 51 | } 52 | 53 | public void setSqInfoBox(InfoBox sqInfoBox) { 54 | this.sqInfoBox = sqInfoBox; 55 | } 56 | 57 | public InfoBox getBsInfoBox() { 58 | return this.bsInfoBox; 59 | } 60 | 61 | public void setBsInfoBox(InfoBox bsInfoBox) { 62 | this.bsInfoBox = bsInfoBox; 63 | } 64 | 65 | public InfoBox getHrInfoBox() { 66 | return this.hrInfoBox; 67 | } 68 | 69 | public void setHrInfoBox(InfoBox hrInfoBox) { 70 | this.hrInfoBox = hrInfoBox; 71 | } 72 | 73 | public InfoBox getSrInfoBox() { 74 | return this.srInfoBox; 75 | } 76 | 77 | public void setSrInfoBox(InfoBox srInfoBox) { 78 | this.srInfoBox = srInfoBox; 79 | } 80 | 81 | public InfoBox getRmInfoBox() { 82 | return this.rmInfoBox; 83 | } 84 | 85 | public void setRmInfoBox(InfoBox rmInfoBox) { 86 | this.rmInfoBox = rmInfoBox; 87 | } 88 | 89 | public InfoBox getDeInfoBox() { 90 | return deInfoBox; 91 | } 92 | 93 | public InfoBox getEnInfoBox() { 94 | return enInfoBox; 95 | } 96 | 97 | public void setEnInfoBox(InfoBox enInfoBox) { 98 | this.enInfoBox = enInfoBox; 99 | } 100 | 101 | public InfoBox getItInfoBox() { 102 | return itInfoBox; 103 | } 104 | 105 | public void setItInfoBox(InfoBox itInfoBox) { 106 | this.itInfoBox = itInfoBox; 107 | } 108 | 109 | public InfoBox getFrInfoBox() { 110 | return frInfoBox; 111 | } 112 | 113 | public void setFrInfoBox(InfoBox frInfoBox) { 114 | this.frInfoBox = frInfoBox; 115 | } 116 | 117 | public void setDeInfoBox(InfoBox deInfoBox) { 118 | this.deInfoBox = deInfoBox; 119 | } 120 | 121 | public InfoBox getTrInfoBox() { 122 | return trInfoBox; 123 | } 124 | 125 | public void setTrInfoBox(InfoBox trInfoBox) { 126 | this.trInfoBox = trInfoBox; 127 | } 128 | 129 | public InfoBox getTiInfoBox() { 130 | return tiInfoBox; 131 | } 132 | 133 | public void setTiInfoBox(InfoBox tiInfoBox) { 134 | this.tiInfoBox = tiInfoBox; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/WhatToDoPositiveTestTextsCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | import com.fasterxml.jackson.annotation.JsonInclude; 14 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 15 | 16 | @JsonInclude(value = Include.NON_NULL) 17 | public class WhatToDoPositiveTestTextsCollection { 18 | 19 | private WhatToDoPositiveTestTexts de; 20 | private WhatToDoPositiveTestTexts fr; 21 | private WhatToDoPositiveTestTexts it; 22 | private WhatToDoPositiveTestTexts en; 23 | private WhatToDoPositiveTestTexts pt; 24 | private WhatToDoPositiveTestTexts es; 25 | private WhatToDoPositiveTestTexts sq; 26 | private WhatToDoPositiveTestTexts bs; 27 | private WhatToDoPositiveTestTexts hr; 28 | private WhatToDoPositiveTestTexts sr; 29 | private WhatToDoPositiveTestTexts rm; 30 | private WhatToDoPositiveTestTexts tr; 31 | private WhatToDoPositiveTestTexts ti; 32 | 33 | public WhatToDoPositiveTestTexts getDe() { 34 | return de; 35 | } 36 | 37 | public void setDe(WhatToDoPositiveTestTexts de) { 38 | this.de = de; 39 | } 40 | 41 | public WhatToDoPositiveTestTexts getFr() { 42 | return fr; 43 | } 44 | 45 | public void setFr(WhatToDoPositiveTestTexts fr) { 46 | this.fr = fr; 47 | } 48 | 49 | public WhatToDoPositiveTestTexts getIt() { 50 | return it; 51 | } 52 | 53 | public void setIt(WhatToDoPositiveTestTexts it) { 54 | this.it = it; 55 | } 56 | 57 | public WhatToDoPositiveTestTexts getEn() { 58 | return en; 59 | } 60 | 61 | public void setEn(WhatToDoPositiveTestTexts en) { 62 | this.en = en; 63 | } 64 | 65 | public WhatToDoPositiveTestTexts getPt() { 66 | return pt; 67 | } 68 | 69 | public void setPt(WhatToDoPositiveTestTexts pt) { 70 | this.pt = pt; 71 | } 72 | 73 | public WhatToDoPositiveTestTexts getEs() { 74 | return es; 75 | } 76 | 77 | public void setEs(WhatToDoPositiveTestTexts es) { 78 | this.es = es; 79 | } 80 | 81 | public WhatToDoPositiveTestTexts getSq() { 82 | return sq; 83 | } 84 | 85 | public void setSq(WhatToDoPositiveTestTexts sq) { 86 | this.sq = sq; 87 | } 88 | 89 | public WhatToDoPositiveTestTexts getBs() { 90 | return bs; 91 | } 92 | 93 | public void setBs(WhatToDoPositiveTestTexts bs) { 94 | this.bs = bs; 95 | } 96 | 97 | public WhatToDoPositiveTestTexts getHr() { 98 | return hr; 99 | } 100 | 101 | public void setHr(WhatToDoPositiveTestTexts hr) { 102 | this.hr = hr; 103 | } 104 | 105 | public WhatToDoPositiveTestTexts getSr() { 106 | return sr; 107 | } 108 | 109 | public void setSr(WhatToDoPositiveTestTexts sr) { 110 | this.sr = sr; 111 | } 112 | 113 | public WhatToDoPositiveTestTexts getRm() { 114 | return rm; 115 | } 116 | 117 | public void setRm(WhatToDoPositiveTestTexts rm) { 118 | this.rm = rm; 119 | } 120 | 121 | public WhatToDoPositiveTestTexts getTr() { 122 | return tr; 123 | } 124 | 125 | public void setTr(WhatToDoPositiveTestTexts tr) { 126 | this.tr = tr; 127 | } 128 | 129 | public WhatToDoPositiveTestTexts getTi() { 130 | return ti; 131 | } 132 | 133 | public void setTi(WhatToDoPositiveTestTexts ti) { 134 | this.ti = ti; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwissCovid Config Backend 2 | 3 | SwissCovid is the official contact tracing app of Switzerland. The app can be installed from the [App Store](https://apps.apple.com/ch/app/swisscovid/id1509275381) or the [Google Play Store](https://play.google.com/store/apps/details?id=ch.admin.bag.dp3t). The SwissCovid 2.0 app uses two types of contact tracing to prevent the spread of COVID-19. 4 | 5 | With proximity tracing close contacts are detected using the bluetooth technology. For this the [dp3t-sdk-backend](https://github.com/DP-3T/dp3t-sdk-backend), [DP3T iOS SDK](https://github.com/DP-3T/dp3t-sdk-ios) and [DP3T Android SDK](https://github.com/DP-3T/dp3t-sdk-android) are used that build on top of the Google & Apple Exposure Notifications. This feature is called SwissCovid encounters. 6 | 7 | With presence tracing people that are at the same venue at the same time are detected. For this the [swisscovid-cn-backend](https://github.com/SwissCovid/swisscovid-cn-backend), [CrowdNotifier iOS SDK](https://github.com/CrowdNotifier/crowdnotifier-sdk-ios) and [CrowdNotifier Android SDK](https://github.com/CrowdNotifier/crowdnotifier-sdk-android) are used that provide a secure, decentralized, privacy-preserving presence tracing system. This feature is called SwissCovid Check-in. 8 | 9 | ## Description 10 | 11 | This backend is used by the apps to get their config every 6h. It is used by the Swiss Government to update the 12 | epidemiological parameters if needed. 13 | 14 | The following endpoints are available: 15 | 16 | - `/v1` - returns `Hello from DP3T Config WS` 17 | - `/v1/config` - returns a `ConfigResponse` with the following structure and default values: 18 | - `InfoBoxCollection` - empty by default 19 | - `deInfoBox` `InfoBox` 20 | - `title` `String` 21 | - `msg` `String` 22 | - `url` `String` 23 | - `urlTitle` `String` 24 | - `isDismissible = false` 25 | - `frInfoBox` `InfoBox` 26 | - `itInfoBox` `InfoBox` 27 | - `enInfoBox` `InfoBox` 28 | - `ptInfoBox` `InfoBox` 29 | - `esInfoBox` `InfoBox` 30 | - `sqInfoBox` `InfoBox` 31 | - `bsInfoBox` `InfoBox` 32 | - `hrInfoBox` `InfoBox` 33 | - `srInfoBox` `InfoBox` 34 | - `rmInfoBox` `InfoBox` 35 | - `SDKConfig` - will soon be removed (30/7/2020) 36 | - `numberOfWindowsForExposure = 3` 37 | - @Deprecated `eventThreshold = 0.8f` 38 | - @Deprecated `badAttenuationThreshold = 73.0f` 39 | - `contactAttenuationThreshold = 73.0f` 40 | - iOSGaenSdkConfig `GAENSDKConfig` 41 | - `lowerThreshold = 53` 42 | - `higherThreshold = 60` 43 | - `factorLow = 1.0d` 44 | - `factorHigh = 0.5d` 45 | - `triggerThreshold = 15` 46 | - androidGaenSdkConfig `GAENSDKConfig` 47 | - `/v1/testinfobox/config` - returns a `ConfigResponse` with only the `SDKConfig` set to a static message 48 | 49 | ## /config special actions 50 | 51 | The call to `/v1/config` does the following special actions: 52 | 1. `If iOS == 13.6`: warn that the German 'no risk' message is misleading 53 | 1. `If App == Testflight`: warn that the app should be updated to the official app-store app 54 | 1. `If App == Initial iOS test app`: set `factorHigh` = `0` to avoid errors 55 | 56 | ## Repositories 57 | 58 | - Android App: [swisscovid-app-android](https://github.com/SwissCovid/swisscovid-app-android) 59 | - iOS App: [swisscovid-app-ios](https://github.com/SwissCovid/swisscovid-app-ios) 60 | - CovidCode Web-App: [CovidCode-UI](https://github.com/admin-ch/CovidCode-UI) 61 | - CovidCode Backend: [CovidCode-Service](https://github.com/admin-ch/CovidCode-service) 62 | - Config Backend: [swisscovid-config-backend](https://github.com/SwissCovid/swisscovid-config-backend) 63 | - Additional Info Backend: [swisscovid-additionalinfo-backend](https://github.com/SwissCovid/swisscovid-additionalinfo-backend) 64 | - QR Code Landingpage: [swisscovid-qr-landingpage](https://github.com/SwissCovid/swisscovid-qr-landingpage) 65 | - DP3T Android SDK & Calibration app: [dp3t-sdk-android](https://github.com/DP-3T/dp3t-sdk-android) 66 | - DP3T iOS SDK & Calibration app: [dp3t-sdk-ios](https://github.com/DP-3T/dp3t-sdk-ios) 67 | - DP3T Backend SDK: [dp3t-sdk-backend](https://github.com/DP-3T/dp3t-sdk-backend) 68 | - CrowdNotifier Android SDK: [crowdnotifier-sdk-android](https://github.com/CrowdNotifier/crowdnotifier-sdk-android) 69 | - CrowdNotifier iOS SDK: [crowdnotifier-sdk-ios](https://github.com/CrowdNotifier/crowdnotifier-sdk-ios) 70 | - CrowdNotifier Backend: [swisscovid-cn-backend](https://github.com/SwissCovid/swisscovid-cn-backend) 71 | 72 | ## License 73 | 74 | This project is licensed under the terms of the MPL 2 license. See the [LICENSE](LICENSE) file. 75 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/helper/TestLocationHelper.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.helper; 2 | 3 | import java.util.ArrayList; 4 | import java.util.EnumMap; 5 | import java.util.List; 6 | import java.util.Locale; 7 | import java.util.Map; 8 | import org.dpppt.switzerland.backend.sdk.config.ws.model.Canton; 9 | import org.dpppt.switzerland.backend.sdk.config.ws.model.Language; 10 | import org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation; 11 | import org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocationCollection; 12 | import org.dpppt.switzerland.backend.sdk.config.ws.poeditor.Messages; 13 | 14 | /** 15 | * Holds the url for test locations for each canton. 16 | * 17 | * @author alig 18 | */ 19 | public class TestLocationHelper { 20 | 21 | private final Messages messages; 22 | private final TestLocationCollection testLocationCollection; 23 | private final Map testInfoUrls; 24 | 25 | private static final List ALL_CANTONS_AND_LIECHTENSTEIN; 26 | 27 | static { 28 | { 29 | ALL_CANTONS_AND_LIECHTENSTEIN = Canton.namePoeditorKeys(); 30 | ALL_CANTONS_AND_LIECHTENSTEIN.add("country_liechtenstein"); 31 | } 32 | } 33 | 34 | public TestLocationHelper(Messages messages) { 35 | this.messages = messages; 36 | this.testLocationCollection = new TestLocationCollection(); 37 | 38 | testLocationCollection.setBs(getTestLocationsForLanguage(Language.BS.toLocale())); 39 | testLocationCollection.setDe(getTestLocationsForLanguage(Language.DE.toLocale())); 40 | testLocationCollection.setEn(getTestLocationsForLanguage(Language.EN.toLocale())); 41 | testLocationCollection.setEs(getTestLocationsForLanguage(Language.ES.toLocale())); 42 | testLocationCollection.setFr(getTestLocationsForLanguage(Language.FR.toLocale())); 43 | testLocationCollection.setHr(getTestLocationsForLanguage(Language.HR.toLocale())); 44 | testLocationCollection.setIt(getTestLocationsForLanguage(Language.IT.toLocale())); 45 | testLocationCollection.setPt(getTestLocationsForLanguage(Language.PT.toLocale())); 46 | testLocationCollection.setRm(getTestLocationsForLanguage(Language.RM.toLocale())); 47 | testLocationCollection.setSq(getTestLocationsForLanguage(Language.SQ.toLocale())); 48 | testLocationCollection.setSr(getTestLocationsForLanguage(Language.SR.toLocale())); 49 | testLocationCollection.setTi(getTestLocationsForLanguage(Language.TI.toLocale())); 50 | testLocationCollection.setTr(getTestLocationsForLanguage(Language.TR.toLocale())); 51 | 52 | this.testInfoUrls = new EnumMap<>(Language.class); 53 | testInfoUrls.put(Language.BS, messages.getMessage("test_info_url", Language.BS.toLocale())); 54 | testInfoUrls.put(Language.DE, messages.getMessage("test_info_url", Language.DE.toLocale())); 55 | testInfoUrls.put(Language.EN, messages.getMessage("test_info_url", Language.EN.toLocale())); 56 | testInfoUrls.put(Language.ES, messages.getMessage("test_info_url", Language.ES.toLocale())); 57 | testInfoUrls.put(Language.FR, messages.getMessage("test_info_url", Language.FR.toLocale())); 58 | testInfoUrls.put(Language.HR, messages.getMessage("test_info_url", Language.HR.toLocale())); 59 | testInfoUrls.put(Language.IT, messages.getMessage("test_info_url", Language.IT.toLocale())); 60 | testInfoUrls.put(Language.PT, messages.getMessage("test_info_url", Language.PT.toLocale())); 61 | testInfoUrls.put(Language.RM, messages.getMessage("test_info_url", Language.RM.toLocale())); 62 | testInfoUrls.put(Language.SQ, messages.getMessage("test_info_url", Language.SQ.toLocale())); 63 | testInfoUrls.put(Language.SR, messages.getMessage("test_info_url", Language.SR.toLocale())); 64 | testInfoUrls.put(Language.TI, messages.getMessage("test_info_url", Language.TI.toLocale())); 65 | testInfoUrls.put(Language.TR, messages.getMessage("test_info_url", Language.TR.toLocale())); 66 | } 67 | 68 | private List getTestLocationsForLanguage(Locale language) { 69 | List testLocations = new ArrayList<>(); 70 | for (String region : ALL_CANTONS_AND_LIECHTENSTEIN) { 71 | testLocations.add( 72 | new TestLocation( 73 | region, messages.getMessage("testlocation_url_" + region, language))); 74 | } 75 | return testLocations; 76 | } 77 | 78 | public TestLocationCollection getTestLocations() { 79 | return this.testLocationCollection; 80 | } 81 | 82 | public Map getTestInfoUrls() { 83 | return this.testInfoUrls; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/test/java/org/dpppt/switzerland/backend/sdk/config/ws/VersionTest.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertNotEquals; 6 | import static org.junit.Assert.assertTrue; 7 | 8 | import java.util.List; 9 | import org.dpppt.switzerland.backend.sdk.config.ws.semver.Version; 10 | import org.junit.Test; 11 | 12 | public class VersionTest { 13 | 14 | @Test 15 | public void testToString() throws Exception { 16 | var v = new Version("ios-1.1.3-test+meta"); 17 | assertEquals("1.1.3-test+meta", v.toString()); 18 | v = new Version("1.1.3+meta"); 19 | assertEquals("1.1.3+meta", v.toString()); 20 | v = new Version("ios-1.1.3-meta"); 21 | assertEquals("1.1.3-meta", v.toString()); 22 | v = new Version("ios-1.1.3"); 23 | assertEquals("1.1.3", v.toString()); 24 | v = new Version("1.1.3"); 25 | assertEquals("1.1.3", v.toString()); 26 | } 27 | 28 | @Test 29 | public void testVersionFromString() throws Exception { 30 | var cases = 31 | List.of( 32 | new Version("ios-0.1.0"), 33 | new Version("android-0.1.1"), 34 | new Version("0.2.0"), 35 | new Version("1.0.0-prerelease"), 36 | new Version("1.0.0"), 37 | new Version("1.0.1+ios")); 38 | for (int i = 0; i < cases.size(); i++) { 39 | var currentVersion = cases.get(i); 40 | assertTrue(currentVersion.isSameVersionAs(currentVersion)); 41 | for (int j = 0; j < i; j++) { 42 | var olderVersion = cases.get(j); 43 | assertTrue(currentVersion.isLargerVersionThan(olderVersion)); 44 | assertFalse(currentVersion.isSameVersionAs(olderVersion)); 45 | assertFalse(currentVersion.isSmallerVersionThan(olderVersion)); 46 | 47 | assertFalse(olderVersion.isLargerVersionThan(currentVersion)); 48 | assertFalse(olderVersion.isSameVersionAs(currentVersion)); 49 | assertTrue(olderVersion.isSmallerVersionThan(currentVersion)); 50 | } 51 | } 52 | var releaseVersion = new Version("1.0.0"); 53 | var metaInfoVersion = new Version("1.0.0+ios"); 54 | assertTrue(releaseVersion.isSameVersionAs(metaInfoVersion)); 55 | assertNotEquals(metaInfoVersion, releaseVersion); 56 | var sameIosVersion = new Version("1.0.0+ios"); 57 | assertEquals(sameIosVersion, metaInfoVersion); 58 | } 59 | 60 | @Test 61 | public void testPlatform() throws Exception { 62 | var iosNonStandard = new Version("ios-1.0.0"); 63 | var iosStandard = new Version("1.0.0+ios"); 64 | assertTrue(iosNonStandard.isIOS()); 65 | assertTrue(iosStandard.isIOS()); 66 | assertFalse(iosNonStandard.isAndroid()); 67 | assertFalse(iosStandard.isAndroid()); 68 | 69 | var androidNonStandard = new Version("android-1.0.0"); 70 | var androidStandard = new Version("1.0.0+android"); 71 | assertFalse(androidNonStandard.isIOS()); 72 | assertFalse(androidStandard.isIOS()); 73 | assertTrue(androidNonStandard.isAndroid()); 74 | assertTrue(androidStandard.isAndroid()); 75 | 76 | var random = new Version("1.0.0"); 77 | assertFalse(random.isAndroid()); 78 | assertFalse(random.isIOS()); 79 | } 80 | 81 | @Test 82 | public void testVersionFromExplicit() throws Exception { 83 | var cases = 84 | List.of( 85 | new Version(0, 1, 0), 86 | new Version(0, 1, 1), 87 | new Version(0, 2, 0), 88 | new Version(1, 0, 0, "prerelease", ""), 89 | new Version(1, 0, 0), 90 | new Version(1, 0, 1, "", "ios")); 91 | for (int i = 0; i < cases.size(); i++) { 92 | var currentVersion = cases.get(i); 93 | assertTrue(currentVersion.isSameVersionAs(currentVersion)); 94 | for (int j = 0; j < i; j++) { 95 | var olderVersion = cases.get(j); 96 | assertTrue(currentVersion.isLargerVersionThan(olderVersion)); 97 | } 98 | } 99 | var releaseVersion = new Version(1, 0, 0); 100 | var metaInfoVersion = new Version(1, 0, 0, "", "ios"); 101 | assertTrue(releaseVersion.isSameVersionAs(metaInfoVersion)); 102 | assertNotEquals(metaInfoVersion, releaseVersion); 103 | var sameIosVersion = new Version(1, 0, 0, "", "ios"); 104 | assertEquals(sameIosVersion, metaInfoVersion); 105 | } 106 | 107 | @Test 108 | public void testMissingMinorOrPatch() throws Exception { 109 | var apiLevel = "29"; 110 | var iosVersion = "13.6"; 111 | var apiLevelWithMeta = "29+test"; 112 | var iosVersionWithMeta = "13.6+test"; 113 | var apiLevelVersion = new Version(apiLevel); 114 | assertTrue( 115 | apiLevelVersion.getMajor().equals(29) 116 | && apiLevelVersion.getMinor().equals(0) 117 | && apiLevelVersion.getPatch().equals(0)); 118 | 119 | var iosVersionVersion = new Version(iosVersion); 120 | assertTrue( 121 | iosVersionVersion.getMajor().equals(13) 122 | && iosVersionVersion.getMinor().equals(6) 123 | && iosVersionVersion.getPatch().equals(0)); 124 | 125 | var apiLevelWithMetaVersion = new Version(apiLevelWithMeta); 126 | assertTrue( 127 | apiLevelWithMetaVersion.getMajor().equals(29) 128 | && apiLevelWithMetaVersion.getMinor().equals(0) 129 | && apiLevelWithMetaVersion.getPatch().equals(0) 130 | && apiLevelWithMetaVersion.getMetaInfo().equals("test")); 131 | 132 | var iosVersionVersionMeta = new Version(iosVersionWithMeta); 133 | 134 | assertTrue( 135 | iosVersionVersionMeta.getMajor().equals(13) 136 | && iosVersionVersionMeta.getMinor().equals(6) 137 | && iosVersionVersionMeta.getPatch().equals(0) 138 | && iosVersionVersionMeta.getMetaInfo().equals("test")); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/config/WSBaseConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.config; 12 | 13 | import io.jsonwebtoken.SignatureAlgorithm; 14 | import java.io.ByteArrayInputStream; 15 | import java.io.Reader; 16 | import java.io.StringReader; 17 | import java.security.KeyFactory; 18 | import java.security.KeyPair; 19 | import java.security.PrivateKey; 20 | import java.security.PublicKey; 21 | import java.security.Security; 22 | import java.security.cert.CertificateFactory; 23 | import java.security.spec.PKCS8EncodedKeySpec; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.concurrent.TimeUnit; 27 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 28 | import org.bouncycastle.util.io.pem.PemObject; 29 | import org.bouncycastle.util.io.pem.PemReader; 30 | import org.dpppt.switzerland.backend.sdk.config.ws.controller.GaenConfigController; 31 | import org.dpppt.switzerland.backend.sdk.config.ws.filter.ResponseWrapperFilter; 32 | import org.dpppt.switzerland.backend.sdk.config.ws.helper.VaccinationInfoHelper; 33 | import org.dpppt.switzerland.backend.sdk.config.ws.interceptor.HeaderInjector; 34 | import org.dpppt.switzerland.backend.sdk.config.ws.poeditor.Messages; 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | import org.springframework.beans.factory.annotation.Value; 38 | import org.springframework.context.MessageSource; 39 | import org.springframework.context.annotation.Bean; 40 | import org.springframework.context.annotation.Configuration; 41 | import org.springframework.context.support.ReloadableResourceBundleMessageSource; 42 | import org.springframework.http.CacheControl; 43 | import org.springframework.scheduling.annotation.EnableScheduling; 44 | import org.springframework.scheduling.annotation.SchedulingConfigurer; 45 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 46 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 47 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 48 | 49 | @Configuration 50 | @EnableScheduling 51 | public abstract class WSBaseConfig implements SchedulingConfigurer, WebMvcConfigurer { 52 | 53 | protected final Logger logger = LoggerFactory.getLogger(getClass()); 54 | final SignatureAlgorithm algorithm = SignatureAlgorithm.ES256; 55 | 56 | @Value("${ws.interops.countrycodes:}") 57 | List interOpsCountryCodes; 58 | 59 | @Value("${ws.headers.protected:}") 60 | List protectedHeaders; 61 | 62 | @Value("${ws.retentiondays: 21}") 63 | int retentionDays; 64 | 65 | @Value("${ws.checkinupdatenotificationenabled: true}") 66 | boolean checkInUpdateNotificationEnabled; 67 | 68 | @Value( 69 | "#{${ws.security.headers: {'X-Content-Type-Options':'nosniff', 'X-Frame-Options':'DENY','X-Xss-Protection':'1; mode=block'}}}") 70 | Map additionalHeaders; 71 | 72 | abstract String getPublicKey(); 73 | 74 | abstract String getPrivateKey(); 75 | 76 | @Override 77 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 78 | registry.addResourceHandler("/**") 79 | .addResourceLocations("classpath:/static/ios_agency_image") 80 | .setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS)); 81 | } 82 | 83 | @Bean 84 | public VaccinationInfoHelper vaccinationInfoHelper(Messages messages) { 85 | return new VaccinationInfoHelper(messages); 86 | } 87 | 88 | @Bean 89 | public GaenConfigController gaenConfigController( 90 | Messages messages, 91 | VaccinationInfoHelper vaccinationInfoHelper, 92 | @Value("${ws.vaccination-info.show:false}") boolean showVaccinationInfo, 93 | @Value("${ws.deactivate-app:false}") boolean deactivate) { 94 | return new GaenConfigController( 95 | messages, 96 | interOpsCountryCodes, 97 | checkInUpdateNotificationEnabled, 98 | vaccinationInfoHelper, 99 | showVaccinationInfo, 100 | deactivate); 101 | } 102 | 103 | @Bean 104 | public ResponseWrapperFilter hashFilter() { 105 | return new ResponseWrapperFilter(getKeyPair(algorithm), retentionDays, protectedHeaders); 106 | } 107 | 108 | @Bean 109 | public HeaderInjector securityHeaderInjector() { 110 | return new HeaderInjector(additionalHeaders); 111 | } 112 | 113 | @Override 114 | public void addInterceptors(InterceptorRegistry registry) { 115 | registry.addInterceptor(securityHeaderInjector()); 116 | } 117 | 118 | public KeyPair getKeyPair(SignatureAlgorithm algorithm) { 119 | Security.addProvider(new BouncyCastleProvider()); 120 | Security.setProperty("crypto.policy", "unlimited"); 121 | return new KeyPair(loadPublicKeyFromString(), loadPrivateKeyFromString()); 122 | } 123 | 124 | private PrivateKey loadPrivateKeyFromString() { 125 | try { 126 | String privateKey = getPrivateKey(); 127 | Reader reader = new StringReader(privateKey); 128 | PemReader readerPem = new PemReader(reader); 129 | PemObject obj = readerPem.readPemObject(); 130 | PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(obj.getContent()); 131 | KeyFactory kf = KeyFactory.getInstance("ECDSA", "BC"); 132 | return (PrivateKey) kf.generatePrivate(pkcs8KeySpec); 133 | } catch (Exception ex) { 134 | ex.printStackTrace(); 135 | throw new RuntimeException(); 136 | } 137 | } 138 | 139 | private PublicKey loadPublicKeyFromString() { 140 | try { 141 | return CertificateFactory.getInstance("X.509") 142 | .generateCertificate(new ByteArrayInputStream(getPublicKey().getBytes())) 143 | .getPublicKey(); 144 | } catch (Exception ex) { 145 | ex.printStackTrace(); 146 | throw new RuntimeException(); 147 | } 148 | } 149 | 150 | @Bean 151 | public Messages messages(MessageSource messageSource) { 152 | Messages messages = new Messages(messageSource); 153 | return messages; 154 | } 155 | 156 | @Bean 157 | public MessageSource messageSource() { 158 | ReloadableResourceBundleMessageSource messageSource = 159 | new ReloadableResourceBundleMessageSource(); 160 | messageSource.setBasenames("classpath:i18n/messages", "classpath:i18n/messages_v1_legacy"); 161 | messageSource.setDefaultEncoding("UTF-8"); 162 | messageSource.setFallbackToSystemLocale(false); 163 | messageSource.setDefaultLocale(null); 164 | return messageSource; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/model/ConfigResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.model; 12 | 13 | import ch.ubique.openapi.docannotations.Documentation; 14 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | @Documentation(description = "ConfigResponse description") 21 | public class ConfigResponse { 22 | 23 | @Documentation( 24 | description = 25 | "Blocks the app and shows a link to the app-store. The user can only continue once " 26 | + "she updated the app") 27 | private boolean forceUpdate = false; 28 | 29 | @Documentation(description = "Holds a message translated in different languages") 30 | private InfoBoxCollection infoBox = null; 31 | 32 | private WhatToDoPositiveTestTextsCollection whatToDoPositiveTestTexts; 33 | 34 | @Documentation(description = "Holds a url for test locations for each canton and Liechtenstein") 35 | private TestLocationCollection testLocations; 36 | 37 | @Documentation(description = "GAEN epidemiological parameters for iOS") 38 | private GAENSDKConfig iOSGaenSdkConfig = new GAENSDKConfig(); 39 | 40 | @Documentation(description = "GAEN epidemiological parameters for Android") 41 | private GAENSDKConfig androidGaenSdkConfig = new GAENSDKConfig(); 42 | 43 | @Documentation( 44 | description = 45 | "list of ISO 3166-1 alpha-2 country codes describing the available interops countries") 46 | private List interOpsCountries = new ArrayList<>(); 47 | 48 | @Documentation(description = "Localized urls to test information website") 49 | private Map testInformationUrls; 50 | 51 | @Documentation( 52 | description = 53 | "Flag to enable notifications informing users about the new checkin feature") 54 | private boolean checkInUpdateNotificationEnabled = false; 55 | 56 | @Documentation(description = "Holds vaccination booking infos for cantons by language") 57 | private Map> vaccinationBookingCantons = null; 58 | 59 | @Documentation(description = "Holds general vaccination booking info by language") 60 | private Map vaccinationBookingInfo = null; 61 | 62 | @Documentation( 63 | description = 64 | "feature flag. when set to true the vaccination infos should be displayed") 65 | private Boolean showVaccinationInfo; 66 | 67 | @Documentation( 68 | description = "Flag to deactivate the app, stopping contact tracing and showing a message to the user" 69 | ) 70 | private Boolean deactivate; 71 | 72 | @Documentation(description = "Holds a message translated in different languages") 73 | private InfoBoxCollection deactivationMessage = null; 74 | 75 | public boolean isForceUpdate() { 76 | return forceUpdate; 77 | } 78 | 79 | public void setForceUpdate(boolean forceUpdate) { 80 | this.forceUpdate = forceUpdate; 81 | } 82 | 83 | public InfoBoxCollection getInfoBox() { 84 | return infoBox; 85 | } 86 | 87 | public void setInfoBox(InfoBoxCollection infoBox) { 88 | this.infoBox = infoBox; 89 | } 90 | 91 | public WhatToDoPositiveTestTextsCollection getWhatToDoPositiveTestTexts() { 92 | return whatToDoPositiveTestTexts; 93 | } 94 | 95 | public void setWhatToDoPositiveTestTexts( 96 | WhatToDoPositiveTestTextsCollection whatToDoPositiveTestTexts) { 97 | this.whatToDoPositiveTestTexts = whatToDoPositiveTestTexts; 98 | } 99 | 100 | public GAENSDKConfig getiOSGaenSdkConfig() { 101 | return iOSGaenSdkConfig; 102 | } 103 | 104 | public void setiOSGaenSdkConfig(GAENSDKConfig iOSGaenSdkConfig) { 105 | this.iOSGaenSdkConfig = iOSGaenSdkConfig; 106 | } 107 | 108 | public GAENSDKConfig getAndroidGaenSdkConfig() { 109 | return androidGaenSdkConfig; 110 | } 111 | 112 | public void setAndroidGaenSdkConfig(GAENSDKConfig androidGaenSdkConfig) { 113 | this.androidGaenSdkConfig = androidGaenSdkConfig; 114 | } 115 | 116 | public TestLocationCollection getTestLocations() { 117 | return testLocations; 118 | } 119 | 120 | public void setTestLocations(TestLocationCollection testLocations) { 121 | this.testLocations = testLocations; 122 | } 123 | 124 | public List getInterOpsCountries() { 125 | return interOpsCountries; 126 | } 127 | 128 | public void setInterOpsCountries(List interOpsCountries) { 129 | this.interOpsCountries = interOpsCountries; 130 | } 131 | 132 | public Map getTestInformationUrls() { 133 | return testInformationUrls; 134 | } 135 | 136 | public void setTestInformationUrls(Map testInformationUrls) { 137 | this.testInformationUrls = testInformationUrls; 138 | } 139 | 140 | public void setCheckInUpdateNotificationEnabled(boolean checkInUpdateNotificationEnabled) { 141 | this.checkInUpdateNotificationEnabled = checkInUpdateNotificationEnabled; 142 | } 143 | 144 | public boolean isCheckInUpdateNotificationEnabled() { 145 | return checkInUpdateNotificationEnabled; 146 | } 147 | 148 | public Map> getVaccinationBookingCantons() { 149 | return vaccinationBookingCantons; 150 | } 151 | 152 | public void setVaccinationBookingCantons( 153 | Map> vaccinationBookingCantons) { 154 | this.vaccinationBookingCantons = vaccinationBookingCantons; 155 | } 156 | 157 | public Map getVaccinationBookingInfo() { 158 | return vaccinationBookingInfo; 159 | } 160 | 161 | public void setVaccinationBookingInfo( 162 | Map vaccinationBookingInfo) { 163 | this.vaccinationBookingInfo = vaccinationBookingInfo; 164 | } 165 | 166 | public Boolean getShowVaccinationInfo() { 167 | return showVaccinationInfo; 168 | } 169 | 170 | public void setShowVaccinationInfo(Boolean showVaccinationInfo) { 171 | this.showVaccinationInfo = showVaccinationInfo; 172 | } 173 | 174 | public Boolean getDeactivate() { 175 | return deactivate; 176 | } 177 | 178 | public void setDeactivate(Boolean deactivate) { 179 | this.deactivate = deactivate; 180 | } 181 | 182 | public InfoBoxCollection getDeactivationMessage() { 183 | return deactivationMessage; 184 | } 185 | 186 | public void setDeactivationMessage( 187 | InfoBoxCollection deactivationMessage) { 188 | this.deactivationMessage = deactivationMessage; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/security/signature/SignatureResponseWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Ubique Innovation AG 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | * 8 | * SPDX-License-Identifier: MPL-2.0 9 | */ 10 | 11 | package org.dpppt.switzerland.backend.sdk.config.ws.security.signature; 12 | 13 | import io.jsonwebtoken.Claims; 14 | import io.jsonwebtoken.Jwts; 15 | import java.io.ByteArrayOutputStream; 16 | import java.io.IOException; 17 | import java.io.OutputStream; 18 | import java.io.OutputStreamWriter; 19 | import java.io.PrintWriter; 20 | import java.io.StringWriter; 21 | import java.security.KeyPair; 22 | import java.security.MessageDigest; 23 | import java.time.Instant; 24 | import java.time.OffsetDateTime; 25 | import java.time.ZoneOffset; 26 | import java.util.Base64; 27 | import java.util.Date; 28 | import java.util.List; 29 | import javax.servlet.ServletOutputStream; 30 | import javax.servlet.WriteListener; 31 | import javax.servlet.http.HttpServletResponse; 32 | import javax.servlet.http.HttpServletResponseWrapper; 33 | import org.bouncycastle.util.encoders.Hex; 34 | import org.bouncycastle.util.io.pem.PemObject; 35 | import org.bouncycastle.util.io.pem.PemWriter; 36 | import org.springframework.util.Base64Utils; 37 | 38 | public class SignatureResponseWrapper extends HttpServletResponseWrapper { 39 | 40 | // after number of days days the list and hence the signature is invalid 41 | public final int retentionPeriod; 42 | 43 | private final MessageDigest digest; 44 | private final ByteArrayOutputStream output; 45 | private final KeyPair pair; 46 | private final List protectedHeaders; 47 | 48 | private HashStream stream; 49 | private PrintWriter writer; 50 | 51 | private static final String HEADER_SIGNATURE = "Signature"; 52 | private static final String HEADER_PUBLIC_KEY = "X-Public-Key"; 53 | private static final String HEADER_DIGEST = "Digest"; 54 | private static final String ISSUER_DP3T = "dp3t"; 55 | private static final String CLAIM_HASH_ALG = "hash-alg"; 56 | private static final String CLAIM_CONTENT_HASH = "content-hash"; 57 | 58 | public SignatureResponseWrapper( 59 | HttpServletResponse response, 60 | KeyPair pair, 61 | int retentionDays, 62 | List protectedHeaders) { 63 | super(response); 64 | this.pair = pair; 65 | this.protectedHeaders = protectedHeaders; 66 | try { 67 | this.output = new ByteArrayOutputStream(response.getBufferSize()); 68 | this.digest = MessageDigest.getInstance("SHA-256"); 69 | this.stream = new HashStream(this.digest, this.output); 70 | this.retentionPeriod = retentionDays; 71 | } catch (Exception ex) { 72 | throw new RuntimeException(ex); 73 | } 74 | } 75 | 76 | @Override 77 | public ServletOutputStream getOutputStream() throws IOException { 78 | if (stream == null) { 79 | stream = new HashStream(this.digest, this.output); 80 | } 81 | return stream; 82 | } 83 | 84 | public byte[] getHash() throws IOException { 85 | return this.stream.getHash(); 86 | } 87 | 88 | @Override 89 | public PrintWriter getWriter() throws IOException { 90 | if (output != null) { 91 | throw new IllegalStateException( 92 | "getOutputStream() has already been called on this response."); 93 | } 94 | 95 | if (writer == null) { 96 | writer = new PrintWriter(new OutputStreamWriter(this.output, getCharacterEncoding())); 97 | } 98 | 99 | return writer; 100 | } 101 | 102 | @Override 103 | public void flushBuffer() throws IOException { 104 | this.setSignature(); 105 | super.flushBuffer(); 106 | if (writer != null) { 107 | writer.flush(); 108 | } else if (output != null) { 109 | output.flush(); 110 | } 111 | } 112 | 113 | public void outputData(OutputStream httpOutput) throws IOException { 114 | this.setSignature(); 115 | httpOutput.write(this.output.toByteArray()); 116 | } 117 | 118 | private void setSignature() throws IOException { 119 | byte[] theHash = this.getHash(); 120 | 121 | Claims claims = Jwts.claims(); 122 | claims.put(CLAIM_CONTENT_HASH, Base64.getEncoder().encodeToString(theHash)); 123 | claims.put(CLAIM_HASH_ALG, "sha-256"); 124 | 125 | claims.setIssuer(ISSUER_DP3T); 126 | claims.setIssuedAt( 127 | Date.from(OffsetDateTime.now().withOffsetSameInstant(ZoneOffset.UTC).toInstant())); 128 | claims.setExpiration( 129 | Date.from( 130 | OffsetDateTime.now() 131 | .withOffsetSameInstant(ZoneOffset.UTC) 132 | .plusDays(retentionPeriod) 133 | .toInstant())); 134 | for (String header : protectedHeaders) { 135 | if (!this.containsHeader(header)) { 136 | continue; 137 | } 138 | 139 | String normalizedHeader = header.toLowerCase().replace("x-", ""); 140 | String headerValue = this.getHeader(header); 141 | claims.put(normalizedHeader, headerValue); 142 | if (normalizedHeader.equals("batch-release-time")) { 143 | OffsetDateTime issueDate = 144 | OffsetDateTime.ofInstant( 145 | Instant.ofEpochMilli(Long.parseLong(headerValue)), ZoneOffset.UTC); 146 | claims.setIssuedAt(Date.from(issueDate.toInstant())); 147 | claims.setExpiration(Date.from(issueDate.plusDays(retentionPeriod).toInstant())); 148 | } 149 | } 150 | String signature = Jwts.builder().setClaims(claims).signWith(pair.getPrivate()).compact(); 151 | 152 | this.setHeader(HEADER_DIGEST, "sha-256=" + Hex.toHexString(theHash)); 153 | this.setHeader(HEADER_PUBLIC_KEY, getPublicKeyAsPEM()); 154 | this.setHeader(HEADER_SIGNATURE, signature); 155 | } 156 | 157 | private String getPublicKeyAsPEM() throws IOException { 158 | StringWriter writer = new StringWriter(); 159 | PemWriter pemWriter = new PemWriter(writer); 160 | pemWriter.writeObject(new PemObject("PUBLIC KEY", pair.getPublic().getEncoded())); 161 | pemWriter.flush(); 162 | pemWriter.close(); 163 | return Base64Utils.encodeToUrlSafeString(writer.toString().trim().getBytes()); 164 | } 165 | 166 | private class HashStream extends ServletOutputStream { 167 | 168 | private MessageDigest digest; 169 | private ByteArrayOutputStream output; 170 | 171 | public HashStream(MessageDigest digest, ByteArrayOutputStream outputStream) { 172 | this.digest = digest; 173 | this.output = outputStream; 174 | } 175 | 176 | @Override 177 | public boolean isReady() { 178 | return false; 179 | } 180 | 181 | @Override 182 | public void setWriteListener(WriteListener listener) {} 183 | 184 | @Override 185 | public void write(int b) throws IOException { 186 | this.digest.update((byte) b); 187 | this.output.write(b); 188 | } 189 | 190 | @Override 191 | public void close() throws IOException { 192 | this.output.close(); 193 | } 194 | 195 | public byte[] getHash() throws IOException { 196 | return this.digest.digest(); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/helper/MockHelper.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.helper; 2 | 3 | import org.dpppt.switzerland.backend.sdk.config.ws.model.ConfigResponse; 4 | import org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox; 5 | import org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBoxCollection; 6 | import org.dpppt.switzerland.backend.sdk.config.ws.model.Language; 7 | import org.dpppt.switzerland.backend.sdk.config.ws.poeditor.Messages; 8 | 9 | public class MockHelper { 10 | 11 | public static ConfigResponse mockConfigResponseWithInfoBox( 12 | boolean dismissible, Messages messages) { 13 | ConfigResponse configResponse = new ConfigResponse(); 14 | 15 | String hearingImpairedInfo = 16 | messages.getMessage("hearing_impaired_info", Language.DE.toLocale()); 17 | 18 | InfoBox infoBoxde = new InfoBox(); 19 | infoBoxde.setMsg( 20 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz DE"); 21 | infoBoxde.setTitle("Hinweis DE"); 22 | infoBoxde.setUrlTitle("Und ein externer Link DE"); 23 | infoBoxde.setUrl("https://www.bag.admin.ch/bag/de/home.html"); 24 | infoBoxde.setIsDismissible(dismissible); 25 | infoBoxde.setHearingImpairedInfo(hearingImpairedInfo + " DE"); 26 | 27 | InfoBox infoBoxfr = new InfoBox(); 28 | infoBoxfr.setMsg( 29 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz FR"); 30 | infoBoxfr.setTitle("Hinweis FR"); 31 | infoBoxfr.setUrlTitle("Und ein externer Link FR"); 32 | infoBoxfr.setUrl("https://www.bag.admin.ch/bag/fr/home.html"); 33 | infoBoxfr.setIsDismissible(dismissible); 34 | infoBoxfr.setHearingImpairedInfo(hearingImpairedInfo + " FR"); 35 | 36 | InfoBox infoBoxit = new InfoBox(); 37 | infoBoxit.setMsg( 38 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz IT"); 39 | infoBoxit.setTitle("Hinweis IT"); 40 | infoBoxit.setUrlTitle("Und ein externer Link IT"); 41 | infoBoxit.setUrl("https://www.bag.admin.ch/bag/it/home.html"); 42 | infoBoxit.setIsDismissible(dismissible); 43 | infoBoxit.setHearingImpairedInfo(hearingImpairedInfo + " IT"); 44 | 45 | InfoBox infoBoxen = new InfoBox(); 46 | infoBoxen.setMsg( 47 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz EN"); 48 | infoBoxen.setTitle("Hinweis EN"); 49 | infoBoxen.setUrlTitle("Und ein externer Link EN"); 50 | infoBoxen.setUrl("https://www.bag.admin.ch/bag/en/home.html"); 51 | infoBoxen.setIsDismissible(dismissible); 52 | infoBoxen.setHearingImpairedInfo(hearingImpairedInfo + " EN"); 53 | 54 | InfoBox infoBoxpt = new InfoBox(); 55 | infoBoxpt.setMsg( 56 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz PT"); 57 | infoBoxpt.setTitle("Hinweis PT"); 58 | infoBoxpt.setUrlTitle("Und ein externer Link PT"); 59 | infoBoxpt.setUrl("https://www.bag.admin.ch/bag/pt/home.html"); 60 | infoBoxpt.setIsDismissible(dismissible); 61 | infoBoxpt.setHearingImpairedInfo(hearingImpairedInfo + " PT"); 62 | 63 | InfoBox infoBoxes = new InfoBox(); 64 | infoBoxes.setMsg( 65 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz ES"); 66 | infoBoxes.setTitle("Hinweis ES"); 67 | infoBoxes.setUrlTitle("Und ein externer Link ES"); 68 | infoBoxes.setUrl("https://www.bag.admin.ch/bag/en/home.html"); 69 | infoBoxes.setIsDismissible(dismissible); 70 | infoBoxes.setHearingImpairedInfo(hearingImpairedInfo + " ES"); 71 | 72 | InfoBox infoBoxsq = new InfoBox(); 73 | infoBoxsq.setMsg( 74 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz SQ"); 75 | infoBoxsq.setTitle("Hinweis SQ"); 76 | infoBoxsq.setUrlTitle("Und ein externer Link SQ"); 77 | infoBoxsq.setUrl("https://www.bag.admin.ch/bag/en/home.html"); 78 | infoBoxsq.setIsDismissible(dismissible); 79 | infoBoxsq.setHearingImpairedInfo(hearingImpairedInfo + " SQ"); 80 | 81 | InfoBox infoBoxbs = new InfoBox(); 82 | infoBoxbs.setMsg( 83 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz BS"); 84 | infoBoxbs.setTitle("Hinweis BS"); 85 | infoBoxbs.setUrlTitle("Und ein externer Link BS"); 86 | infoBoxbs.setUrl("https://www.bag.admin.ch/bag/en/home.html"); 87 | infoBoxbs.setIsDismissible(dismissible); 88 | infoBoxbs.setHearingImpairedInfo(hearingImpairedInfo + " BS"); 89 | 90 | InfoBox infoBoxhr = new InfoBox(); 91 | infoBoxhr.setMsg( 92 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz HR"); 93 | infoBoxhr.setTitle("Hinweis HR"); 94 | infoBoxhr.setUrlTitle("Und ein externer Link HR"); 95 | infoBoxhr.setUrl("https://www.bag.admin.ch/bag/en/home.html"); 96 | infoBoxhr.setIsDismissible(dismissible); 97 | infoBoxhr.setHearingImpairedInfo(hearingImpairedInfo + " HR"); 98 | 99 | InfoBox infoBoxrm = new InfoBox(); 100 | infoBoxrm.setMsg( 101 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz RM"); 102 | infoBoxrm.setTitle("Hinweis RM"); 103 | infoBoxrm.setUrlTitle("Und ein externer Link RM"); 104 | infoBoxrm.setUrl("https://www.bag.admin.ch/bag/en/home.html"); 105 | infoBoxrm.setIsDismissible(dismissible); 106 | infoBoxrm.setHearingImpairedInfo(hearingImpairedInfo + " RM"); 107 | 108 | InfoBox infoBoxsr = new InfoBox(); 109 | infoBoxsr.setMsg( 110 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz SR"); 111 | infoBoxsr.setTitle("Hinweis SR"); 112 | infoBoxsr.setUrlTitle("Und ein externer Link SR"); 113 | infoBoxsr.setUrl("https://www.bag.admin.ch/bag/en/home.html"); 114 | infoBoxsr.setIsDismissible(dismissible); 115 | infoBoxsr.setHearingImpairedInfo(hearingImpairedInfo + " SR"); 116 | 117 | InfoBox infoBoxtr = new InfoBox(); 118 | infoBoxtr.setMsg( 119 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz TR"); 120 | infoBoxtr.setTitle("Hinweis TR"); 121 | infoBoxtr.setUrlTitle("Und ein externer Link TR"); 122 | infoBoxtr.setUrl("https://www.bag.admin.ch/bag/en/home.html"); 123 | infoBoxtr.setIsDismissible(dismissible); 124 | infoBoxtr.setHearingImpairedInfo(hearingImpairedInfo + " TR"); 125 | 126 | InfoBox infoBoxti = new InfoBox(); 127 | infoBoxti.setMsg( 128 | "Hier steht ein Text. Das kann ein Hinweis sein. Je länger umso mehr Platz TI"); 129 | infoBoxti.setTitle("Hinweis TI"); 130 | infoBoxti.setUrlTitle("Und ein externer Link TI"); 131 | infoBoxti.setUrl("https://www.bag.admin.ch/bag/en/home.html"); 132 | infoBoxti.setIsDismissible(dismissible); 133 | infoBoxti.setHearingImpairedInfo(hearingImpairedInfo + " TI"); 134 | 135 | InfoBoxCollection collection = new InfoBoxCollection(); 136 | collection.setDeInfoBox(infoBoxde); 137 | collection.setEnInfoBox(infoBoxen); 138 | collection.setFrInfoBox(infoBoxfr); 139 | collection.setItInfoBox(infoBoxit); 140 | collection.setPtInfoBox(infoBoxpt); 141 | collection.setEsInfoBox(infoBoxes); 142 | collection.setSqInfoBox(infoBoxsq); 143 | collection.setHrInfoBox(infoBoxhr); 144 | collection.setBsInfoBox(infoBoxbs); 145 | collection.setRmInfoBox(infoBoxrm); 146 | collection.setSrInfoBox(infoBoxsr); 147 | collection.setTrInfoBox(infoBoxtr); 148 | collection.setTiInfoBox(infoBoxti); 149 | 150 | configResponse.setInfoBox(collection); 151 | 152 | return configResponse; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/semver/Version.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.semver; 2 | 3 | import java.util.Objects; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * Semver implementation to allow for some special cases in the android/ios world: - platform is 8 | * prepended to version - minor and patch are optional - if missing, they are set to 0. 9 | */ 10 | public class Version implements Comparable { 11 | 12 | private Integer major; 13 | private Integer minor; 14 | private Integer patch; 15 | private String preReleaseString = ""; 16 | private String metaInfo = ""; 17 | private String platform = ""; 18 | 19 | // Pattern copied from 20 | // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string 21 | // and adapted for the mobile strings. 22 | private final Pattern semVerPattern = 23 | Pattern.compile( 24 | "^(?:(?ios|android)-)?(?0|[1-9]\\d*)(\\.(?0|[1-9]\\d*))?(\\.(?0|[1-9]\\d*))?(?:-(?(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+(?[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"); 25 | 26 | public Version() {} 27 | 28 | public Version(String versionString) { 29 | if (versionString == null) { 30 | this.setInvalidValue(); 31 | return; 32 | } 33 | this.major = -1; 34 | this.minor = 0; 35 | this.patch = 0; 36 | 37 | var matches = semVerPattern.matcher(versionString.trim()); 38 | if (matches.find()) { 39 | this.major = Integer.parseInt(matches.group("major")); 40 | if (matches.group("minor") != null) { 41 | this.minor = Integer.parseInt(matches.group("minor")); 42 | } 43 | if (matches.group("patch") != null) { 44 | this.patch = Integer.parseInt(matches.group("patch")); 45 | } 46 | if (matches.group("platform") != null) { 47 | this.platform = matches.group("platform"); 48 | } 49 | if (matches.group("prerelease") != null) { 50 | this.preReleaseString = matches.group("prerelease"); 51 | } 52 | if (matches.group("buildmetadata") != null) { 53 | this.metaInfo = matches.group("buildmetadata"); 54 | } 55 | } else { 56 | this.setInvalidValue(); 57 | } 58 | } 59 | 60 | public Version(Integer major, Integer minor, Integer patch) { 61 | this.major = major; 62 | this.minor = minor; 63 | this.patch = patch; 64 | this.preReleaseString = ""; 65 | this.metaInfo = ""; 66 | } 67 | 68 | public Version(Integer major, Integer minor) { 69 | this.major = major; 70 | this.minor = minor; 71 | this.patch = 0; 72 | this.preReleaseString = ""; 73 | this.metaInfo = ""; 74 | } 75 | 76 | public Version(Integer major) { 77 | this.major = major; 78 | this.minor = 0; 79 | this.patch = 0; 80 | this.preReleaseString = ""; 81 | this.metaInfo = ""; 82 | } 83 | 84 | public Version( 85 | Integer major, Integer minor, Integer patch, String preReleaseString, String metaInfo) { 86 | this.major = major; 87 | this.minor = minor; 88 | this.patch = patch; 89 | this.preReleaseString = preReleaseString; 90 | this.metaInfo = metaInfo; 91 | } 92 | 93 | private void setInvalidValue() { 94 | this.major = -1; 95 | this.minor = -1; 96 | this.patch = -1; 97 | this.preReleaseString = ""; 98 | this.metaInfo = ""; 99 | } 100 | 101 | public boolean isValid() { 102 | return major.compareTo(Integer.valueOf(0)) >= 0 103 | && minor.compareTo(Integer.valueOf(0)) >= 0 104 | && patch.compareTo(Integer.valueOf(0)) >= 0; 105 | } 106 | 107 | public Integer getMajor() { 108 | return this.major; 109 | } 110 | 111 | public void setMajor(Integer major) { 112 | this.major = major; 113 | } 114 | 115 | public Integer getMinor() { 116 | return this.minor; 117 | } 118 | 119 | public void setMinor(Integer minor) { 120 | this.minor = minor; 121 | } 122 | 123 | public Integer getPatch() { 124 | return this.patch; 125 | } 126 | 127 | public void setPatch(Integer patch) { 128 | this.patch = patch; 129 | } 130 | 131 | public String getPreReleaseString() { 132 | return this.preReleaseString; 133 | } 134 | 135 | public void setPreReleaseString(String preReleaseString) { 136 | this.preReleaseString = preReleaseString; 137 | } 138 | 139 | public String getMetaInfo() { 140 | return this.metaInfo; 141 | } 142 | 143 | public void setMetaInfo(String metaInfo) { 144 | this.metaInfo = metaInfo; 145 | } 146 | 147 | public String getPlatform() { 148 | return this.platform; 149 | } 150 | 151 | public void setPlatform(String platform) { 152 | this.platform = platform; 153 | } 154 | 155 | public Version major(Integer major) { 156 | this.major = major; 157 | return this; 158 | } 159 | 160 | public Version minor(Integer minor) { 161 | this.minor = minor; 162 | return this; 163 | } 164 | 165 | public Version patch(Integer patch) { 166 | this.patch = patch; 167 | return this; 168 | } 169 | 170 | public Version preReleaseString(String preReleaseString) { 171 | this.preReleaseString = preReleaseString; 172 | return this; 173 | } 174 | 175 | public Version metaInfo(String metaInfo) { 176 | this.metaInfo = metaInfo; 177 | return this; 178 | } 179 | 180 | public boolean isPrerelease() { 181 | return !preReleaseString.isEmpty(); 182 | } 183 | 184 | public boolean isAndroid() { 185 | return platform.contains("android") || metaInfo.contains("android"); 186 | } 187 | 188 | public boolean isIOS() { 189 | return platform.contains("ios") || metaInfo.contains("ios"); 190 | } 191 | 192 | @Override 193 | public boolean equals(Object o) { 194 | if (o == this) { 195 | return true; 196 | } 197 | if (!(o instanceof Version)) { 198 | return false; 199 | } 200 | Version version = (Version) o; 201 | return Objects.equals(major, version.major) 202 | && Objects.equals(minor, version.minor) 203 | && Objects.equals(patch, version.patch) 204 | && Objects.equals(preReleaseString, version.preReleaseString) 205 | && Objects.equals(metaInfo, version.metaInfo) 206 | && Objects.equals(platform, version.platform); 207 | } 208 | 209 | @Override 210 | public int hashCode() { 211 | return Objects.hash(major, minor, patch, preReleaseString, metaInfo); 212 | } 213 | 214 | @Override 215 | public String toString() { 216 | return getMajor() 217 | + "." 218 | + getMinor() 219 | + "." 220 | + getPatch() 221 | + (getPreReleaseString().isEmpty() ? "" : "-" + getPreReleaseString()) 222 | + (getMetaInfo().isEmpty() ? "" : "+" + getMetaInfo()); 223 | } 224 | 225 | @Override 226 | public int compareTo(Version o) { 227 | if (this.major.compareTo(o.major) != 0) { 228 | return this.major.compareTo(o.major); 229 | } 230 | if (this.minor.compareTo(o.minor) != 0) { 231 | return this.minor.compareTo(o.minor); 232 | } 233 | if (this.patch.compareTo(o.patch) != 0) { 234 | return this.patch.compareTo(o.patch); 235 | } 236 | if (this.isPrerelease() && o.isPrerelease()) { 237 | if (this.preReleaseString.compareTo(o.preReleaseString) != 0) { 238 | return this.preReleaseString.compareTo(o.preReleaseString); 239 | } 240 | } else if (this.isPrerelease() && !o.isPrerelease()) { 241 | return -1; 242 | } else if (!this.isPrerelease() && o.isPrerelease()) { 243 | return 1; 244 | } 245 | return 0; 246 | } 247 | 248 | public boolean isSmallerVersionThan(Version other) { 249 | return this.compareTo(other) < 0; 250 | } 251 | 252 | public boolean isLargerVersionThan(Version other) { 253 | return this.compareTo(other) > 0; 254 | } 255 | 256 | public boolean isSameVersionAs(Version other) { 257 | return this.compareTo(other) == 0; 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /dpppt-config-backend/src/main/java/org/dpppt/switzerland/backend/sdk/config/ws/helper/IOS136InfoBoxHelper.java: -------------------------------------------------------------------------------- 1 | package org.dpppt.switzerland.backend.sdk.config.ws.helper; 2 | 3 | import org.dpppt.switzerland.backend.sdk.config.ws.model.ConfigResponse; 4 | import org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox; 5 | import org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBoxCollection; 6 | 7 | public class IOS136InfoBoxHelper { 8 | 9 | public static void setInfoTextForiOS136(ConfigResponse configResponse) { 10 | InfoBoxCollection infoBoxCollection = new InfoBoxCollection(); 11 | configResponse.setInfoBox(infoBoxCollection); 12 | 13 | InfoBox infoBoxDe = new InfoBox(); 14 | infoBoxCollection.setDeInfoBox(infoBoxDe); 15 | infoBoxDe.setMsg( 16 | "Das Betriebssystem Ihres Mobiltelefons informiert Sie im wöchentlichen Update über die Anzahl identifizierter Begegnungen. Sie können diesen Hinweis ignorieren. Ab iOS 13.7 wird diese Information nicht mehr angezeigt; wir empfehlen Ihnen, Ihr Mobiltelefon zu aktualisieren.\n" 17 | + "\n" 18 | + "Relevante Meldungen über mögliche Ansteckungen erhalten Sie als Mitteilung von der SwissCovid App. Die Übersicht finden Sie in der App im Abschnitt «Meldungen»."); 19 | infoBoxDe.setTitle("Hinweis"); 20 | infoBoxDe.setUrl( 21 | "https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/technik/wieso-erscheint-auf-ios-136-w%C3%B6chtlich-eine-benachrichtigung-dein-ger%C3%A4t-hat-%C2%AB0-m%C3%B6gliche"); 22 | infoBoxDe.setUrlTitle("Weitere Informationen"); 23 | infoBoxDe.setIsDismissible(true); 24 | 25 | InfoBox infoBoxFr = new InfoBox(); 26 | infoBoxCollection.setFrInfoBox(infoBoxFr); 27 | infoBoxFr.setMsg( 28 | "Lors de la mise à jour hebdomadaire, le système d'exploitation de votre téléphone vous indique le nombre de contacts identifiés. Vous pouvez ignorer cette information. À partir de la version 13.7 d'iOS, ce renseignement n'apparaît plus ; nous vous recommandons de mettre à jour votre téléphone.\n" 29 | + "\n" 30 | + "L'application SwissCovid vous signalera les infections par le biais d'une notification. Vous trouverez l'aperçu sous la rubrique \"Notifications\"."); 31 | infoBoxFr.setTitle("Note"); 32 | infoBoxFr.setUrl( 33 | "https://www.bag.admin.ch/bag/fr/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/fr/pourquoi-l%E2%80%99application-swisscovid-ne-me-signale-t-elle-aucune-infection-potentielle-bien-que"); 34 | infoBoxFr.setUrlTitle("Plus d'informations"); 35 | infoBoxFr.setIsDismissible(true); 36 | 37 | InfoBox infoBoxIt = new InfoBox(); 38 | infoBoxCollection.setItInfoBox(infoBoxIt); 39 | infoBoxIt.setMsg( 40 | "Il sistema operativo del tuo telefono cellulare ti informa con un aggiornamento settimanale sul numero di incontri identificati. Puoi ignorare questo avviso. A partire da iOS 13.7 questa informazione non viene più visualizzata; ti consigliamo di aggiornare il tuo telefono cellulare.\n" 41 | + "\n" 42 | + "Riceverai le segnalazioni rilevanti sui possibili contagi sotto forma di messaggio dell'app SwissCovid. La panoramica si trova nell'app, nella sezione «Segnalazioni»."); 43 | infoBoxIt.setTitle("Nota"); 44 | infoBoxIt.setUrl( 45 | "https://www.bag.admin.ch/bag/it/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/it/perch%C3%A9-dall%E2%80%99app-swisscovid-non-ricevo-segnalazioni-su-un-possibile-contagio-quando-invece-ios-136-mi"); 46 | infoBoxIt.setUrlTitle("Ulteriori informazioni"); 47 | infoBoxIt.setIsDismissible(true); 48 | 49 | InfoBox infoBoxEn = new InfoBox(); 50 | infoBoxCollection.setEnInfoBox(infoBoxEn); 51 | infoBoxEn.setMsg( 52 | "Your mobile phone's operating system will give you a weekly update of the number of identified encounters. You can ignore this message. This information is no longer shown as of iOS 13.7. We recommend you to update your mobile phone.\n" 53 | + "\n" 54 | + "You will receive relevant notifications about possible infections in the form of a message from the SwissCovid App. You will find the overview in the app, under \"Reports\"."); 55 | infoBoxEn.setTitle("Note"); 56 | infoBoxEn.setUrl( 57 | "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/en/swisscovid-app-how-it-works-technology/why-do-i-not-receive-warning-swisscovid-app-about-possible"); 58 | infoBoxEn.setUrlTitle("Further information"); 59 | infoBoxEn.setIsDismissible(true); 60 | 61 | InfoBox infoBoxEs = new InfoBox(); 62 | infoBoxCollection.setEsInfoBox(infoBoxEs); 63 | infoBoxEs.setMsg( 64 | "El sistema operativo de su teléfono móvil le informa semanalmente sobre el número de contactos identificados. Puede usted ignorar esta información. A partir de la versión iOS 13.7 esta información ya no se indica; le recomendamos que actualice su teléfono móvil.\n" 65 | + "\n" 66 | + "Cualquier aviso relevante sobre un posible contagio se transmite mediante una notificación de la aplicación SwissCovid. Consulte la lista en la aplicación bajo la sección «Notificaciones»."); 67 | infoBoxEs.setTitle("Nota"); 68 | infoBoxEs.setUrl( 69 | "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/en/swisscovid-app-how-it-works-technology/why-do-i-not-receive-warning-swisscovid-app-about-possible"); 70 | infoBoxEs.setUrlTitle("Más informaciones"); 71 | infoBoxEs.setIsDismissible(true); 72 | 73 | InfoBox infoBoxPt = new InfoBox(); 74 | infoBoxCollection.setPtInfoBox(infoBoxPt); 75 | infoBoxPt.setMsg( 76 | "O sistema operativo do seu telemóvel informa-o com atualizações semanais sobre o número de contactos que teve com pessoas infetadas. Pode optar por ignorar estes avisos. A partir da versão iOS 13.7, esta informação deixará de ser exibida; aconselhamo-lo a atualizar o telemóvel.\n" 77 | + "\n" 78 | + "As notificações relevantes sobre possíveis contágios ser-lhe-ão enviadas por mensagem pela app SwissCovid. Poderá consultar o resumo das notificações da app na secção «Notificações»."); 79 | infoBoxPt.setTitle("Nota"); 80 | infoBoxPt.setUrl( 81 | "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/en/swisscovid-app-how-it-works-technology/why-do-i-not-receive-warning-swisscovid-app-about-possible"); 82 | infoBoxPt.setUrlTitle("Mais informações"); 83 | infoBoxPt.setIsDismissible(true); 84 | 85 | InfoBox infoBoxBs = new InfoBox(); 86 | infoBoxCollection.setBsInfoBox(infoBoxBs); 87 | infoBoxBs.setMsg( 88 | "Operativni sistem na mobilnom telefonu vas obaveštava na nedeljnom nivou o broju identifikovanih kontakata. Možete da zanemarite te napomene. Te informacije se više neće prikazivati od verzije iOS 13.7. Preporučujemo vam da ažurirate mobilni telefon.\n" 89 | + "\n" 90 | + "Relevantne poruke o mogućim infekcijama dobijaćete u vidu obaveštenja iz aplikacije SwissCovid. Pregled možete naći u odeljku „Poruke“ u aplikaciji."); 91 | infoBoxBs.setTitle("Bilješka"); 92 | infoBoxBs.setUrl( 93 | "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/en/swisscovid-app-how-it-works-technology/why-do-i-not-receive-warning-swisscovid-app-about-possible"); 94 | infoBoxBs.setUrlTitle("Više informacija"); 95 | infoBoxBs.setIsDismissible(true); 96 | 97 | InfoBox infoBoxSq = new InfoBox(); 98 | infoBoxCollection.setSqInfoBox(infoBoxSq); 99 | infoBoxSq.setMsg( 100 | "Në përditësimin javor sistemi operativ i celularit tuaj ju informon për numrin e kontakteve të identifikuara. Mund ta shpërfillni këtë njoftim. Duke nisur nga versioni iOS 13.7 ky informacion nuk shfaqet më; ju rekomandojmë që ta përditësoni celularin tuaj.\n" 101 | + "\n" 102 | + "Mesazhet përkatëse për infektimet e mundshme i merrni si njoftime nga aplikacioni \"SwissCovid\". Pamjen e përgjithshme mund ta gjeni në seksionin \"Njoftimet\" në aplikacion."); 103 | infoBoxSq.setTitle("Shënim"); 104 | infoBoxSq.setUrl( 105 | "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/en/swisscovid-app-how-it-works-technology/why-do-i-not-receive-warning-swisscovid-app-about-possible"); 106 | infoBoxSq.setUrlTitle("Informacione të tjera"); 107 | infoBoxSq.setIsDismissible(true); 108 | 109 | InfoBox infoBoxRm = new InfoBox(); 110 | infoBoxCollection.setRmInfoBox(infoBoxRm); 111 | infoBoxRm.setMsg( 112 | "Il sistem operativ da Voss telefonin As infurmescha cun l'update emnil davart il dumber da scuntradas identifitgadas. Vus pudais ignorar questa infurmaziun. A partir dad iOS 13.7 na vegn questa infurmaziun betg pli inditgada. Nus As recumandain, d'actualisar Voss telefonin.\n" 113 | + "\n" 114 | + "Annunzias relevantas davart infecziuns pussaivlas survegnis Vus sco communicaziun da l'app SwissCovid. La survista chattais Vus en l'app en la part «Avis»."); 115 | infoBoxRm.setTitle("Indicaziun"); 116 | infoBoxRm.setUrl( 117 | "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/en/swisscovid-app-how-it-works-technology/why-do-i-not-receive-warning-swisscovid-app-about-possible"); 118 | infoBoxRm.setUrlTitle("Ulteriuras infurmaziuns"); 119 | infoBoxRm.setIsDismissible(true); 120 | 121 | InfoBox infoBoxTr = new InfoBox(); 122 | infoBoxCollection.setTrInfoBox(infoBoxTr); 123 | infoBoxTr.setMsg( 124 | "Cep telefonunuzun işletim sistemi, tespit edilen temaslı kişilerin sayısı hakkında haftalık güncelleme ile sizi bilgilendirir. Dilerseniz bu bildirimleri engelleyebilirsiniz. iOS 13.7 sürümünden itibaren bu bilgi artık görüntülenmemektedir; telefonunuzu güncellemenizi tavsiye ediyoruz.\n" 125 | + "\n" 126 | + "Olası bulaşma riski ile ilgili bildirimleri SwissCovid uygulamasından mesaj olarak alacaksınız. Özetini, uygulama içerisindeki «Bildirimler» bölümünde bulabilirsiniz."); 127 | infoBoxTr.setTitle("Bilgi"); 128 | infoBoxTr.setUrl( 129 | "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/en/swisscovid-app-how-it-works-technology/why-do-i-not-receive-warning-swisscovid-app-about-possible"); 130 | infoBoxTr.setUrlTitle("Daha fazla bilgi"); 131 | infoBoxTr.setIsDismissible(true); 132 | 133 | InfoBox infoBoxTi = new InfoBox(); 134 | infoBoxCollection.setTiInfoBox(infoBoxTi); 135 | infoBoxTi.setMsg( 136 | "እቲ ስርዓተ መስርሕ ኣብ ሞባይልኩም ኩሉ ሰሙን ምስቲ ዝመጽእ ኣፕደይት ብዛዕባ መጠን ዝተረጋገጸ ምንቅስቓስ ይሕብረኩም። እዚ ምልክት ከተስተውዕሉ ኣየድልየኩምን። ካብ ቨርዝዮን iOS 13.7 ንየው እዚ ሓበሬታ ኣይክረአን እዩ፤ ሞባይላትኩም ከተሕድስዎም ንመኽረኩም ኢና።\n" 137 | + "\n" 138 | + "ካብ SwissCovid ኤፕ ኣገደስቲ ሓበሬታታት ብዛዕባ ዝኾኑ ልበዳታት ክትወሃቡ ኢኹም። ሓፈሻዊ ሓበሬታ ኣብቲ ኤፕ ኣብ «ሓበሬታ» ዝብሃል ክፋል ክትረኽቡ ኢኹም።"); 139 | infoBoxTi.setTitle("ምልክታ"); 140 | infoBoxTi.setUrl( 141 | "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/en/swisscovid-app-how-it-works-technology/why-do-i-not-receive-warning-swisscovid-app-about-possible"); 142 | infoBoxTi.setUrlTitle("ተወሰኽቲ ሓበሬታታት"); 143 | infoBoxTi.setIsDismissible(true); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /documentation/yaml/sdk.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | servers: 3 | - url: https://demo.dpppt.org 4 | description: '' 5 | info: 6 | version: 1.0-gapple 7 | description: DP3T API 8 | title: DP3T API 9 | paths: 10 | /v1/: 11 | get: 12 | summary: hello 13 | description: Echo endpoint 14 | responses: 15 | '200': 16 | description: Hello from DP3T Config WS 17 | headers: { 18 | } 19 | content: 20 | application/json: 21 | schema: 22 | type: string 23 | /v1/config: 24 | get: 25 | summary: getConfig 26 | description: Read latest configuration and messages, depending on the version 27 | of the phone and the app. 28 | responses: 29 | '200': 30 | description: ConfigResponse structure with eventual notifications and epidemic 31 | parameters 32 | headers: { 33 | } 34 | content: 35 | application/json: 36 | schema: 37 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.ConfigResponse' 38 | parameters: 39 | - name: appversion 40 | in: query 41 | description: Version of the App installed 42 | example: ios-1.0.7 43 | required: true 44 | schema: 45 | type: string 46 | - name: osversion 47 | in: query 48 | description: Version of the OS 49 | example: ios13.6 50 | required: true 51 | schema: 52 | type: string 53 | - name: buildnr 54 | in: query 55 | description: Build number of the app 56 | example: ios-200619.2333.175 57 | required: true 58 | schema: 59 | type: string 60 | /v1/testinfobox/config: 61 | get: 62 | summary: getGhettoboxConfig 63 | description: getGhettoboxConfig 64 | responses: 65 | '200': 66 | description: '' 67 | content: 68 | application/json: 69 | schema: 70 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.ConfigResponse' 71 | parameters: 72 | - name: appversion 73 | in: query 74 | description: Version of the App installed 75 | example: ios-1.0.7 76 | required: true 77 | schema: 78 | type: string 79 | - name: osversion 80 | in: query 81 | description: Version of the OS 82 | example: ios13.6 83 | required: true 84 | schema: 85 | type: string 86 | - name: buildnr 87 | in: query 88 | description: Build number of the app 89 | example: ios-200619.2333.175 90 | required: true 91 | schema: 92 | type: string 93 | components: 94 | schemas: 95 | org.dpppt.switzerland.backend.sdk.config.ws.model.ConfigResponse: 96 | type: object 97 | properties: 98 | forceUpdate: 99 | type: boolean 100 | description: Blocks the app and shows a link to the app-store. The user 101 | can only continue once she updated the app 102 | example: '' 103 | infoBox: 104 | allOf: 105 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBoxCollection' 106 | - description: Holds a message translated in different languages 107 | - example: '' 108 | whatToDoPositiveTestTexts: 109 | allOf: 110 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTextsCollection' 111 | testLocations: 112 | allOf: 113 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocationCollection' 114 | - description: Holds a url for test locations for each canton and Liechtenstein 115 | - example: '' 116 | iOSGaenSdkConfig: 117 | allOf: 118 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.GAENSDKConfig' 119 | - description: GAEN epidemiological parameters for iOS 120 | - example: '' 121 | androidGaenSdkConfig: 122 | allOf: 123 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.GAENSDKConfig' 124 | - description: GAEN epidemiological parameters for Android 125 | - example: '' 126 | interOpsCountries: 127 | type: array 128 | items: 129 | type: string 130 | description: list of ISO 3166-1 alpha-2 country codes describing the available 131 | interops countries 132 | testInformationUrls: 133 | type: object 134 | description: Localized urls to test information website 135 | example: '' 136 | additionalProperties: 137 | type: string 138 | checkInUpdateNotificationEnabled: 139 | type: boolean 140 | description: Flag to enable notifications informing users about the new 141 | checkin feature 142 | example: '' 143 | vaccinationBookingCantons: 144 | type: object 145 | description: Holds vaccination booking infos for cantons by language 146 | example: '' 147 | additionalProperties: 148 | type: array 149 | items: 150 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.VaccinationBookingCanton' 151 | vaccinationBookingInfo: 152 | type: object 153 | description: Holds general vaccination booking info by language 154 | example: '' 155 | additionalProperties: 156 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.VaccinationBookingInfo' 157 | showVaccinationInfo: 158 | type: boolean 159 | description: feature flag. when set to true the vaccination infos should 160 | be displayed 161 | example: '' 162 | org.dpppt.switzerland.backend.sdk.config.ws.model.FaqEntry: 163 | type: object 164 | properties: 165 | title: 166 | type: string 167 | text: 168 | type: string 169 | iconAndroid: 170 | type: string 171 | iconIos: 172 | type: string 173 | linkTitle: 174 | type: string 175 | linkUrl: 176 | type: string 177 | org.dpppt.switzerland.backend.sdk.config.ws.model.GAENSDKConfig: 178 | type: object 179 | properties: 180 | lowerThreshold: 181 | type: integer 182 | description: Lower threshold that is sent to the GAEN to count encounters 183 | with other devices. It is expressed in dB attenuation from the Bluetooth 184 | chip. 185 | example: '53' 186 | higherThreshold: 187 | type: integer 188 | description: Higher threshold that is sent to the GAEN to count encounters 189 | with other devices .It is expressed in dB attenuation from the Bluetooth 190 | chip. 191 | example: '60' 192 | factorLow: 193 | type: number 194 | format: double 195 | description: Multiplication factor used to weigh the return value of the 196 | GAEN in attenuationDuration[0] 197 | example: 1.0d 198 | factorHigh: 199 | type: number 200 | format: double 201 | description: Multiplication factor used to weigh the return value of the 202 | GAEN in attenuationDuration[1] 203 | example: 0.5d 204 | triggerThreshold: 205 | type: integer 206 | description: Minimum duration of exposure during one day reported by the 207 | GAEN before the user is alerted of an exposure risk 208 | example: '15' 209 | org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox: 210 | type: object 211 | properties: 212 | title: 213 | type: string 214 | description: Title of the infobox 215 | example: Alert 216 | msg: 217 | type: string 218 | description: Message shown to the user 219 | example: Please restart your phone 220 | url: 221 | type: string 222 | description: If given, adds a url to the message 223 | example: https://dp-3t.github.io/ 224 | urlTitle: 225 | type: string 226 | description: Title to be shown for the URL 227 | example: DP3T page 228 | isDismissible: 229 | type: boolean 230 | description: If true, the user can dismiss the message 231 | example: 'true' 232 | hearingImpairedInfo: 233 | type: string 234 | description: Text to display in hearing impaired pop up 235 | example: '' 236 | org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBoxCollection: 237 | type: object 238 | properties: 239 | deInfoBox: 240 | allOf: 241 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 242 | frInfoBox: 243 | allOf: 244 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 245 | itInfoBox: 246 | allOf: 247 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 248 | enInfoBox: 249 | allOf: 250 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 251 | ptInfoBox: 252 | allOf: 253 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 254 | esInfoBox: 255 | allOf: 256 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 257 | sqInfoBox: 258 | allOf: 259 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 260 | bsInfoBox: 261 | allOf: 262 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 263 | hrInfoBox: 264 | allOf: 265 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 266 | srInfoBox: 267 | allOf: 268 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 269 | rmInfoBox: 270 | allOf: 271 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 272 | trInfoBox: 273 | allOf: 274 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 275 | tiInfoBox: 276 | allOf: 277 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 278 | org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation: 279 | type: object 280 | properties: 281 | region: 282 | type: string 283 | url: 284 | type: string 285 | org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocationCollection: 286 | type: object 287 | properties: 288 | de: 289 | type: array 290 | items: 291 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 292 | fr: 293 | type: array 294 | items: 295 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 296 | it: 297 | type: array 298 | items: 299 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 300 | en: 301 | type: array 302 | items: 303 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 304 | pt: 305 | type: array 306 | items: 307 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 308 | es: 309 | type: array 310 | items: 311 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 312 | sq: 313 | type: array 314 | items: 315 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 316 | bs: 317 | type: array 318 | items: 319 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 320 | hr: 321 | type: array 322 | items: 323 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 324 | sr: 325 | type: array 326 | items: 327 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 328 | rm: 329 | type: array 330 | items: 331 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 332 | tr: 333 | type: array 334 | items: 335 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 336 | ti: 337 | type: array 338 | items: 339 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.TestLocation' 340 | org.dpppt.switzerland.backend.sdk.config.ws.model.VaccinationBookingCanton: 341 | type: object 342 | required: 343 | - name 344 | - iconAndroid 345 | - iconIos 346 | - linkUrl 347 | properties: 348 | name: 349 | type: string 350 | iconAndroid: 351 | type: string 352 | iconIos: 353 | type: string 354 | linkUrl: 355 | type: string 356 | org.dpppt.switzerland.backend.sdk.config.ws.model.VaccinationBookingInfo: 357 | type: object 358 | required: 359 | - title 360 | - text 361 | - info 362 | properties: 363 | title: 364 | type: string 365 | text: 366 | type: string 367 | info: 368 | type: string 369 | org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts: 370 | type: object 371 | properties: 372 | enterCovidcodeBoxSupertitle: 373 | type: string 374 | enterCovidcodeBoxTitle: 375 | type: string 376 | enterCovidcodeBoxText: 377 | type: string 378 | enterCovidcodeBoxButtonTitle: 379 | type: string 380 | infoBox: 381 | allOf: 382 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.InfoBox' 383 | faqEntries: 384 | type: array 385 | items: 386 | $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.FaqEntry' 387 | org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTextsCollection: 388 | type: object 389 | properties: 390 | de: 391 | allOf: 392 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 393 | fr: 394 | allOf: 395 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 396 | it: 397 | allOf: 398 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 399 | en: 400 | allOf: 401 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 402 | pt: 403 | allOf: 404 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 405 | es: 406 | allOf: 407 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 408 | sq: 409 | allOf: 410 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 411 | bs: 412 | allOf: 413 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 414 | hr: 415 | allOf: 416 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 417 | sr: 418 | allOf: 419 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 420 | rm: 421 | allOf: 422 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 423 | tr: 424 | allOf: 425 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 426 | ti: 427 | allOf: 428 | - $ref: '#/components/schemas/org.dpppt.switzerland.backend.sdk.config.ws.model.WhatToDoPositiveTestTexts' 429 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | --------------------------------------------------------------------------------